diff --git a/.gitignore b/.gitignore index 41997358..7070c8ad 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ _test /build /dist /etc/*.local.yml +/etc/plugins/*.local.yml /etc/log/log.test.json /data* /tarball diff --git a/docs/account-access.md b/docs/account-access.md new file mode 100644 index 00000000..68d5fdfa --- /dev/null +++ b/docs/account-access.md @@ -0,0 +1,16 @@ +## 登陆相关 + +#### 来源地址限制 + +IP地址的获取顺序 + +- http header "X-Forwarded-For" +- http header "X-Real-Ip" +- http request RemoteAddr + +nginx 代理配置客户端地址 + +``` +# https://www.nginx.com/resources/wiki/start/topics/examples/forwarded/ +proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; +``` diff --git a/etc/address.yml b/etc/address.yml index 2e634a14..744e9525 100644 --- a/etc/address.yml +++ b/etc/address.yml @@ -42,5 +42,11 @@ judge: addresses: - 127.0.0.1 +prober: + http: 0.0.0.0:8023 + rpc: 0.0.0.0:8024 + addresses: + - 127.0.0.1 + agent: http: 0.0.0.0:2080 diff --git a/etc/ams.yml b/etc/ams.yml index 9d7e1999..704b50fe 100644 --- a/etc/ams.yml +++ b/etc/ams.yml @@ -6,7 +6,7 @@ logger: http: mode: release cookieDomain: "" - cookieName: ecmc-user + cookieName: ecmc-sid tokens: - ams-builtin-token diff --git a/etc/dict.json b/etc/dict.json index 6fd47c36..5a71afec 100644 --- a/etc/dict.json +++ b/etc/dict.json @@ -57,6 +57,101 @@ "node is managed by other system": "租户正在被系统系统使用", "resources not found by %s": "通过 %s 没有找到资源", "cannot delete root user": "root用户不能删除", - "user not found": "用户未找到" + "user not found": "用户未找到", + + "Databases": "数据库", + "if the list is empty, then metrics are gathered from all database tables": "如果列表为空,则收集所有数据库表", + "Process List": "进程列表", + "gather thread state counts from INFORMATION_SCHEMA.PROCESSLIST": "从 INFORMATION_SCHEMA.PROCESSLIST 收集线程状态信息", + "User Statistics": "User Statistics", + "gather user statistics from INFORMATION_SCHEMA.USER_STATISTICS": "从 INFORMATION_SCHEMA.USER_STATISTICS 收集用户状态信息", + "Auto Increment": "Auto Increment", + "gather auto_increment columns and max values from information schema": "采集 auto_increment 和 max values 信息", + "Innodb Metrics": "Innodb Metrics", + "gather metrics from INFORMATION_SCHEMA.INNODB_METRICS": "采集 INFORMATION_SCHEMA.INNODB_METRICS 信息", + "Slave Status": "Slave Status", + "gather metrics from SHOW SLAVE STATUS command output": "采集 metrics from SHOW SLAVE STATUS command output", + "Binary Logs": "Binary Logs", + "gather metrics from SHOW BINARY LOGS command output": "采集 metrics from SHOW BINARY LOGS command output", + "Table IO Waits": "Table IO Waits", + "gather metrics from PERFORMANCE_SCHEMA.TABLE_IO_WAITS_SUMMARY_BY_TABLE": "采集 from PERFORMANCE_SCHEMA.TABLE_IO_WAITS_SUMMARY_BY_TABLE", + "Table Lock Waits": "Table Lock Waits", + "gather metrics from PERFORMANCE_SCHEMA.TABLE_LOCK_WAITS": "采集 from PERFORMANCE_SCHEMA.TABLE_LOCK_WAITS", + "Index IO Waits": "Index IO Waits", + "gather metrics from PERFORMANCE_SCHEMA.TABLE_IO_WAITS_SUMMARY_BY_INDEX_USAGE": "采集 from PERFORMANCE_SCHEMA.TABLE_IO_WAITS_SUMMARY_BY_INDEX_USAGE", + "Event Waits": "Event Waits", + "gather metrics from PERFORMANCE_SCHEMA.EVENT_WAITS": "采集 from PERFORMANCE_SCHEMA.EVENT_WAITS", + "Tables": "Tables", + "gather metrics from INFORMATION_SCHEMA.TABLES for databases provided above list": "采集 from INFORMATION_SCHEMA.TABLES for databases provided above list", + "File Events Stats": "File Events Stats", + "gather metrics from PERFORMANCE_SCHEMA.FILE_SUMMARY_BY_EVENT_NAME": "采集 from PERFORMANCE_SCHEMA.FILE_SUMMARY_BY_EVENT_NAME", + "Perf Events Statements": "Perf Events Statements", + "gather metrics from PERFORMANCE_SCHEMA.EVENTS_STATEMENTS_SUMMARY_BY_DIGEST": "采集 from PERFORMANCE_SCHEMA.EVENTS_STATEMENTS_SUMMARY_BY_DIGEST", + "Interval Slow": "Interval Slow", + + "Repositories": "Repositories", + "List of repositories to monitor": "List of repositories to monitor", + "Access token": "Access token", + "Github API access token. Unauthenticated requests are limited to 60 per hour": "Github API access token. Unauthenticated requests are limited to 60 per hour", + "Enterprise base url": "Enterprise base url", + "Github API enterprise url. Github Enterprise accounts must specify their base url": "Github API enterprise url. Github Enterprise accounts must specify their base url", + "HTTP timeout": "HTTP timeout", + "Timeout for HTTP requests": "Timeout for HTTP requests", + + "Unable to get captcha": "无法获得验证码", + "Invalid captcha answer": "错误的验证码", + "Username %s is invalid": "用户名 %s 不符合规范", + "Username %s too long > 64": "用户名 %s 太长(64)", + "Unable to get login arguments": "无法获得登陆参数", + "Deny Access from %s with whitelist control": "来自 %s 的访问被白名单规则拒绝", + "Invalid login type %s": "不支持的登陆类型 %s", + "Unable to get type, sms-code | email-code": "无法获得验证码类型", + "Unable to get code arg": "无法获得验证码类型", + "sms/email sender is disabled": "无法发送 短信/邮件 验证码", + "Invalid code type %s": "不支持的验证码类型 %s", + "Cannot find the user by %s": "无法用 %s 找到相关用户", + "Unable to get password": "无法获取密码", + "Invalid code": "不符合规范的验证码", + "The code is incorrect": "无效的验证码", + "The code has expired": "失效的验证码", + "Invalid arguments %s": "不合法的参数 %s", + "Login fail, check your username and password": "登陆失败,请检查用户名/密码", + "User dose not exist": "用户不存在", + "Username %s already exists": "用户名 %s 已存在", + + "Upper char": "大写字母", + "Lower char": "小写字母", + "Number": "数字", + "Special char": "特殊字符", + "Must include %s": "必须包含 %s", + "Invalid Password, %s": "密码不符合规范, %s", + "character: %s not supported": "不支持的字符 %s", + "Incorrect login/password %s times, you still have %s chances": "登陆失败%d次,你还有%d次机会", + "The limited sessions %d": "会话数量限制,最多%d个会话", + "Password has been expired": "密码已过期,请重置密码", + "User is inactive": "用户已禁用", + "User is locked": "用户已锁定", + "User is frozen": "用户已休眠", + "User is writen off": "用户已注销", + "Minimum password length %d": "密码最小长度 %d", + "Password too short (min:%d) %s": "密码太短 (最小 %d) %s", + "%s format error":"%s 所填内容不符合规范", + "%s %s format error":"%s %s 所填内容不符合规范", + "username too long (max:%d)": "用户名太长 (最长:%d)", + "dispname too long (max:%d)": "昵称太长 (最长:%d)", + "email %s or phone %s is exists": "邮箱 %s 或者 手机号 %s 已存在", + "Password is not set": "密码未设置", + "Incorrect old password": "密码错误", + "The password is the same as the old password": "密码与历史密码重复", + "phone": "手机号", + "email": "邮箱", + "username": "用户名", + "dispname": "昵称", + "Temporary user has expired": "临时账户,已过有效期", + "Invalid user status %d": "异常的用户状态 %d", + "Password expired, please change the password in time": "密码过期,请及时修改密码", + "First Login, please change the password in time": "初始登陆,请及时修改密码", + + "EOF": "" } -} \ No newline at end of file +} diff --git a/etc/identity.yml b/etc/identity.yml index 0471088d..c584b9ce 100644 --- a/etc/identity.yml +++ b/etc/identity.yml @@ -3,7 +3,7 @@ ip: specify: "" shell: ifconfig `route|grep '^default'|awk '{print $NF}'`|grep inet|awk '{print $2}'|head -n 1|awk -F':' '{print $NF}' -# MON、JOB的客户端拿来做本机标识 +# MON、JOB, judge, prober 的客户端拿来做本机标识 ident: specify: "" - shell: ifconfig `route|grep '^default'|awk '{print $NF}'`|grep inet|awk '{print $2}'|head -n 1|awk -F':' '{print $NF}' \ No newline at end of file + shell: ifconfig `route|grep '^default'|awk '{print $NF}'`|grep inet|awk '{print $2}'|head -n 1|awk -F':' '{print $NF}' diff --git a/etc/job.yml b/etc/job.yml index 72803467..3477ce29 100644 --- a/etc/job.yml +++ b/etc/job.yml @@ -6,7 +6,7 @@ logger: http: mode: release cookieDomain: "" - cookieName: ecmc-user + cookieName: ecmc-sid output: # database | remote diff --git a/etc/login-code-email.tpl b/etc/login-code-email.tpl index f29c8e4a..28b9f3e0 100644 --- a/etc/login-code-email.tpl +++ b/etc/login-code-email.tpl @@ -1 +1 @@ -您好,您的登录验证码为 {{.Code}} 。 +您好,您的验证码为 {{.Code}} 。 diff --git a/etc/login-code-sms.tpl b/etc/login-code-sms.tpl index f29c8e4a..28b9f3e0 100644 --- a/etc/login-code-sms.tpl +++ b/etc/login-code-sms.tpl @@ -1 +1 @@ -您好,您的登录验证码为 {{.Code}} 。 +您好,您的验证码为 {{.Code}} 。 diff --git a/etc/monapi.yml b/etc/monapi.yml index 8aada37c..df11a205 100644 --- a/etc/monapi.yml +++ b/etc/monapi.yml @@ -25,6 +25,8 @@ redis: # conn: 500 # read: 3000 # write: 3000 +i18n: + lang: zh notify: p1: ["voice", "sms", "mail", "im"] @@ -36,3 +38,8 @@ link: stra: http://n9e.com/mon/strategy/%v event: http://n9e.com/mon/history/his/%v claim: http://n9e.com/mon/history/cur/%v + +http: + mode: release + cookieDomain: "" + cookieName: ecmc-sid diff --git a/etc/plugins/mysql.yml b/etc/plugins/mysql.yml new file mode 100644 index 00000000..8aa64a8b --- /dev/null +++ b/etc/plugins/mysql.yml @@ -0,0 +1,856 @@ +metrics: + - name: mysql_queries + type: COUNTER + - name: mysql_transactions + type: COUNTER + expr: mysql_com_commit + mysql_com_rollback + - name: mysql_threads_running + type: GAUGE + comment: "并发数" + - name: mysql_threads_connected + type: GAUGE + comment: "当前连接数" + - name: mysql_variables_max_connections + type: GAUGE + comment: "最大连接数" + - name: mysql_connections_threshold + type: GAUGE + expr: mysql_threads_connected / mysql_variables_max_connections + comment: "连接数阈值 < 0.8" + - name: mysql_innodb_buffer_pool_read_requests + type: COUNTER + comment: "innodb缓冲池查询总数" + - name: mysql_innodb_buffer_pool_reads + type: COUNTER + comment: "innodb从磁盘查询数" + - name: mysql_innodb_buffer_read_threshold + type: COUNTER + expr: (mysql_innodb_buffer_pool_read_requests - mysql_innodb_buffer_pool_reads) / mysql_innodb_buffer_pool_read_requests + comment: "磁盘查询报警阈值 < 0.95" + - name: mysql_binary_files_count + type: COUNTER + - name: mysql_binary_size_bytes + type: COUNTER + - name: mysql_binlog_bytes_written + type: COUNTER + - name: mysql_binlog_cache_disk_use + type: COUNTER + - name: mysql_binlog_cache_use + type: COUNTER + - name: mysql_binlog_commits + type: COUNTER + - name: mysql_com_begin + type: COUNTER + - name: mysql_com_binlog + type: COUNTER + - name: mysql_com_commit + type: COUNTER + - name: mysql_com_create_table + type: COUNTER + - name: mysql_com_delete + type: COUNTER + - name: mysql_com_delete_multi + type: COUNTER + - name: mysql_com_drop_table + type: COUNTER + - name: mysql_com_empty_query + type: COUNTER + - name: mysql_com_execute_sql + type: COUNTER + - name: mysql_com_flush + type: COUNTER + - name: mysql_com_insert + type: COUNTER + - name: mysql_com_lock_tables + type: COUNTER + - name: mysql_com_rollback + type: COUNTER + - name: mysql_com_stmt_close + type: COUNTER + - name: mysql_com_stmt_execute + type: COUNTER + - name: mysql_com_stmt_prepare + type: COUNTER + - name: mysql_com_stmt_reprepare + type: COUNTER + - name: mysql_com_stmt_reset + type: COUNTER + - name: mysql_com_stmt_fetch + type: COUNTER + - name: mysql_com_update + type: COUNTER + - name: mysql_com_update_multi + type: COUNTER + - name: mysql_compression + type: COUNTER + - name: mysql_connections + type: GAUGE + - name: mysql_max_used_connections + type: COUNTER + - name: mysql_open_files + type: GAUGE + - name: mysql_open_streams + type: GAUGE + - name: mysql_open_tables + type: GAUGE + - name: mysql_opened_files + type: COUNTER + - name: mysql_opened_table_definitions + type: COUNTER + - name: mysql_opened_tables + type: COUNTER + - name: mysql_opened_views + type: COUNTER + - name: mysql_rows_read + type: COUNTER + - name: mysql_rows_sent + type: COUNTER + - name: mysql_sort_rows + type: COUNTER + +trash: + - name: mysql_aborted_clients + type: COUNTER + comment: "mysql aborted clients number" + - name: mysql_aborted_connects + type: COUNTER + - name: mysql_access_denied_errors + type: COUNTER + - name: mysql_aria_pagecache_blocks_not_flushed + type: COUNTER + - name: mysql_aria_pagecache_blocks_unused + type: COUNTER + - name: mysql_aria_pagecache_blocks_used + type: COUNTER + - name: mysql_aria_pagecache_read_requests + type: COUNTER + - name: mysql_aria_pagecache_reads + type: COUNTER + - name: mysql_aria_pagecache_write_requests + type: COUNTER + - name: mysql_aria_pagecache_writes + type: COUNTER + - name: mysql_aria_transaction_log_syncs + type: COUNTER + - name: mysql_binlog_group_commits + - name: mysql_binlog_snapshot_position + - name: mysql_binlog_stmt_cache_disk_use + - name: mysql_binlog_stmt_cache_use + - name: mysql_busy_time + - name: mysql_bytes_received + - name: mysql_bytes_sent + - name: mysql_com_admin_commands + - name: mysql_com_alter_db + - name: mysql_com_alter_db_upgrade + - name: mysql_com_alter_event + - name: mysql_com_alter_function + - name: mysql_com_alter_procedure + - name: mysql_com_alter_server + - name: mysql_com_alter_table + - name: mysql_com_alter_tablespace + - name: mysql_com_analyze + - name: mysql_com_assign_to_keycache + - name: mysql_com_call_procedure + - name: mysql_com_change_db + - name: mysql_com_change_master + - name: mysql_com_check + - name: mysql_com_checksum + - name: mysql_com_create_db + - name: mysql_com_create_event + - name: mysql_com_create_function + - name: mysql_com_create_index + - name: mysql_com_create_procedure + - name: mysql_com_create_server + - name: mysql_com_create_trigger + - name: mysql_com_create_udf + - name: mysql_com_create_user + - name: mysql_com_create_view + - name: mysql_com_dealloc_sql + - name: mysql_com_do + - name: mysql_com_drop_db + - name: mysql_com_drop_event + - name: mysql_com_drop_function + - name: mysql_com_drop_index + - name: mysql_com_drop_procedure + - name: mysql_com_drop_server + - name: mysql_com_drop_trigger + - name: mysql_com_drop_user + - name: mysql_com_drop_view + - name: mysql_com_grant + - name: mysql_com_ha_close + - name: mysql_com_ha_open + - name: mysql_com_ha_read + - name: mysql_com_help + - name: mysql_com_insert_select + - name: mysql_com_install_plugin + - name: mysql_com_kill + - name: mysql_com_load + - name: mysql_com_optimize + - name: mysql_com_preload_keys + - name: mysql_com_prepare_sql + - name: mysql_com_purge + - name: mysql_com_purge_before_date + - name: mysql_com_release_savepoint + - name: mysql_com_rename_table + - name: mysql_com_rename_user + - name: mysql_com_repair + - name: mysql_com_replace + - name: mysql_com_replace_select + - name: mysql_com_reset + - name: mysql_com_resignal + - name: mysql_com_revoke + - name: mysql_com_revoke_all + - name: mysql_com_rollback_to_savepoint + - name: mysql_com_savepoint + - name: mysql_com_select + - name: mysql_com_set_option + - name: mysql_com_show_authors + - name: mysql_com_show_binlog_events + - name: mysql_com_show_binlogs + - name: mysql_com_show_charsets + - name: mysql_com_show_client_statistics + - name: mysql_com_show_collations + - name: mysql_com_show_contributors + - name: mysql_com_show_create_db + - name: mysql_com_show_create_event + - name: mysql_com_show_create_func + - name: mysql_com_show_create_proc + - name: mysql_com_show_create_table + - name: mysql_com_show_create_trigger + - name: mysql_com_show_databases + - name: mysql_com_show_engine_logs + - name: mysql_com_show_engine_mutex + - name: mysql_com_show_engine_status + - name: mysql_com_show_errors + - name: mysql_com_show_events + - name: mysql_com_show_fields + - name: mysql_com_show_function_status + - name: mysql_com_show_grants + - name: mysql_com_show_index_statistics + - name: mysql_com_show_keys + - name: mysql_com_show_master_status + - name: mysql_com_show_open_tables + - name: mysql_com_show_plugins + - name: mysql_com_show_privileges + - name: mysql_com_show_procedure_status + - name: mysql_com_show_processlist + - name: mysql_com_show_profile + - name: mysql_com_show_profiles + - name: mysql_com_show_relaylog_events + - name: mysql_com_show_slave_hosts + - name: mysql_com_show_slave_status + - name: mysql_com_show_status + - name: mysql_com_show_storage_engines + - name: mysql_com_show_table_statistics + - name: mysql_com_show_table_status + - name: mysql_com_show_tables + - name: mysql_com_show_triggers + - name: mysql_com_show_user_statistics + - name: mysql_com_show_variables + - name: mysql_com_show_warnings + - name: mysql_com_signal + - name: mysql_com_slave_start + - name: mysql_com_slave_stop + - name: mysql_com_stmt_send_long_data + - name: mysql_com_truncate + - name: mysql_com_uninstall_plugin + - name: mysql_com_unlock_tables + - name: mysql_com_xa_commit + - name: mysql_com_xa_end + - name: mysql_com_xa_prepare + - name: mysql_com_xa_recover + - name: mysql_com_xa_rollback + - name: mysql_com_xa_start + - name: mysql_cpu_time + - name: mysql_created_tmp_disk_tables + - name: mysql_created_tmp_files + - name: mysql_created_tmp_tables + - name: mysql_delayed_errors + - name: mysql_delayed_insert_threads + - name: mysql_delayed_writes + - name: mysql_empty_queries + - name: mysql_executed_events + - name: mysql_executed_triggers + - name: mysql_feature_dynamic_columns + - name: mysql_feature_fulltext + - name: mysql_feature_gis + - name: mysql_feature_locale + - name: mysql_feature_subquery + - name: mysql_feature_timezone + - name: mysql_feature_trigger + - name: mysql_feature_xml + - name: mysql_flush_commands + - name: mysql_handler_commit + - name: mysql_handler_delete + - name: mysql_handler_discover + - name: mysql_handler_icp_attempts + - name: mysql_handler_icp_match + - name: mysql_handler_mrr_init + - name: mysql_handler_mrr_key_refills + - name: mysql_handler_mrr_rowid_refills + - name: mysql_handler_prepare + - name: mysql_handler_read_first + - name: mysql_handler_read_key + - name: mysql_handler_read_last + - name: mysql_handler_read_next + - name: mysql_handler_read_prev + - name: mysql_handler_read_rnd + - name: mysql_handler_read_rnd_deleted + - name: mysql_handler_read_rnd_next + - name: mysql_handler_rollback + - name: mysql_handler_savepoint + - name: mysql_handler_savepoint_rollback + - name: mysql_handler_tmp_update + - name: mysql_handler_tmp_write + - name: mysql_handler_update + - name: mysql_handler_write + - name: mysql_innodb_adaptive_hash_cells + - name: mysql_innodb_adaptive_hash_hash_searches + - name: mysql_innodb_adaptive_hash_heap_buffers + - name: mysql_innodb_adaptive_hash_non_hash_searches + - name: mysql_innodb_background_log_sync + - name: mysql_innodb_buffer_pool_bytes_data + - name: mysql_innodb_buffer_pool_bytes_dirty + - name: mysql_innodb_buffer_pool_pages_data + - name: mysql_innodb_buffer_pool_pages_dirty + - name: mysql_innodb_buffer_pool_pages_flushed + - name: mysql_innodb_buffer_pool_pages_free + - name: mysql_innodb_buffer_pool_pages_lru_flushed + - name: mysql_innodb_buffer_pool_pages_made_not_young + - name: mysql_innodb_buffer_pool_pages_made_young + - name: mysql_innodb_buffer_pool_pages_misc + - name: mysql_innodb_buffer_pool_pages_old + - name: mysql_innodb_buffer_pool_pages_total + - name: mysql_innodb_buffer_pool_read_ahead + - name: mysql_innodb_buffer_pool_read_ahead_evicted + - name: mysql_innodb_buffer_pool_read_ahead_rnd + - name: mysql_innodb_buffer_pool_wait_free + - name: mysql_innodb_buffer_pool_write_requests + - name: mysql_innodb_checkpoint_age + - name: mysql_innodb_checkpoint_max_age + - name: mysql_innodb_checkpoint_target_age + - name: mysql_innodb_current_row_locks + - name: mysql_innodb_data_fsyncs + - name: mysql_innodb_data_pending_fsyncs + - name: mysql_innodb_data_pending_reads + - name: mysql_innodb_data_pending_writes + - name: mysql_innodb_data_read + - name: mysql_innodb_data_reads + - name: mysql_innodb_data_writes + - name: mysql_innodb_data_written + - name: mysql_innodb_dblwr_pages_written + - name: mysql_innodb_dblwr_writes + - name: mysql_innodb_deadlocks + - name: mysql_innodb_descriptors_memory + - name: mysql_innodb_dict_tables + - name: mysql_innodb_have_atomic_builtins + - name: mysql_innodb_history_list_length + - name: mysql_innodb_ibuf_discarded_delete_marks + - name: mysql_innodb_ibuf_discarded_deletes + - name: mysql_innodb_ibuf_discarded_inserts + - name: mysql_innodb_ibuf_free_list + - name: mysql_innodb_ibuf_merged_delete_marks + - name: mysql_innodb_ibuf_merged_deletes + - name: mysql_innodb_ibuf_merged_inserts + - name: mysql_innodb_ibuf_merges + - name: mysql_innodb_ibuf_segment_size + - name: mysql_innodb_ibuf_size + - name: mysql_innodb_log_waits + - name: mysql_innodb_log_write_requests + - name: mysql_innodb_log_writes + - name: mysql_innodb_lsn_current + - name: mysql_innodb_lsn_flushed + - name: mysql_innodb_lsn_last_checkpoint + - name: mysql_innodb_master_thread_10_second_loops + - name: mysql_innodb_master_thread_1_second_loops + - name: mysql_innodb_master_thread_background_loops + - name: mysql_innodb_master_thread_main_flush_loops + - name: mysql_innodb_master_thread_sleeps + - name: mysql_innodb_max_trx_id + - name: mysql_innodb_mem_adaptive_hash + - name: mysql_innodb_mem_dictionary + - name: mysql_innodb_mem_total + - name: mysql_innodb_mutex_os_waits + - name: mysql_innodb_mutex_spin_rounds + - name: mysql_innodb_mutex_spin_waits + - name: mysql_innodb_oldest_view_low_limit_trx_id + - name: mysql_innodb_os_log_fsyncs + - name: mysql_innodb_os_log_pending_fsyncs + - name: mysql_innodb_os_log_pending_writes + - name: mysql_innodb_os_log_written + - name: mysql_innodb_page_size + - name: mysql_innodb_pages_created + - name: mysql_innodb_pages_read + - name: mysql_innodb_pages_written + - name: mysql_innodb_purge_trx_id + - name: mysql_innodb_purge_undo_no + - name: mysql_innodb_read_views_memory + - name: mysql_innodb_row_lock_current_waits + - name: mysql_innodb_row_lock_time + - name: mysql_innodb_row_lock_time_avg + - name: mysql_innodb_row_lock_time_max + - name: mysql_innodb_row_lock_waits + - name: mysql_innodb_rows_deleted + - name: mysql_innodb_rows_inserted + - name: mysql_innodb_rows_read + - name: mysql_innodb_rows_updated + - name: mysql_innodb_s_lock_os_waits + - name: mysql_innodb_s_lock_spin_rounds + - name: mysql_innodb_s_lock_spin_waits + - name: mysql_innodb_truncated_status_writes + - name: mysql_innodb_x_lock_os_waits + - name: mysql_innodb_x_lock_spin_rounds + - name: mysql_innodb_x_lock_spin_waits + - name: mysql_key_blocks_not_flushed + - name: mysql_key_blocks_unused + - name: mysql_key_blocks_used + - name: mysql_key_blocks_warm + - name: mysql_key_read_requests + - name: mysql_key_reads + - name: mysql_key_write_requests + - name: mysql_key_writes + - name: mysql_last_query_cost + - name: mysql_not_flushed_delayed_rows + - name: mysql_open_table_definitions + - name: mysql_performance_schema_cond_classes_lost + - name: mysql_performance_schema_cond_instances_lost + - name: mysql_performance_schema_file_classes_lost + - name: mysql_performance_schema_file_handles_lost + - name: mysql_performance_schema_file_instances_lost + - name: mysql_performance_schema_locker_lost + - name: mysql_performance_schema_mutex_classes_lost + - name: mysql_performance_schema_mutex_instances_lost + - name: mysql_performance_schema_rwlock_classes_lost + - name: mysql_performance_schema_rwlock_instances_lost + - name: mysql_performance_schema_table_handles_lost + - name: mysql_performance_schema_table_instances_lost + - name: mysql_performance_schema_thread_classes_lost + - name: mysql_performance_schema_thread_instances_lost + - name: mysql_prepared_stmt_count + - name: mysql_process_list_threads_after_create + - name: mysql_process_list_threads_altering_table + - name: mysql_process_list_threads_analyzing + - name: mysql_process_list_threads_checking_permissions + - name: mysql_process_list_threads_checking_table + - name: mysql_process_list_threads_cleaning_up + - name: mysql_process_list_threads_closing_tables + - name: mysql_process_list_threads_converting_heap_to_myisam + - name: mysql_process_list_threads_copying_to_tmp_table + - name: mysql_process_list_threads_creating_sort_index + - name: mysql_process_list_threads_creating_table + - name: mysql_process_list_threads_creating_tmp_table + - name: mysql_process_list_threads_deleting + - name: mysql_process_list_threads_end + - name: mysql_process_list_threads_executing + - name: mysql_process_list_threads_execution_of_init_command + - name: mysql_process_list_threads_flushing_tables + - name: mysql_process_list_threads_freeing_items + - name: mysql_process_list_threads_fulltext_initialization + - name: mysql_process_list_threads_idle + - name: mysql_process_list_threads_init + - name: mysql_process_list_threads_killed + - name: mysql_process_list_threads_logging_slow_query + - name: mysql_process_list_threads_login + - name: mysql_process_list_threads_manage_keys + - name: mysql_process_list_threads_opening_tables + - name: mysql_process_list_threads_optimizing + - name: mysql_process_list_threads_other + - name: mysql_process_list_threads_preparing + - name: mysql_process_list_threads_reading_from_net + - name: mysql_process_list_threads_removing_duplicates + - name: mysql_process_list_threads_removing_tmp_table + - name: mysql_process_list_threads_reopen_tables + - name: mysql_process_list_threads_repair_by_sorting + - name: mysql_process_list_threads_repair_done + - name: mysql_process_list_threads_repair_with_keycache + - name: mysql_process_list_threads_replication_master + - name: mysql_process_list_threads_rolling_back + - name: mysql_process_list_threads_searching_rows_for_update + - name: mysql_process_list_threads_sending_data + - name: mysql_process_list_threads_sorting_for_group + - name: mysql_process_list_threads_sorting_for_order + - name: mysql_process_list_threads_sorting_index + - name: mysql_process_list_threads_sorting_result + - name: mysql_process_list_threads_statistics + - name: mysql_process_list_threads_updating + - name: mysql_process_list_threads_waiting_for_lock + - name: mysql_process_list_threads_waiting_for_table_flush + - name: mysql_process_list_threads_waiting_for_tables + - name: mysql_process_list_threads_waiting_on_cond + - name: mysql_process_list_threads_writing_to_net + - name: mysql_qcache_free_blocks + - name: mysql_qcache_free_memory + - name: mysql_qcache_hits + - name: mysql_qcache_inserts + - name: mysql_qcache_lowmem_prunes + - name: mysql_qcache_not_cached + - name: mysql_qcache_queries_in_cache + - name: mysql_qcache_total_blocks + - name: mysql_questions + - name: mysql_rows_tmp_read + - name: mysql_select_full_join + - name: mysql_select_full_range_join + - name: mysql_select_range + - name: mysql_select_range_check + - name: mysql_select_scan + - name: mysql_slave_heartbeat_period + - name: mysql_slave_open_temp_tables + - name: mysql_slave_received_heartbeats + - name: mysql_slave_retried_transactions + - name: mysql_slave_running + - name: mysql_slow_launch_threads + - name: mysql_slow_queries + - name: mysql_sort_merge_passes + - name: mysql_sort_range + - name: mysql_sort_scan + - name: mysql_ssl_accept_renegotiates + - name: mysql_ssl_accepts + - name: mysql_ssl_callback_cache_hits + - name: mysql_ssl_client_connects + - name: mysql_ssl_connect_renegotiates + - name: mysql_ssl_ctx_verify_depth + - name: mysql_ssl_ctx_verify_mode + - name: mysql_ssl_default_timeout + - name: mysql_ssl_finished_accepts + - name: mysql_ssl_finished_connects + - name: mysql_ssl_session_cache_hits + - name: mysql_ssl_session_cache_misses + - name: mysql_ssl_session_cache_overflows + - name: mysql_ssl_session_cache_size + - name: mysql_ssl_session_cache_timeouts + - name: mysql_ssl_sessions_reused + - name: mysql_ssl_used_session_cache_entries + - name: mysql_ssl_verify_depth + - name: mysql_ssl_verify_mode + - name: mysql_subquery_cache_hit + - name: mysql_subquery_cache_miss + - name: mysql_syncs + - name: mysql_table_locks_immediate + - name: mysql_table_locks_waited + - name: mysql_tc_log_max_pages_used + - name: mysql_tc_log_page_size + - name: mysql_tc_log_page_waits + - name: mysql_threadpool_idle_threads + - name: mysql_threadpool_threads + - name: mysql_threads_cached + - name: mysql_threads_created + - name: mysql_uptime + - name: mysql_uptime_since_flush_status + - name: mysql_users_connections + - name: mysql_variables_aria_block_size + - name: mysql_variables_aria_checkpoint_interval + - name: mysql_variables_aria_checkpoint_log_activity + - name: mysql_variables_aria_force_start_after_recovery_failures + - name: mysql_variables_aria_group_commit_interval + - name: mysql_variables_aria_log_file_size + - name: mysql_variables_aria_max_sort_file_size + - name: mysql_variables_aria_page_checksum + - name: mysql_variables_aria_pagecache_age_threshold + - name: mysql_variables_aria_pagecache_buffer_size + - name: mysql_variables_aria_pagecache_division_limit + - name: mysql_variables_aria_repair_threads + - name: mysql_variables_aria_sort_buffer_size + - name: mysql_variables_aria_used_for_temp_tables + - name: mysql_variables_auto_increment_increment + - name: mysql_variables_auto_increment_offset + - name: mysql_variables_autocommit + - name: mysql_variables_automatic_sp_privileges + - name: mysql_variables_back_log + - name: mysql_variables_big_tables + - name: mysql_variables_binlog_annotate_row_events + - name: mysql_variables_binlog_cache_size + - name: mysql_variables_binlog_direct_non_transactional_updates + - name: mysql_variables_binlog_optimize_thread_scheduling + - name: mysql_variables_binlog_stmt_cache_size + - name: mysql_variables_bulk_insert_buffer_size + - name: mysql_variables_connect_timeout + - name: mysql_variables_deadlock_search_depth_long + - name: mysql_variables_deadlock_search_depth_short + - name: mysql_variables_deadlock_timeout_long + - name: mysql_variables_deadlock_timeout_short + - name: mysql_variables_debug_no_thread_alarm + - name: mysql_variables_default_week_format + - name: mysql_variables_delay_key_write + - name: mysql_variables_delayed_insert_limit + - name: mysql_variables_delayed_insert_timeout + - name: mysql_variables_delayed_queue_size + - name: mysql_variables_div_precision_increment + - name: mysql_variables_engine_condition_pushdown + - name: mysql_variables_event_scheduler + - name: mysql_variables_expensive_subquery_limit + - name: mysql_variables_expire_logs_days + - name: mysql_variables_extra_max_connections + - name: mysql_variables_extra_port + - name: mysql_variables_flush + - name: mysql_variables_flush_time + - name: mysql_variables_foreign_key_checks + - name: mysql_variables_ft_max_word_len + - name: mysql_variables_ft_min_word_len + - name: mysql_variables_ft_query_expansion_limit + - name: mysql_variables_general_log + - name: mysql_variables_group_concat_max_len + - name: mysql_variables_have_compress + - name: mysql_variables_have_crypt + - name: mysql_variables_have_csv + - name: mysql_variables_have_dynamic_loading + - name: mysql_variables_have_geometry + - name: mysql_variables_have_innodb + - name: mysql_variables_have_ndbcluster + - name: mysql_variables_have_partitioning + - name: mysql_variables_have_profiling + - name: mysql_variables_have_query_cache + - name: mysql_variables_have_rtree_keys + - name: mysql_variables_ignore_builtin_innodb + - name: mysql_variables_innodb_adaptive_flushing + - name: mysql_variables_innodb_adaptive_hash_index + - name: mysql_variables_innodb_adaptive_hash_index_partitions + - name: mysql_variables_innodb_additional_mem_pool_size + - name: mysql_variables_innodb_autoextend_increment + - name: mysql_variables_innodb_autoinc_lock_mode + - name: mysql_variables_innodb_blocking_buffer_pool_restore + - name: mysql_variables_innodb_buffer_pool_instances + - name: mysql_variables_innodb_buffer_pool_populate + - name: mysql_variables_innodb_buffer_pool_restore_at_startup + - name: mysql_variables_innodb_buffer_pool_shm_checksum + - name: mysql_variables_innodb_buffer_pool_shm_key + - name: mysql_variables_innodb_buffer_pool_size + - name: mysql_variables_innodb_checkpoint_age_target + - name: mysql_variables_innodb_checksums + - name: mysql_variables_innodb_commit_concurrency + - name: mysql_variables_innodb_concurrency_tickets + - name: mysql_variables_innodb_dict_size_limit + - name: mysql_variables_innodb_doublewrite + - name: mysql_variables_innodb_fake_changes + - name: mysql_variables_innodb_fast_checksum + - name: mysql_variables_innodb_fast_shutdown + - name: mysql_variables_innodb_file_format_check + - name: mysql_variables_innodb_file_per_table + - name: mysql_variables_innodb_flush_log_at_trx_commit + - name: mysql_variables_innodb_force_load_corrupted + - name: mysql_variables_innodb_force_recovery + - name: mysql_variables_innodb_ibuf_accel_rate + - name: mysql_variables_innodb_ibuf_active_contract + - name: mysql_variables_innodb_ibuf_max_size + - name: mysql_variables_innodb_import_table_from_xtrabackup + - name: mysql_variables_innodb_io_capacity + - name: mysql_variables_innodb_kill_idle_transaction + - name: mysql_variables_innodb_large_prefix + - name: mysql_variables_innodb_lazy_drop_table + - name: mysql_variables_innodb_lock_wait_timeout + - name: mysql_variables_innodb_locking_fake_changes + - name: mysql_variables_innodb_locks_unsafe_for_binlog + - name: mysql_variables_innodb_log_block_size + - name: mysql_variables_innodb_log_buffer_size + - name: mysql_variables_innodb_log_file_size + - name: mysql_variables_innodb_log_files_in_group + - name: mysql_variables_innodb_max_bitmap_file_size + - name: mysql_variables_innodb_max_changed_pages + - name: mysql_variables_innodb_max_dirty_pages_pct + - name: mysql_variables_innodb_max_purge_lag + - name: mysql_variables_innodb_merge_sort_block_size + - name: mysql_variables_innodb_mirrored_log_groups + - name: mysql_variables_innodb_old_blocks_pct + - name: mysql_variables_innodb_old_blocks_time + - name: mysql_variables_innodb_open_files + - name: mysql_variables_innodb_page_size + - name: mysql_variables_innodb_print_all_deadlocks + - name: mysql_variables_innodb_purge_batch_size + - name: mysql_variables_innodb_purge_threads + - name: mysql_variables_innodb_random_read_ahead + - name: mysql_variables_innodb_read_ahead_threshold + - name: mysql_variables_innodb_read_io_threads + - name: mysql_variables_innodb_recovery_stats + - name: mysql_variables_innodb_recovery_update_relay_log + - name: mysql_variables_innodb_replication_delay + - name: mysql_variables_innodb_rollback_on_timeout + - name: mysql_variables_innodb_rollback_segments + - name: mysql_variables_innodb_show_locks_held + - name: mysql_variables_innodb_show_verbose_locks + - name: mysql_variables_innodb_simulate_comp_failures + - name: mysql_variables_innodb_spin_wait_delay + - name: mysql_variables_innodb_stats_auto_update + - name: mysql_variables_innodb_stats_modified_counter + - name: mysql_variables_innodb_stats_on_metadata + - name: mysql_variables_innodb_stats_sample_pages + - name: mysql_variables_innodb_stats_traditional + - name: mysql_variables_innodb_stats_update_need_lock + - name: mysql_variables_innodb_strict_mode + - name: mysql_variables_innodb_support_xa + - name: mysql_variables_innodb_sync_spin_loops + - name: mysql_variables_innodb_table_locks + - name: mysql_variables_innodb_thread_concurrency + - name: mysql_variables_innodb_thread_concurrency_timer_based + - name: mysql_variables_innodb_thread_sleep_delay + - name: mysql_variables_innodb_track_changed_pages + - name: mysql_variables_innodb_use_atomic_writes + - name: mysql_variables_innodb_use_fallocate + - name: mysql_variables_innodb_use_global_flush_log_at_trx_commit + - name: mysql_variables_innodb_use_native_aio + - name: mysql_variables_innodb_use_stacktrace + - name: mysql_variables_innodb_use_sys_malloc + - name: mysql_variables_innodb_use_sys_stats_table + - name: mysql_variables_innodb_write_io_threads + - name: mysql_variables_interactive_timeout + - name: mysql_variables_join_buffer_size + - name: mysql_variables_join_buffer_space_limit + - name: mysql_variables_join_cache_level + - name: mysql_variables_keep_files_on_create + - name: mysql_variables_key_buffer_size + - name: mysql_variables_key_cache_age_threshold + - name: mysql_variables_key_cache_block_size + - name: mysql_variables_key_cache_division_limit + - name: mysql_variables_key_cache_segments + - name: mysql_variables_large_files_support + - name: mysql_variables_large_page_size + - name: mysql_variables_large_pages + - name: mysql_variables_local_infile + - name: mysql_variables_lock_wait_timeout + - name: mysql_variables_locked_in_memory + - name: mysql_variables_log + - name: mysql_variables_log_bin + - name: mysql_variables_log_bin_trust_function_creators + - name: mysql_variables_log_queries_not_using_indexes + - name: mysql_variables_log_slave_updates + - name: mysql_variables_log_slow_queries + - name: mysql_variables_log_slow_rate_limit + - name: mysql_variables_log_warnings + - name: mysql_variables_long_query_time + - name: mysql_variables_low_priority_updates + - name: mysql_variables_lower_case_file_system + - name: mysql_variables_lower_case_table_names + - name: mysql_variables_master_verify_checksum + - name: mysql_variables_max_allowed_packet + - name: mysql_variables_max_binlog_cache_size + - name: mysql_variables_max_binlog_size + - name: mysql_variables_max_binlog_stmt_cache_size + - name: mysql_variables_max_connect_errors + - name: mysql_variables_max_delayed_threads + - name: mysql_variables_max_error_count + - name: mysql_variables_max_heap_table_size + - name: mysql_variables_max_insert_delayed_threads + - name: mysql_variables_max_join_size + - name: mysql_variables_max_length_for_sort_data + - name: mysql_variables_max_long_data_size + - name: mysql_variables_max_prepared_stmt_count + - name: mysql_variables_max_relay_log_size + - name: mysql_variables_max_seeks_for_key + - name: mysql_variables_max_sort_length + - name: mysql_variables_max_sp_recursion_depth + - name: mysql_variables_max_tmp_tables + - name: mysql_variables_max_user_connections + - name: mysql_variables_max_write_lock_count + - name: mysql_variables_metadata_locks_cache_size + - name: mysql_variables_min_examined_row_limit + - name: mysql_variables_mrr_buffer_size + - name: mysql_variables_multi_range_count + - name: mysql_variables_myisam_block_size + - name: mysql_variables_myisam_data_pointer_size + - name: mysql_variables_myisam_max_sort_file_size + - name: mysql_variables_myisam_mmap_size + - name: mysql_variables_myisam_repair_threads + - name: mysql_variables_myisam_sort_buffer_size + - name: mysql_variables_myisam_use_mmap + - name: mysql_variables_net_buffer_length + - name: mysql_variables_net_read_timeout + - name: mysql_variables_net_retry_count + - name: mysql_variables_net_write_timeout + - name: mysql_variables_old + - name: mysql_variables_old_alter_table + - name: mysql_variables_old_passwords + - name: mysql_variables_open_files_limit + - name: mysql_variables_optimizer_prune_level + - name: mysql_variables_optimizer_search_depth + - name: mysql_variables_performance_schema + - name: mysql_variables_performance_schema_events_waits_history_long_size + - name: mysql_variables_performance_schema_events_waits_history_size + - name: mysql_variables_performance_schema_max_cond_classes + - name: mysql_variables_performance_schema_max_cond_instances + - name: mysql_variables_performance_schema_max_file_classes + - name: mysql_variables_performance_schema_max_file_handles + - name: mysql_variables_performance_schema_max_file_instances + - name: mysql_variables_performance_schema_max_mutex_classes + - name: mysql_variables_performance_schema_max_mutex_instances + - name: mysql_variables_performance_schema_max_rwlock_classes + - name: mysql_variables_performance_schema_max_rwlock_instances + - name: mysql_variables_performance_schema_max_table_handles + - name: mysql_variables_performance_schema_max_table_instances + - name: mysql_variables_performance_schema_max_thread_classes + - name: mysql_variables_performance_schema_max_thread_instances + - name: mysql_variables_port + - name: mysql_variables_preload_buffer_size + - name: mysql_variables_profiling + - name: mysql_variables_profiling_history_size + - name: mysql_variables_progress_report_time + - name: mysql_variables_protocol_version + - name: mysql_variables_query_alloc_block_size + - name: mysql_variables_query_cache_limit + - name: mysql_variables_query_cache_min_res_unit + - name: mysql_variables_query_cache_size + - name: mysql_variables_query_cache_strip_comments + - name: mysql_variables_query_cache_type + - name: mysql_variables_query_cache_wlock_invalidate + - name: mysql_variables_query_prealloc_size + - name: mysql_variables_range_alloc_block_size + - name: mysql_variables_read_buffer_size + - name: mysql_variables_read_only + - name: mysql_variables_read_rnd_buffer_size + - name: mysql_variables_relay_log_purge + - name: mysql_variables_relay_log_recovery + - name: mysql_variables_relay_log_space_limit + - name: mysql_variables_replicate_annotate_row_events + - name: mysql_variables_report_port + - name: mysql_variables_rowid_merge_buff_size + - name: mysql_variables_rpl_recovery_rank + - name: mysql_variables_secure_auth + - name: mysql_variables_server_id + - name: mysql_variables_skip_external_locking + - name: mysql_variables_skip_name_resolve + - name: mysql_variables_skip_networking + - name: mysql_variables_skip_show_database + - name: mysql_variables_slave_compressed_protocol + - name: mysql_variables_slave_max_allowed_packet + - name: mysql_variables_slave_net_timeout + - name: mysql_variables_slave_skip_errors + - name: mysql_variables_slave_sql_verify_checksum + - name: mysql_variables_slave_transaction_retries + - name: mysql_variables_slow_launch_time + - name: mysql_variables_slow_query_log + - name: mysql_variables_sort_buffer_size + - name: mysql_variables_sql_auto_is_null + - name: mysql_variables_sql_big_selects + - name: mysql_variables_sql_big_tables + - name: mysql_variables_sql_buffer_result + - name: mysql_variables_sql_log_bin + - name: mysql_variables_sql_log_off + - name: mysql_variables_sql_low_priority_updates + - name: mysql_variables_sql_max_join_size + - name: mysql_variables_sql_notes + - name: mysql_variables_sql_quote_show_create + - name: mysql_variables_sql_safe_updates + - name: mysql_variables_sql_select_limit + - name: mysql_variables_sql_slave_skip_counter + - name: mysql_variables_sql_warnings + - name: mysql_variables_stored_program_cache + - name: mysql_variables_sync_binlog + - name: mysql_variables_sync_frm + - name: mysql_variables_sync_master_info + - name: mysql_variables_sync_relay_log + - name: mysql_variables_sync_relay_log_info + - name: mysql_variables_table_definition_cache + - name: mysql_variables_table_open_cache + - name: mysql_variables_thread_cache_size + - name: mysql_variables_thread_concurrency + - name: mysql_variables_thread_pool_idle_timeout + - name: mysql_variables_thread_pool_max_threads + - name: mysql_variables_thread_pool_oversubscribe + - name: mysql_variables_thread_pool_size + - name: mysql_variables_thread_pool_stall_limit + - name: mysql_variables_thread_stack + - name: mysql_variables_timed_mutexes + - name: mysql_variables_tmp_table_size + - name: mysql_variables_transaction_alloc_block_size + - name: mysql_variables_transaction_prealloc_size + - name: mysql_variables_unique_checks + - name: mysql_variables_updatable_views_with_limit + - name: mysql_variables_userstat + - name: mysql_variables_wait_timeout diff --git a/etc/prober.yml b/etc/prober.yml new file mode 100644 index 00000000..9ef20f1d --- /dev/null +++ b/etc/prober.yml @@ -0,0 +1,8 @@ +region: default +workerProcesses: 5 +logger: + dir: logs/prober + level: DEBUG + keepHours: 24 +pluginsConfig: etc/plugins +ignoreConfig: false diff --git a/etc/rdb.yml b/etc/rdb.yml index 6b1523ed..8ce5d9fa 100644 --- a/etc/rdb.yml +++ b/etc/rdb.yml @@ -5,8 +5,15 @@ logger: http: mode: release - cookieDomain: "" - cookieName: ecmc-user + session: + cookieName: ecmc-sid + domain: "" + httpOnly: true + gcInterval: 60 + cookieLifetime: 86400 # 单位秒,0: 与浏览器相同 + +i18n: + lang: zh sso: enable: false @@ -23,7 +30,13 @@ sso: coverAttributes: false stateExpiresIn: 300 -captcha: false +auth: + captcha: false + extraMode: + enable: false # enable whiteList, login retry lock, userControl, ... + whiteList: false + frozenDays: 90 # frozen time (day) + writenOffDays: 365 # writenOff time (day) tokens: - rdb-builtin-token diff --git a/go.mod b/go.mod index ea73b65b..437d9303 100644 --- a/go.mod +++ b/go.mod @@ -3,28 +3,28 @@ module github.com/didi/nightingale go 1.12 require ( - github.com/Shopify/sarama v1.19.0 + github.com/Shopify/sarama v1.27.1 github.com/cespare/xxhash v1.1.0 github.com/codegangsta/negroni v1.0.0 github.com/coreos/go-oidc v2.2.1+incompatible github.com/dgryski/go-tsz v0.0.0-20180227144327-03b7d791f4fe - github.com/eapache/go-resiliency v1.2.0 // indirect github.com/garyburd/redigo v1.6.2 github.com/gin-contrib/pprof v1.3.0 github.com/gin-gonic/gin v1.6.3 github.com/go-sql-driver/mysql v1.5.0 + github.com/google/go-github/v32 v32.1.0 github.com/google/uuid v1.1.2-0.20190416172445-c2e93f3ae59f github.com/gorilla/mux v1.7.3 github.com/hashicorp/golang-lru v0.5.4 github.com/hpcloud/tail v1.0.0 github.com/influxdata/influxdb v1.8.0 + github.com/influxdata/telegraf v1.16.2 github.com/m3db/m3 v0.15.17 github.com/mattn/go-isatty v0.0.12 github.com/mattn/go-sqlite3 v1.14.0 // indirect github.com/mojocn/base64Captcha v1.3.1 github.com/open-falcon/rrdlite v0.0.0-20200214140804-bf5829f786ad github.com/pquerna/cachecontrol v0.0.0-20200819021114-67c6ae64274f // indirect - github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect github.com/robfig/go-cache v0.0.0-20130306151617-9fc39e0dbf62 // indirect github.com/shirou/gopsutil v3.20.11+incompatible github.com/spaolacci/murmur3 v1.1.0 @@ -35,10 +35,8 @@ require ( github.com/ugorji/go/codec v1.1.7 github.com/unrolled/render v1.0.3 go.uber.org/automaxprocs v1.3.0 // indirect - golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de // indirect golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d golang.org/x/text v0.3.3 - google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.0.1 // indirect google.golang.org/protobuf v1.25.0 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df diff --git a/go.sum b/go.sum index 37ee0a44..1d4fcd2b 100644 --- a/go.sum +++ b/go.sum @@ -9,25 +9,44 @@ cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxK cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.51.0 h1:PvKAVQWCtlGUSlZkGW3QLelKaWq7KYv/MW1EboG8bfM= cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0 h1:MZQCQQaRwOrAcuKjiHWHrgKykt4fZyuwF2dtiG3fGW8= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigtable v1.2.0/go.mod h1:JcVAOl45lrTmQfLj7T6TxyMzIN/3FGGcFm+2xVAli2o= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +code.cloudfoundry.org/clock v1.0.0/go.mod h1:QD9Lzhd/ux6eNQVUDVRJX/RKTigpewimNYBi7ivZKY8= collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/azure-amqp-common-go/v3 v3.0.0/go.mod h1:SY08giD/XbhTz07tJdpw1SoxQXHPN30+DI3Z04SYqyg= +github.com/Azure/azure-event-hubs-go/v3 v3.2.0/go.mod h1:BPIIJNH/l/fVHYq3Rm6eg4clbrULrQ3q7+icmqHyyLc= +github.com/Azure/azure-pipeline-go v0.1.8/go.mod h1:XA1kFWRVhSK+KNFiOhfv83Fv8L9achrP7OxIzeTn1Yg= +github.com/Azure/azure-pipeline-go v0.1.9/go.mod h1:XA1kFWRVhSK+KNFiOhfv83Fv8L9achrP7OxIzeTn1Yg= +github.com/Azure/azure-sdk-for-go v37.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v40.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-storage-blob-go v0.6.0/go.mod h1:oGfmITT1V6x//CswqY2gtAHND+xIP64/qL7a5QJix0Y= +github.com/Azure/azure-storage-queue-go v0.0.0-20181215014128-6ed74e755687/go.mod h1:K6am8mT+5iFXgingS9LUc7TmbsW6XBw3nxaRyaMyWc8= +github.com/Azure/go-amqp v0.12.6/go.mod h1:qApuH6OFTSKZFmCOxccvAv5rLizBQf4v8pRmG138DPo= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= +github.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8GNUx2nRB378IPt/1p0= github.com/Azure/go-autorest/autorest v0.10.0/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630= github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= +github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc= +github.com/Azure/go-autorest/autorest/adal v0.8.1/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= +github.com/Azure/go-autorest/autorest/azure/auth v0.4.2/go.mod h1:90gmfKdlmKgfjUpnCEpOJzsUEjrWDSLwHIG73tSXddM= +github.com/Azure/go-autorest/autorest/azure/cli v0.3.1/go.mod h1:ZG5p860J94/0kI9mNJVoIoLgXcirM2gF5i2kWloofxw= github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= @@ -49,12 +68,14 @@ github.com/DataDog/datadog-go v3.7.1+incompatible h1:HmA9qHVrHIAqpSvoCYJ+c6qst0l github.com/DataDog/datadog-go v3.7.1+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Mellanox/rdmamap v0.0.0-20191106181932-7c3c4763a6ee/go.mod h1:jDA6v0TUYrFEIAE5uGJ29LQOeONIgMdP4Rkqb8HUnPM= github.com/MichaelTJones/pcg v0.0.0-20180122055547-df440c6ed7ed h1:hQC4FSwvsLH6rOLJTndsHnANARF9RwW4PbrDTjks/0A= github.com/MichaelTJones/pcg v0.0.0-20180122055547-df440c6ed7ed/go.mod h1:NQ4UMHqyfXyYVmZopcfwPRWJa0rw2aH16eDIltReVUo= +github.com/Microsoft/ApplicationInsights-Go v0.4.2/go.mod h1:CukZ/G66zxXtI+h/VcVn3eVVDGDHfXM2zVILF7bMmsg= +github.com/Microsoft/go-winio v0.4.9/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= -github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= @@ -66,36 +87,46 @@ github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdko github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/RoaringBitmap/roaring v0.4.21 h1:WJ/zIlNX4wQZ9x8Ey33O1UaD9TCTakYsdLFSBcTwH+8= github.com/RoaringBitmap/roaring v0.4.21/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo= -github.com/Shopify/sarama v1.19.0 h1:9oksLxC6uxVPHPVYUmq6xhr1BOF/hHobWH2UzO67z1s= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/sarama v1.27.1 h1:iUlzHymqWsITyttu6KxazcAz8WEj5FqcwFK/oEi7rE8= +github.com/Shopify/sarama v1.27.1/go.mod h1:g5s5osgELxgM+Md9Qni9rzo7Rbt+vvFQI4bt/Mc93II= github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/aerospike/aerospike-client-go v1.27.0/go.mod h1:zj8LBEnWBDOVEIJt8LvaRvDG5ARAoa5dBeHaB472NRc= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5 h1:P5U+E4x5OkVEKQDklVPmzs71WM56RTTRqV4OrDC//Y4= github.com/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5/go.mod h1:976q2ETgjT2snVCf2ZaBnyBbVoPERGjUz+0sofzEfro= +github.com/amir/raidman v0.0.0-20170415203553-1ccc43bfb9c9/go.mod h1:eliMa/PW+RDr2QLWRmLH1R1ZA4RInpmvOzDDXtaIZkc= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q= github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0= +github.com/apex/log v1.3.0 h1:1fyfbPvUwD10nMoh3hY6MXzvZShJQn9/ck7ATgAt5pA= github.com/apex/log v1.3.0/go.mod h1:jd8Vpsr46WAe3EZSQ/IUMs2qQD/GOycT5rPWCO1yGcs= github.com/apex/logs v0.0.4/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo= github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE= github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3stzu0Xys= +github.com/aristanetworks/glog v0.0.0-20191112221043-67e8567f59f3/go.mod h1:KASm+qXFKs/xjSoWn30NrWBBvdTTQq+UjkhjEJHfSFA= +github.com/aristanetworks/goarista v0.0.0-20190325233358-a123909ec740/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-metrics v0.3.0/go.mod h1:zXjbSimjXTd7vOpY8B0/2LpvNvDoXBuplAD+gJD3GYs= github.com/armon/go-metrics v0.3.4 h1:Xqf+7f2Vhl9tsqDYmXhnXInUdcrtgpRNpIA15/uldSc= github.com/armon/go-metrics v0.3.4/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= @@ -107,16 +138,20 @@ github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQ github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.29.18/go.mod h1:1KvfttTE3SPKMpo8g2c6jL3ZKfXtFvKscTgahTma5Xg= +github.com/aws/aws-sdk-go v1.34.34/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= +github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bitly/go-hostpool v0.1.0/go.mod h1:4gOCgp6+NZnVqlKyZ/iBZFTAJKembaVENUpMkpg42fw= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/bmatcuk/doublestar v1.3.1/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b h1:AP/Y7sqYicnjGDfD5VcY4CIfh1hRXBUavxrvELjTiOE= github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b/go.mod h1:ac9efd0D1fsDb3EJvhqgXRbFx7bs2wqZ10HQPeU8U/Q= @@ -124,8 +159,10 @@ github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx2 github.com/briandowns/spinner v1.11.1/go.mod h1:QOuQk7x+EaDASo80FEXwlwiA+j/PPIcX3FScO+3/ZPQ= github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= github.com/c2h5oh/datasize v0.0.0-20171227191756-4eba002a5eae/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= +github.com/caio/go-tdigest v2.3.0+incompatible/go.mod h1:sHQM/ubZStBUmF1WbB8FAm8q9GjDajLC5T7ydxE3JHI= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v0.0.0-20181003080854-62661b46c409/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/cenkalti/backoff v2.0.0+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= @@ -139,15 +176,18 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/cisco-ie/nx-telemetry-proto v0.0.0-20190531143454-82441e232cf6/go.mod h1:ugEfq4B8T8ciw/h5mCkgdiDRFS4CkqqhH2dymDB4knc= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/codegangsta/negroni v1.0.0 h1:+aYywywx4bnKXWvoWtRfJ91vC59NbEhEY03sZjQhbVY= github.com/codegangsta/negroni v1.0.0/go.mod h1:v0y3T5G7Y1UlFfyxFn/QLRU4a2EuNau2iZY63YTKWo0= +github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/continuity v0.0.0-20200413184840-d3ef23f19fbb/go.mod h1:Dq467ZllaHgAtVp4p1xUQWBrFXR9s/wyoTpG8zOJGkY= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -164,9 +204,13 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7 github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/couchbase/go-couchbase v0.0.0-20180501122049-16db1f1fe037/go.mod h1:TWI8EKQMs5u5jLKW/tsb9VwauIrMIxQG1r5fMsswK5U= +github.com/couchbase/gomemcached v0.0.0-20180502221210-0da75df14530/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c= +github.com/couchbase/goutils v0.0.0-20180530154633-e865a1461c8a/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -174,20 +218,26 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4 h1:YcpmyvADGYw5LqMnHqSkyIELsHCGF6PkrmM31V8rF7o= github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= +github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4= github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dgryski/go-sip13 v0.0.0-20190329191031-25c5027a8c7b/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dgryski/go-tsz v0.0.0-20180227144327-03b7d791f4fe h1:VOrqop9SqFzqwZpROEOZpIufuLEUoJ3reNhdOdC9Zzw= github.com/dgryski/go-tsz v0.0.0-20180227144327-03b7d791f4fe/go.mod h1:ft6P746mYUFQBCsH3OkFBG8FtjLx1XclLMo+9Jh1Yts= +github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= +github.com/docker/distribution v2.6.0-rc.1.0.20170726174610-edc3ab29cdff+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v17.12.0-ce-rc1.0.20200916142827-bd33bbf0497b+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/libnetwork v0.8.0-dev.2.0.20181012153825-d7b61745d166/go.mod h1:93m0aTqz6z+g32wla4l4WxTrdtvBRmVzYRkYvasA5Z8= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4 h1:qk/FSDDxo05wdJH28W+p5yivv7LuLYLRXPPD8KQCtZs= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/eapache/go-resiliency v1.1.0 h1:1NtRmCAqadE2FN4ZcN6g90TP3uk8cg9rn9eNK2197aU= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-resiliency v1.2.0 h1:v7g92e/KSN71Rq7vSThKaWIq68fL4YHvWyiUKorFR1Q= github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= @@ -208,16 +258,21 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/ericchiang/k8s v1.2.0 h1:vxrMwEzY43oxu8aZyD/7b1s8tsBM+xoUoxjWECWFbPI= +github.com/ericchiang/k8s v1.2.0/go.mod h1:/OmBgSq2cd9IANnsGHGlEz27nwMZV2YxlpXuQtU3Bz4= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/fortytw2/leaktest v1.2.1-0.20180901000122-b433bbd6d743 h1:QDM8xNoGxemDHdExynv+HzqkTPsFFZ8EyZdMwGElpGg= github.com/fortytw2/leaktest v1.2.1-0.20180901000122-b433bbd6d743/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/fossas/fossa-cli v1.0.30/go.mod h1:5K4/qTj0P2qaT1G3SccFidhmazoJ9dm/OexAAYT8lOI= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= +github.com/frankban/quicktest v1.10.2 h1:19ARM85nVi4xH7xPXuc5eM/udya5ieh7b/Sv+d844Tk= +github.com/frankban/quicktest v1.10.2/go.mod h1:K+q6oSqb0W0Ininfk863uOk1lMy69l/P6txr3mVT54s= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/garethr/kubeval v0.0.0-20180821130434-c44f5193dc94/go.mod h1:L8VwozDBY4bGI25r29I6FURZus8xlVo/B7lNOSfre2g= @@ -225,6 +280,8 @@ github.com/garyburd/redigo v1.6.2 h1:yE/pwKCrbLpLpQICzYTeZ7JsTA/C53wFTJHaEtRqniM github.com/garyburd/redigo v1.6.2/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ERFA1PUxfmGpolnw2v0bKOREu5ew= +github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I= github.com/gin-contrib/pprof v1.3.0 h1:G9eK6HnbkSqDZBYbzG4wrjCsA4e+cvYAHUZw6W+W9K0= github.com/gin-contrib/pprof v1.3.0/go.mod h1:waMjT1H9b179t3CxuG1cV3DHpga6ybizwfBaM5OXaB0= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= @@ -233,6 +290,7 @@ github.com/gin-gonic/gin v1.6.2/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwv github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/glinton/ping v0.1.4-0.20200311211934-5ac87da8cd96/go.mod h1:uY+1eqFUyotrQxF1wYFNtMeHp/swbYRsoGzfcPZ8x3o= github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= @@ -243,6 +301,7 @@ github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1T github.com/gnewton/jargo v0.0.0-20150417131352-41f5f186a805/go.mod h1:x+HLDnZexLq1FmhrdgFf4c3EWGbqhU3ITvISBFyzvRo= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= @@ -250,6 +309,7 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E= github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= @@ -299,6 +359,8 @@ github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD87 github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= +github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= @@ -306,6 +368,10 @@ github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk8V3XHWUcJmYTh+ZnlHVyc+A4oZYS3Y= github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM= +github.com/goburrow/modbus v0.1.0/go.mod h1:Kx552D5rLIS8E7TyUwQ/UdHEqvX5T8tyiGBTlzMcZBg= +github.com/goburrow/serial v0.1.0/go.mod h1:sAiqG0nRVswsm1C97xsttiYCzSLBmUZ/VSlVLZJ8haA= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/gofrs/uuid v2.1.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -321,19 +387,22 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7 h1:5ZkaAPbicIKTF2I64qf5Fh8Aa83Q/dnOafMYV0OMwjA= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -356,8 +425,13 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-github/v30 v30.1.0/go.mod h1:n8jBpHl45a/rlBUtRJMOG4GhNADUQFEufcolZ95JfU8= +github.com/google/go-github/v32 v32.1.0 h1:GWkQOdXqviCPx7Q7Fj+KyPoGm4SwHRh8rheoPhd27II= +github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI= github.com/google/go-jsonnet v0.16.0/go.mod h1:sOcuej3UW1vpPTZOr8L7RQimqai1a57bt5j22LzGZCw= +github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -365,6 +439,7 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -374,18 +449,16 @@ github.com/google/uuid v1.1.2-0.20190416172445-c2e93f3ae59f/go.mod h1:TIyPZe4Mgq github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/gopcua/opcua v0.1.12/go.mod h1:a6QH4F9XeODklCmWuvaOdL8v9H0d73CEKUHWVZLQyE8= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gophercloud/gophercloud v0.8.0/go.mod h1:Kc/QKr9thLKruO/dG0szY8kRIYS+iENz0ziI0hJf76A= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw= github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/handlers v1.3.0/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= github.com/gorilla/handlers v1.4.2 h1:0QniY0USkHQ1RGCLfKxeNHK9bkDHGRYGNDFBCS+YARg= github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= -github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= @@ -407,6 +480,9 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.14.1 h1:YuM9SXYy583fxvSOkzCDyBPCtY+/IMSHEG1dKFMLZsA= github.com/grpc-ecosystem/grpc-gateway v1.14.1/go.mod h1:6CwZWGDSPRJidgKAtJVvND6soZe6fT7iteq8wDPdhb0= +github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= +github.com/harlow/kinesis-consumer v0.3.1-0.20181230152818-2f58b136fee0/go.mod h1:dk23l2BruuUzRP8wbybQbPn3J7sZga2QHICCeaEy5rQ= +github.com/hashicorp/consul v1.2.1/go.mod h1:mFrjN1mfidgJfYP1xrJCF+AfRhr6Eaqhb2+sfyn/OOI= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/api v1.4.0/go.mod h1:xc8u05kyMa3Wjr9eEAsIAo3dg8+LywT5E/Cl7cNS5nU= @@ -428,6 +504,7 @@ github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHh github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90/go.mod h1:o4zcYY1e0GEZI6eSEr+43QDYmuGglw1qSO6qdHUHCgg= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= @@ -441,7 +518,6 @@ github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= @@ -453,8 +529,10 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/memberlist v0.1.4/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/memberlist v0.1.5/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/memberlist v0.2.2 h1:5+RffWKwqJ71YPu9mWsF7ZOscZmwfasdA8kbdC7AO2g= github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= +github.com/hashicorp/serf v0.8.1/go.mod h1:h/Ru6tmZazX7WO/GDmwdpS975F019L4t5ng5IgwbNrE= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -466,6 +544,7 @@ github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf/go.mod h1:hyb9oH7vZsitZCiBt0ZvifOrB+qc8PS5IiilCIb87rg= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/flux v0.65.0/go.mod h1:BwN2XG2lMszOoquQaFdPET8FRQfrXiZsWmcMO9rkaVY= +github.com/influxdata/go-syslog/v2 v2.0.1/go.mod h1:hjvie1UTaD5E1fTnDmxaCw8RRDrT4Ve+XHr5O2dKSCo= github.com/influxdata/influxdb v1.7.7/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= github.com/influxdata/influxdb v1.8.0 h1:/X+G+i3udzHVxpBMuXdPZcUbkIE0ouT+6U+CzQTsOys= github.com/influxdata/influxdb v1.8.0/go.mod h1:SIzcnsjaHRFpmlxpJ4S3NT64qtEKYweNTUMb/vh0OMQ= @@ -474,8 +553,15 @@ github.com/influxdata/influxql v1.1.0/go.mod h1:KpVI7okXjK6PRi3Z5B+mtKZli+R1DnZg github.com/influxdata/line-protocol v0.0.0-20180522152040-32c6aa80de5e/go.mod h1:4kt73NQhadE3daL3WhR5EJ/J2ocX0PZzwxQ0gXJ7oFE= github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19ybifQhZoQNF5D8= github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bSgUQ7q5ZLSO+bKBGqJiCBGAl+9DxyW63zLTujjUlOE= +github.com/influxdata/tail v1.0.1-0.20200707181643-03a791b270e4/go.mod h1:VeiWgI3qaGdJWust2fP27a6J+koITo/1c/UhxeOxgaM= github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0= +github.com/influxdata/telegraf v1.16.2 h1:G988b0+CL2IVDft9V2ZUteKgnbp+eI7vtQ0liPhQKxw= +github.com/influxdata/telegraf v1.16.2/go.mod h1:LZ/6hlf60cwqGr8phfbRKf8x1HoAoqxoMpTp/iqcNXk= +github.com/influxdata/toml v0.0.0-20190415235208-270119a8ce65/go.mod h1:zApaNFpP/bTpQItGZNNUMISDMDAnTXu9UqJ4yT3ocz8= github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po= +github.com/influxdata/wlog v0.0.0-20160411224016-7c63b0a71ef8/go.mod h1:/2NMgWB1DHM1ti/gqhOlg+LJeBVk6FqR5aVGYY0hlwI= +github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= +github.com/jackc/pgx v3.6.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= @@ -493,10 +579,15 @@ github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJS github.com/jhump/protoreflect v1.6.1 h1:4/2yi5LyDPP7nN+Hiird1SAJ6YoxUm13/oxHGRnbPd8= github.com/jhump/protoreflect v1.6.1/go.mod h1:RZQ/lnuN+zqeRVpQigTwO6o0AJUkxbnSnpuG7toUTG4= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= +github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -511,33 +602,43 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0= +github.com/kardianos/service v1.0.0/go.mod h1:8CzDhVuCuugtsHyZoTvsOBuvonN/UDBvl0kH+BUxvbo= +github.com/karrick/godirwalk v1.12.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.11.0 h1:wJbzvpYMVGG9iTI9VxpnNZfd4DzMPoCWze3GgSqz8yg= +github.com/klauspost/compress v1.11.0/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg= github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kubernetes/apimachinery v0.0.0-20190119020841-d41becfba9ee/go.mod h1:Pe/YBTPc3vqoMkbuIWPH8CF9ehINdvNyS0dP3J6HC0s= github.com/kylelemons/godebug v0.0.0-20160406211939-eadb3ce320cb/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/leanovate/gopter v0.2.8 h1:eFPtJ3aa5zLfbxGROSNY75T9Dume60CWBAqoWQ3h/ig= github.com/leanovate/gopter v0.2.8/go.mod h1:gNcbPWNEWRe4lm+bycKqxUYoH5uoVje5SkOJ3uoLer8= +github.com/leesper/go_rng v0.0.0-20190531154944-a612b043e353/go.mod h1:N0SVk0uhy+E1PZ3C9ctsPRlvOPAFPkCNlcPBDkt0N3U= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= +github.com/leodido/ragel-machinery v0.0.0-20181214104525-299bdde78165/go.mod h1:WZxr2/6a/Ar9bMDc2rN/LJrE/hF6bXE4LPyDSIxwAfg= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.6.0 h1:I5DPxhYJChW9KYc66se+oKFFQX6VuQrKiprsX6ivRZc= github.com/lib/pq v1.6.0/go.mod h1:4vXEAYvW1fRQ2/FhZ78H73A60MHw1geSm145z2mdY1g= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743 h1:143Bb8f8DuGWck/xpNUOckBVYfFbBTnLevfRZ1aVVqo= @@ -579,6 +680,7 @@ github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP github.com/magiconair/properties v1.8.2 h1:znVR8Q4g7/WlcvsxLBRWvo+vtFJUAbDn3w+Yak2xVMI= github.com/magiconair/properties v1.8.2/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20180717111219-efc7eb8984d6/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -604,11 +706,17 @@ github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4f github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mauricelam/genny v0.0.0-20180903214747-eb2c5232c885/go.mod h1:wRyVMWiOZeVj+MieWS5tIBBtJ3RtqqMbPsA5Z+t5b5U= +github.com/mdlayher/apcupsd v0.0.0-20190314144147-eb3dd99a75fe/go.mod h1:y3mw3VG+t0m20OMqpG8RQqw8cDXvShVb+L8Z8FEnebw= +github.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc= +github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= +github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= +github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.27 h1:aEH/kqUzUxGJ/UHcEKdJY+ugH6WEzsEBBSPa8zuy1aM= github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= +github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -631,19 +739,27 @@ github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mojocn/base64Captcha v1.3.1 h1:2Wbkt8Oc8qjmNJ5GyOfSo4tgVQPsbKMftqASnq8GlT0= github.com/mojocn/base64Captcha v1.3.1/go.mod h1:wAQCKEc5bDujxKRmbT6/vTnTt5CjStQ8bRfPWUuz/iY= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae h1:VeRdUYdCw49yizlSbMEn2SZ+gT+3IUKx8BqxyQdz+BY= github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= +github.com/multiplay/go-ts3 v1.0.0/go.mod h1:14S6cS3fLNT3xOytrA/DkRyAFNuQLMLEqOYAsf87IbQ= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 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/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= +github.com/nats-io/nats-server/v2 v2.1.4/go.mod h1:Jw1Z28soD/QasIA2uWjXyM9El1jly3YwyFOuR8tH1rg= github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/newrelic/newrelic-telemetry-sdk-go v0.2.0/go.mod h1:G9MqE/cHGv3Hx3qpYhfuyFUsGx2DpVcGi1iJIqTg+JQ= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nsqio/go-nsq v1.0.7/go.mod h1:XP5zaUs3pqf+Q71EqUJs3HYfBIqfK6G83WQMdNN+Ito= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= @@ -663,6 +779,7 @@ github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/open-falcon/rrdlite v0.0.0-20200214140804-bf5829f786ad h1:GXUy5t8CYdaaEj1lRnE22CbHVY1M5h6Rv4kk0PJQc54= github.com/open-falcon/rrdlite v0.0.0-20200214140804-bf5829f786ad/go.mod h1:pXROoG0iWVnqq4u2Ii97S0Vt9iCTVypshsl9HXsV6cs= +github.com/openconfig/gnmi v0.0.0-20180912164834-33a1865c3029/go.mod h1:t+O9It+LKzfOAhKTT5O0ehDix+MTqbtT0T9t+7zzOvc= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= @@ -678,6 +795,7 @@ github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxS github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/openzipkin/zipkin-go-opentracing v0.3.4/go.mod h1:js2AbwmHW0YD9DwIw2JhQWmbfFi/UnWyYwdVhqbCDOE= github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= @@ -698,8 +816,9 @@ github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHu github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ= github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= -github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI= +github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -721,6 +840,7 @@ github.com/prashantv/protectmem v0.0.0-20171002184600-e20412882b3a h1:AA9vgIBDjM github.com/prashantv/protectmem v0.0.0-20171002184600-e20412882b3a/go.mod h1:lzZQ3Noex5pfAy7mkAeCjcBDteYU85uWWnJ/y6gKU8k= github.com/prometheus/alertmanager v0.20.0/go.mod h1:9g2i48FAyZW6BtbsnvHtMHQXl2aVtrORKwKVCQ+nbrg= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= @@ -739,6 +859,7 @@ github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6T github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -748,11 +869,11 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx github.com/prometheus/prometheus v1.8.2-0.20200420081721-18254838fbe2/go.mod h1:ZnfuiMn3LNsry2q7ECmRe4WcscxmJSd2dIFpOi4w3lM= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rakyll/statik v0.1.6/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs= -github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ= github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/remeh/sizedwaitgroup v1.0.0/go.mod h1:3j2R4OIe/SeS6YDhICBy22RWjJC5eNCJ1V+9+NVNYlo= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc= github.com/rhysd/go-github-selfupdate v1.2.2/go.mod h1:khesvSyKcXDUxeySCedFh621iawCks0dS/QnHPcpCws= github.com/robfig/go-cache v0.0.0-20130306151617-9fc39e0dbf62 h1:pyecQtsPmlkCsMkYhT5iZ+sUXuwee+OvfuJjinEA3ko= @@ -768,6 +889,8 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/rveen/ogdl v0.0.0-20200522080342-eeeda1a978e7/go.mod h1:9fqUB54wJS9u5TSXJZhRfTdh1lXVxTytDjed7t2cNdw= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/safchain/ethtool v0.0.0-20200218184317-f459e2d13664/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= +github.com/samuel/go-zookeeper v0.0.0-20180130194729-c4fab1ac1bec/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= @@ -780,19 +903,19 @@ github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shirou/gopsutil v2.17.13-0.20180801053943-8048a2e9c577+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shirou/gopsutil v2.20.5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shirou/gopsutil v2.20.7+incompatible h1:Ymv4OD12d6zm+2yONe39VSmp2XooJe8za7ngOLW/o/w= -github.com/shirou/gopsutil v2.20.7+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/shirou/gopsutil v2.20.9+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shirou/gopsutil v3.20.11+incompatible h1:LJr4ZQK4mPpIV5gOa4jCOKOGb4ty4DZO54I4FGqIpto= github.com/shirou/gopsutil v3.20.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= +github.com/shopspring/decimal v0.0.0-20200105231215-408a2507e114/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.0.0 h1:UVQPSSmc3qtTi+zPPkCXvZX9VvW/xT/NsRvKfwY81a8= github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= @@ -802,8 +925,8 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9 github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs= github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/soniah/gosnmp v1.25.0/go.mod h1:8YvfZxH388NIIw2A+X5z2Oh97VcNhtmxDLt5QeUzVuQ= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -825,7 +948,6 @@ github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0 github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -833,6 +955,7 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk= github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= +github.com/streadway/amqp v0.0.0-20180528204448-e5adc2ada8b8/go.mod h1:1WNBiOZtZQLpVAyu0iTduoJL9hEsMloAK5XWrtW0xdY= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v1.0.0 h1:kuuDrUJFZL1QYL9hUNuCxNObNzB0bV/ZG5jV3RWAQgo= @@ -840,6 +963,8 @@ github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1Sd github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/streadway/quantile v0.0.0-20150917103942-b0c588724d25 h1:7z3LSn867ex6VSaahyKadf4WtSsJIgne6A1WLOAGM8A= github.com/streadway/quantile v0.0.0-20150917103942-b0c588724d25/go.mod h1:lbP8tGiBjZ5YWIc2fzuRpTaz0b/53vT6PEs3QuAWzuU= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= @@ -849,7 +974,11 @@ github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.2.1-0.20190917103637-de67a6614a4d h1:YN4gX82mT31qsizy2jRheOCrGLCs15VF9SV5XPuBvkQ= github.com/subosito/gotenv v1.2.1-0.20190917103637-de67a6614a4d/go.mod h1:GVSeM7r0P1RI1gOKYyN9IuNkhMmQwKGsjVf3ulDrdzo= +github.com/tbrandon/mbserver v0.0.0-20170611213546-993e1772cc62/go.mod h1:qUzPVlSj2UgxJkVbH0ZwuuiR46U8RBMDT5KLY78Ifpw= github.com/tcnksm/go-gitconfig v0.1.2/go.mod h1:/8EhP4H7oJZdIPyT+/UIsG87kTzrzM4UsLGSItWYCpE= +github.com/tedsuo/ifrit v0.0.0-20191009134036-9a97d0632f00/go.mod h1:eyZnKCc955uh98WQvzOm0dgAeLnf2O0Rz0LPoC5ze+0= +github.com/tidwall/gjson v1.6.0/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls= +github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tinylib/msgp v1.1.0 h1:9fQd+ICuRIu/ue4vxJZu6/LzxN0HwMds2nq/0cFvxHU= @@ -887,12 +1016,20 @@ github.com/unrolled/render v1.0.3/go.mod h1:gN9T0NhL4Bfbwu8ann7Ry/TGHYfosul+J0ob github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= -github.com/vmihailenco/msgpack v2.8.3+incompatible h1:76LCLwxS08gKHRpGA10PBxfWk72JfUH6mgzp2+URwYM= +github.com/vishvananda/netlink v0.0.0-20171020171820-b2de5d10e38e/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= +github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= +github.com/vjeantet/grok v1.0.0/go.mod h1:/FWYEVYekkm+2VjcFmO9PufDU5FgXHUz9oy2EGqmQBo= github.com/vmihailenco/msgpack v2.8.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= +github.com/vmware/govmomi v0.19.0/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU= +github.com/wavefronthq/wavefront-sdk-go v0.9.2/go.mod h1:hQI6y8M9OtTCtc0xdwh+dCER4osxXdEAeCpacjpDZEU= github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/willf/bitset v1.1.10 h1:NotGKqX0KwQ72NUzqrjZq5ipPNDQex9lo3WpaS8L2sc= github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= +github.com/wvanbergen/kafka v0.0.0-20171203153745-e2edea948ddf/go.mod h1:nxx7XRXbR9ykhnC8lXqQyJS0rfvJGxKyKw/sT1YOttg= +github.com/wvanbergen/kazoo-go v0.0.0-20180202103751-f72d8611297a/go.mod h1:vQQATAGxVK20DC1rRubTJbZDDhhpA4QfU02pMdPxGO4= github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= +github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= +github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= @@ -900,7 +1037,9 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/gopher-lua v0.0.0-20180630135845-46796da1b0b4/go.mod h1:aEV29XrmTYFr3CiRxZeGHpkvbwq+prZduBqMaascyCU= github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= @@ -914,6 +1053,8 @@ go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.starlark.net v0.0.0-20200901195727-6e684ef5eeee/go.mod h1:f0znQkUKRrkk36XxWbGjMqQM8wGv/xHBVE2qc3B5oFU= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -934,6 +1075,7 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -949,14 +1091,16 @@ golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= +golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig= -golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -967,6 +1111,9 @@ golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm0 golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190501045829-6d32002ffd75/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= @@ -979,13 +1126,16 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1009,11 +1159,15 @@ golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191003171128-d98b1b443823/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1021,8 +1175,9 @@ golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA= +golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1036,14 +1191,15 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6 h1:DvY3Zkh7KabQE/kfzMvYvKirSiguP9Q/veMtkYyf0o8= golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -1087,22 +1243,34 @@ golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191203134012-c197fd4bf371/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200108203644-89082a384178/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200305205014-bc073721adb6/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200317043434-63da46f3035e/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b h1:zSzQJAznWxAh9fZxiPy2FZo+ZZEYoYFYYDYdOrU7AaM= golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 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= +golang.zx2c4.com/wireguard v0.0.20200121/go.mod h1:P2HsVp8SKwZEufsnezXZA4GRX/T49/HlU7DGuelXsU4= +golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200205215550-e35592f146e4/go.mod h1:UdS9frhv65KTfwxME1xE8+rHYoFpbm36gOud1GhBe9c= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.6.0/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU= +gonum.org/v1/gonum v0.6.2/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU= gonum.org/v1/netlib v0.0.0-20181029234149-ec6d1f5cefe6/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= @@ -1114,6 +1282,7 @@ google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEn google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1142,8 +1311,14 @@ google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200108215221-bd8f9a0ef82f/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200305110556-506484158171 h1:xes2Q2k+d/+YNXVw0FpZkIDJiaux4OVrRKXRAzH6A0U= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200317114155-1f3552e48f24 h1:IGPykv426z7LZSVPlaPufOyphngM4at5uZ7x5alaFvE= +google.golang.org/genproto v0.0.0-20200317114155-1f3552e48f24/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= @@ -1160,18 +1335,15 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.33.2 h1:EQyQC3sa8M+p6Ulc8yy9SWSS2GVwyRc83gAbG8lrl4o= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.0.1 h1:M8spwkmx0pHrPq+uMdl22w5CvJ/Y+oAJTIs9oGoCpOE= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.0.1/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= @@ -1182,12 +1354,15 @@ gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d h1:TxyelI5cVkbREznMhfzycHdkp5cLA7DpE+GKjSslYhM= gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -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/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fatih/pool.v2 v2.0.0/go.mod h1:8xVGeu1/2jr2wm5V9SPuMht2H5AEmf5aFMGSQixtjTY= +gopkg.in/fsnotify.v1 v1.2.1/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/fsnotify/fsnotify.v1 v1.4.7/go.mod h1:Fyux9zXlo4rWoMSIzpn9fDAYjalPqJ/K1qJ27s+7ltE= @@ -1198,18 +1373,26 @@ gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8 gopkg.in/go-playground/validator.v9 v9.7.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= 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/gorethink/gorethink.v3 v3.0.5/go.mod h1:+3yIIHJUGMBK+wyPH+iN5TP+88ikFDfZdqTlK3Y9q8I= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.51.1 h1:GyboHr4UqMiLUybYjd22ZjQIKEJEpgtLXtuGbR21Oho= gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/jcmturner/aescts.v1 v1.0.1 h1:cVVZBK2b1zY26haWB4vbBiZrfFQnfbTVrE3xZq6hrEw= gopkg.in/jcmturner/aescts.v1 v1.0.1/go.mod h1:nsR8qBOg+OucoIW+WMhB3GspUQXq9XorLnQb9XtvcOo= +gopkg.in/jcmturner/dnsutils.v1 v1.0.1 h1:cIuC1OLRGZrld+16ZJvvZxVJeKPsvd5eUIvxfoN5hSM= gopkg.in/jcmturner/dnsutils.v1 v1.0.1/go.mod h1:m3v+5svpVOhtFAP/wSz+yzh4Mc0Fg7eRhxkJMWSIz9Q= +gopkg.in/jcmturner/goidentity.v3 v3.0.0 h1:1duIyWiTaYvVx3YX2CYtpJbUFd7/UuPYCfgXtQ3VTbI= gopkg.in/jcmturner/goidentity.v3 v3.0.0/go.mod h1:oG2kH0IvSYNIu80dVAyu/yoefjq1mNfM5bm88whjWx4= +gopkg.in/jcmturner/gokrb5.v7 v7.5.0 h1:a9tsXlIDD9SKxotJMK3niV7rPZAJeX2aD/0yg3qlIrg= gopkg.in/jcmturner/gokrb5.v7 v7.5.0/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM= +gopkg.in/jcmturner/rpc.v1 v1.1.0 h1:QHIUxTX1ISuAv9dD2wJ9HWQVuWDX/Zc0PfeC2tjc4rU= gopkg.in/jcmturner/rpc.v1 v1.1.0/go.mod h1:YIdkC4XfD6GXbzje11McwsDuOlZQSb9W4vfLvuNnlv8= gopkg.in/ldap.v3 v3.1.0 h1:DIDWEjI7vQWREh0S8X5/NFPCZ3MCVd55LmXKPW4XLGE= gopkg.in/ldap.v3 v3.1.0/go.mod h1:dQjCc0R0kfyFjIlWNMH1DORwUASZyDxo2Ry1B51dXaQ= +gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= +gopkg.in/olivere/elastic.v5 v5.0.70/go.mod h1:FylZT6jQWtfHsicejzOm3jIMVPOAksa80i3o+6qtQRk= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/russross/blackfriday.v2 v2.0.0/go.mod h1:6sSBNz/GtOm/pJTuh5UmBK2ZHfmnxGbl2NZg1UliSOI= gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= @@ -1217,6 +1400,7 @@ gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76 gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8= +gopkg.in/tomb.v1 v1.0.0-20140529071818-c131134a1947/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 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/validator.v2 v2.0.0-20160201165114-3e4f037f12a1 h1:1IZMbdoz1SZAQ4HMRwAP0FPSyXt7ywsiJ4q7OPTEu4A= @@ -1233,16 +1417,20 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +honnef.co/go/netdb v0.0.0-20150201073656-a416d700ae39/go.mod h1:rbNo0ST5hSazCG4rGfpHrwnwvzP1QX62WbhzD+ghGzs= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/api v0.17.3/go.mod h1:YZ0OTkuw7ipbe305fMpIdf3GLXZKRigjtZaV5gzC2J0= -k8s.io/apimachinery v0.17.3 h1:f+uZV6rm4/tHE7xXgLyToprg6xWairaClGVkm2t8omg= +k8s.io/apimachinery v0.17.1/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= k8s.io/apimachinery v0.17.3/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g= k8s.io/client-go v0.17.3/go.mod h1:cLXlTMtWHkuK4tD360KpWz2gG2KtdWEr/OT02i3emRQ= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= @@ -1252,8 +1440,16 @@ k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +modernc.org/httpfs v1.0.0/go.mod h1:BSkfoMUcahSijQD5J/Vu4UMOxzmEf5SNRwyXC4PJBEw= +modernc.org/libc v1.3.1/go.mod h1:f8sp9GAfEyGYh3lsRIKtBh/XwACdFvGznxm6GJmQvXk= +modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/memory v1.0.1/go.mod h1:NSjvC08+g3MLOpcAxQbdctcThAEX4YlJ20WWHYEhvRg= +modernc.org/sqlite v1.7.4/go.mod h1:xse4RHCm8Fzw0COf5SJqAyiDrVeDwAQthAS1V/woNIA= +modernc.org/tcl v1.4.1/go.mod h1:8YCvzidU9SIwkz7RZwlCWK61mhV8X9UwfkRDRp7y5e0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/sql/README.md b/sql/README.md new file mode 100644 index 00000000..1aed59d0 --- /dev/null +++ b/sql/README.md @@ -0,0 +1,20 @@ +## sql 的维护 + +- n9e_{module}.sql 完整的sql +- n9e_{module}-path.sql 增量的sql + +## sql 的版本发布 +在使用 git tag 之前,将特定版本的增量文件固化下来 + +``` +module=rdb +version=v3.3.3 +cat n9e_${module}-patch.sql > upgrade/n9e_${module}-${version}.sql +echo > n9e_{module}-patch.sql + +# 然后提交更改后,再打上版本的tag +git add . +git commit -a -m "${version} release" +git tag ${version} +git push +``` diff --git a/sql/n9e_hbs.sql b/sql/n9e_hbs.sql index 1d572a0a..928f9049 100644 --- a/sql/n9e_hbs.sql +++ b/sql/n9e_hbs.sql @@ -10,7 +10,8 @@ create table `instance` ( `identity` varchar(255) not null, `rpc_port` varchar(16) not null, `http_port` varchar(16) not null, - `remark` text, + `region` varchar(32) not null, + `remark` text, `ts` int unsigned not null, primary key (`id`), key(`module`,`identity`,`rpc_port`,`http_port`) @@ -26,4 +27,4 @@ create table `detector` ( `ts` int unsigned not null, primary key (`id`), key(`ip`,`port`) -) engine=innodb default charset=utf8; \ No newline at end of file +) engine=innodb default charset=utf8; diff --git a/sql/n9e_mon-patch.sql b/sql/n9e_mon-patch.sql new file mode 100644 index 00000000..c10db7c2 --- /dev/null +++ b/sql/n9e_mon-patch.sql @@ -0,0 +1,25 @@ +set names utf8; +use n9e_mon; + +CREATE TABLE `collect_rule` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id', + `nid` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT 'nid', + `step` int(11) NOT NULL DEFAULT '0' COMMENT 'step', + `timeout` int(11) NOT NULL DEFAULT '0' COMMENT 'total timeout', + `collect_type` varchar(64) NOT NULL DEFAULT '' COMMENT 'collector name', + `name` varchar(255) NOT NULL DEFAULT '' COMMENT 'name', + `region` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'region', + `comment` varchar(512) NOT NULL DEFAULT '' COMMENT 'comment', + `data` blob NULL COMMENT 'data', + `tags` varchar(512) NOT NULL DEFAULT '' COMMENT 'tags', + `creator` varchar(64) NOT NULL DEFAULT '' COMMENT 'creator', + `last_updator` varchar(64) NOT NULL DEFAULT '' COMMENT 'last_updator', + `created` datetime NOT NULL COMMENT 'created', + `last_updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_nid` (`nid`), + KEY `idx_collect_type` (`collect_type`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT 'api collect'; + + + diff --git a/sql/n9e_mon.sql b/sql/n9e_mon.sql index 5029a1a7..7586fe49 100644 --- a/sql/n9e_mon.sql +++ b/sql/n9e_mon.sql @@ -11,7 +11,7 @@ create table `maskconf` ( `metric` varchar(255) not null, `tags` varchar(255) not null default '', `cause` varchar(255) not null default '', - `user` varchar(32) not null default 'operate user', + `user` varchar(64) not null default 'operate user', `btime` bigint not null default 0 comment 'begin time', `etime` bigint not null default 0 comment 'end time', primary key (`id`), @@ -176,7 +176,7 @@ CREATE TABLE `stra_log` ( `sid` bigint(20) NOT NULL DEFAULT '0' COMMENT 'collect id', `action` varchar(255) NOT NULL DEFAULT '' COMMENT '动作 update, delete', `body` text COMMENT '修改之前采集的内容', - `creator` varchar(255) NOT NULL DEFAULT '' COMMENT 'creator', + `creator` varchar(64) NOT NULL DEFAULT '' COMMENT 'creator', `created` timestamp NOT NULL DEFAULT '1971-01-01 00:00:00' COMMENT 'created', PRIMARY KEY (`id`), KEY `idx_sid` (`sid`) @@ -192,9 +192,9 @@ CREATE TABLE `port_collect` ( `step` int(11) NOT NULL DEFAULT '0' COMMENT '采集周期', `timeout` int(11) NOT NULL DEFAULT '0' COMMENT 'connect time', `comment` varchar(512) NOT NULL DEFAULT '' COMMENT 'comment', - `creator` varchar(255) NOT NULL DEFAULT '' COMMENT 'creator', + `creator` varchar(64) NOT NULL DEFAULT '' COMMENT 'creator', `created` datetime NOT NULL COMMENT 'created', - `last_updator` varchar(128) NOT NULL DEFAULT '' COMMENT 'last_updator', + `last_updator` varchar(64) NOT NULL DEFAULT '' COMMENT 'last_updator', `last_updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'last_updated', PRIMARY KEY (`id`), KEY `idx_nid` (`nid`), @@ -211,9 +211,9 @@ CREATE TABLE `proc_collect` ( `target` varchar(255) NOT NULL DEFAULT '' COMMENT '采集对象', `step` int(11) NOT NULL DEFAULT '0' COMMENT '采集周期', `comment` varchar(512) NOT NULL DEFAULT '' COMMENT 'comment', - `creator` varchar(255) NOT NULL DEFAULT '' COMMENT 'creator', + `creator` varchar(64) NOT NULL DEFAULT '' COMMENT 'creator', `created` datetime NOT NULL COMMENT 'created', - `last_updator` varchar(128) NOT NULL DEFAULT '' COMMENT 'last_updator', + `last_updator` varchar(64) NOT NULL DEFAULT '' COMMENT 'last_updator', `last_updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_nid` (`nid`), @@ -237,9 +237,9 @@ CREATE TABLE `log_collect` ( `unit` varchar(64) NOT NULL DEFAULT '' COMMENT 'unit', `zero_fill` tinyint(4) NOT NULL DEFAULT '0' COMMENT 'zero fill', `comment` varchar(512) NOT NULL DEFAULT '' COMMENT 'comment', - `creator` varchar(255) NOT NULL DEFAULT '' COMMENT 'creator', + `creator` varchar(64) NOT NULL DEFAULT '' COMMENT 'creator', `created` datetime NOT NULL COMMENT 'created', - `last_updator` varchar(128) NOT NULL DEFAULT '' COMMENT 'last_updator', + `last_updator` varchar(64) NOT NULL DEFAULT '' COMMENT 'last_updator', `last_updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_nid` (`nid`), @@ -257,9 +257,9 @@ CREATE TABLE `plugin_collect` ( `stdin` text NOT NULL COMMENT 'stdin', `env` text NOT NULL COMMENT 'env', `comment` varchar(512) NOT NULL DEFAULT '' COMMENT 'comment', - `creator` varchar(255) NOT NULL DEFAULT '' COMMENT 'creator', + `creator` varchar(64) NOT NULL DEFAULT '' COMMENT 'creator', `created` datetime NOT NULL COMMENT 'created', - `last_updator` varchar(128) NOT NULL DEFAULT '' COMMENT 'last_updator', + `last_updator` varchar(64) NOT NULL DEFAULT '' COMMENT 'last_updator', `last_updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_nid` (`nid`), @@ -286,9 +286,9 @@ CREATE TABLE `api_collect` ( `unexpected_string` varchar(255) NOT NULL DEFAULT '' COMMENT 'unexpected_string', `region` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'region', `comment` varchar(512) NOT NULL DEFAULT '' COMMENT 'comment', - `creator` varchar(255) NOT NULL DEFAULT '' COMMENT 'creator', + `creator` varchar(64) NOT NULL DEFAULT '' COMMENT 'creator', `created` datetime NOT NULL COMMENT 'created', - `last_updator` varchar(128) NOT NULL DEFAULT '' COMMENT 'last_updator', + `last_updator` varchar(64) NOT NULL DEFAULT '' COMMENT 'last_updator', `last_updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_nid` (`nid`), @@ -309,9 +309,9 @@ CREATE TABLE `snmp_collect` ( `step` int(11) NOT NULL DEFAULT '0' COMMENT 'step', `timeout` int(11) NOT NULL DEFAULT '0' COMMENT 'total timeout', `comment` varchar(512) NOT NULL DEFAULT '' COMMENT 'comment', - `creator` varchar(255) NOT NULL DEFAULT '' COMMENT 'creator', + `creator` varchar(64) NOT NULL DEFAULT '' COMMENT 'creator', `created` datetime NOT NULL COMMENT 'created', - `last_updator` varchar(128) NOT NULL DEFAULT '' COMMENT 'last_updator', + `last_updator` varchar(64) NOT NULL DEFAULT '' COMMENT 'last_updator', `last_updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_nid` (`nid`), @@ -319,6 +319,26 @@ CREATE TABLE `snmp_collect` ( KEY `idx_collect_type` (`collect_type`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT 'api collect'; +CREATE TABLE `collect_rule` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id', + `nid` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT 'nid', + `step` int(11) NOT NULL DEFAULT '0' COMMENT 'step', + `timeout` int(11) NOT NULL DEFAULT '0' COMMENT 'total timeout', + `collect_type` varchar(64) NOT NULL DEFAULT '' COMMENT 'collector name', + `name` varchar(255) NOT NULL DEFAULT '' COMMENT 'name', + `region` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'region', + `comment` varchar(512) NOT NULL DEFAULT '' COMMENT 'comment', + `data` blob NULL COMMENT 'data', + `tags` varchar(512) NOT NULL DEFAULT '' COMMENT 'tags', + `creator` varchar(64) NOT NULL DEFAULT '' COMMENT 'creator', + `last_updator` varchar(64) NOT NULL DEFAULT '' COMMENT 'last_updator', + `created` datetime NOT NULL COMMENT 'created', + `last_updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_nid` (`nid`), + KEY `idx_collect_type` (`collect_type`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT 'api collect'; + CREATE TABLE `aggr_calc` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id', `nid` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT 'nid', @@ -389,12 +409,3 @@ CREATE TABLE `collect_hist` ( PRIMARY KEY (`id`), KEY `idx_cid` (`cid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT 'hist'; - - -CREATE TABLE `api_collect_sid` ( - `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id', - `sid` bigint(20) NOT NULL DEFAULT '0' COMMENT 'stra id', - `cid` bigint(20) NOT NULL DEFAULT '0' COMMENT 'collect id', - PRIMARY KEY (`id`), - KEY (`sid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/sql/n9e_rdb-patch.sql b/sql/n9e_rdb-patch.sql new file mode 100644 index 00000000..34ac2577 --- /dev/null +++ b/sql/n9e_rdb-patch.sql @@ -0,0 +1,42 @@ +set names utf8; +use n9e_rdb; + +CREATE TABLE `white_list` ( + `id` bigint unsigned not null AUTO_INCREMENT, + `start_ip` varchar(32) DEFAULT '0' NOT NULL, + `end_ip` varchar(32) DEFAULT '0' NOT NULL, + `start_ip_int` bigint DEFAULT '0' NOT NULL, + `end_ip_int` bigint DEFAULT '0' NOT NULL, + `start_time` bigint DEFAULT '0' NOT NULL, + `end_time` bigint DEFAULT '0' NOT NULL, + `created_at` bigint DEFAULT '0' NOT NULL, + `updated_at` bigint DEFAULT '0' NOT NULL, + `creator` varchar(64) DEFAULT '' NOT NULL, + `updater` varchar(64) DEFAULT '' NOT NULL, + PRIMARY KEY (`id`), + KEY (`start_ip_int`, `end_ip_int`), + KEY (`start_time`, `end_time`), + KEY (`created_at`) +) ENGINE = InnoDB DEFAULT CHARSET = utf8; + +CREATE TABLE `session` ( + `sid` char(128) NOT NULL, + `username` varchar(64) DEFAULT '', + `remote_addr` varchar(32) DEFAULT '', + `created_at` integer unsigned DEFAULT '0', + `updated_at` integer unsigned DEFAULT '0' NOT NULL, + PRIMARY KEY (`sid`), + KEY (`username`), + KEY (`updated_at`) +) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8; + +alter table user add `login_err_num` int unsigned not null default 0 after leader_name; + +alter table user add `active_begin` bigint not null default 0 after login_err_num; +alter table user add `active_end` bigint not null default 0 after active_begin; +alter table user add `locked_at` bigint not null default 0 after active_end; +alter table user add `updated_at` bigint not null default 0 after locked_at; +alter table user add `pwd_updated_at` bigint not null default 0 after updated_at; +alter table user add `logged_at` bigint not null default 0 after pwd_updated_at; +alter table user add `passwords` varchar(512) not null default '' after password; +alter table login_log add `err` varchar(128) not null default '' after loginout; diff --git a/sql/n9e_rdb.sql b/sql/n9e_rdb.sql index c5ca52e3..4d3adf0a 100644 --- a/sql/n9e_rdb.sql +++ b/sql/n9e_rdb.sql @@ -6,23 +6,31 @@ use n9e_rdb; CREATE TABLE `user` ( - `id` int unsigned not null AUTO_INCREMENT, - `uuid` varchar(128) not null comment 'use in cookie', - `username` varchar(64) not null comment 'login name, cannot rename', - `password` varchar(128) not null default '', - `dispname` varchar(32) not null default '' comment 'display name, chinese name', - `phone` varchar(16) not null default '', - `email` varchar(64) not null default '', - `im` varchar(64) not null default '', - `portrait` varchar(2048) not null default '', - `intro` varchar(2048) not null default '', - `organization` varchar(255) not null default '', - `typ` tinyint(1) not null default 0 comment '0: long-term account; 1: temporary account', - `status` tinyint(1) not null default 0 comment '0: active; 1: inactive 2: disable', - `is_root` tinyint(1) not null, - `leader_id` int unsigned not null default 0, - `leader_name` varchar(32) not null default '', - `create_at` timestamp not null default CURRENT_TIMESTAMP, + `id` int unsigned not null AUTO_INCREMENT, + `uuid` varchar(128) not null comment 'use in cookie', + `username` varchar(64) not null comment 'login name, cannot rename', + `password` varchar(128) not null default '', + `passwords` varchar(512) not null default '', + `dispname` varchar(32) not null default '' comment 'display name, chinese name', + `phone` varchar(16) not null default '', + `email` varchar(64) not null default '', + `im` varchar(64) not null default '', + `portrait` varchar(2048) not null default '', + `intro` varchar(2048) not null default '', + `organization` varchar(255) not null default '', + `typ` tinyint(1) not null default 0 comment '0: long-term account; 1: temporary account', + `status` tinyint(1) not null default 0 comment '0: active, 1: inactive, 2: locked, 3: frozen, 5: writen-off', + `is_root` tinyint(1) not null, + `leader_id` int unsigned not null default 0, + `leader_name` varchar(32) not null default '', + `login_err_num` int unsigned not null default 0, + `active_begin` bigint not null default 0, + `active_end` bigint not null default 0, + `locked_at` bigint not null default 0, + `updated_at` bigint not null default 0, + `pwd_updated_at` bigint not null default 0, + `logged_at` bigint not null default 0, + `create_at` timestamp not null default CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY (`username`), UNIQUE KEY (`uuid`) @@ -269,6 +277,7 @@ CREATE TABLE `login_log` `client` varchar(128) not null comment 'client ip', `clock` bigint not null comment 'login timestamp', `loginout` char(3) not null comment 'in or out', + `err` varchar(128) not null comment 'err msg', PRIMARY KEY (`id`), KEY (`username`), KEY (`clock`) @@ -315,3 +324,32 @@ CREATE TABLE `captcha` ( KEY (`captcha_id`, `answer`), KEY (`created_at`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8; + +CREATE TABLE `white_list` ( + `id` bigint unsigned not null AUTO_INCREMENT, + `start_ip` varchar(32) DEFAULT '0' NOT NULL, + `end_ip` varchar(32) DEFAULT '0' NOT NULL, + `start_ip_int` bigint DEFAULT '0' NOT NULL, + `end_ip_int` bigint DEFAULT '0' NOT NULL, + `start_time` bigint DEFAULT '0' NOT NULL, + `end_time` bigint DEFAULT '0' NOT NULL, + `created_at` bigint DEFAULT '0' NOT NULL, + `updated_at` bigint DEFAULT '0' NOT NULL, + `creator` varchar(64) DEFAULT '' NOT NULL, + `updater` varchar(64) DEFAULT '' NOT NULL, + PRIMARY KEY (`id`), + KEY (`start_ip_int`, `end_ip_int`), + KEY (`start_time`, `end_time`), + KEY (`created_at`) +) ENGINE = InnoDB DEFAULT CHARSET = utf8; + +CREATE TABLE `session` ( + `sid` char(128) NOT NULL, + `username` varchar(64) DEFAULT '', + `remote_addr` varchar(32) DEFAULT '', + `created_at` integer unsigned DEFAULT '0', + `updated_at` integer unsigned DEFAULT '0' NOT NULL, + PRIMARY KEY (`sid`), + KEY (`username`), + KEY (`updated_at`) +) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8; diff --git a/sql/n9e_ams_3.1.6.sql b/sql/upgrade/n9e_ams-v3.1.6.sql similarity index 100% rename from sql/n9e_ams_3.1.6.sql rename to sql/upgrade/n9e_ams-v3.1.6.sql diff --git a/sql/n9e_rdb_3.3.0.sql b/sql/upgrade/n9e_rdb-v3.3.0.sql similarity index 100% rename from sql/n9e_rdb_3.3.0.sql rename to sql/upgrade/n9e_rdb-v3.3.0.sql diff --git a/src/common/dataobj/metric.go b/src/common/dataobj/metric.go index a04a20c5..05ae460b 100644 --- a/src/common/dataobj/metric.go +++ b/src/common/dataobj/metric.go @@ -30,9 +30,9 @@ type MetricValue struct { Step int64 `json:"step"` ValueUntyped interface{} `json:"value"` Value float64 `json:"-"` - CounterType string `json:"counterType"` - Tags string `json:"tags"` - TagsMap map[string]string `json:"tagsMap"` //保留2种格式,方便后端组件使用 + CounterType string `json:"counterType"` // GAUGE | COUNTER | SUBTRACT | DERIVE + Tags string `json:"tags"` // a=1,b=2,c=3 + TagsMap map[string]string `json:"tagsMap"` // {"a":1, "b"=2, "c="3} 保留2种格式,方便后端组件使用 Extra string `json:"extra"` } @@ -69,7 +69,7 @@ func (m *MetricValue) CheckValidity(now int64) (err error) { } if m.Nid == "" && m.Endpoint == "" { - err = fmt.Errorf("nid or endpoint should not be empty") + err = fmt.Errorf("nid and endpoint should not be empty") return } @@ -133,7 +133,7 @@ func (m *MetricValue) CheckValidity(now int64) (err error) { k = filterString(k) v = filterString(v) if len(k) == 0 || len(v) == 0 { - err = fmt.Errorf("tag key and value should not be empty") + err = fmt.Errorf("tag key and value should not be empty key:%s value:%s", k, v) return } diff --git a/src/common/dataobj/query_item.go b/src/common/dataobj/query_item.go index 5347cbdd..2e477c47 100644 --- a/src/common/dataobj/query_item.go +++ b/src/common/dataobj/query_item.go @@ -110,4 +110,5 @@ type IndexByFullTagsResp struct { Tags []string `json:"tags"` Step int `json:"step"` DsType string `json:"dstype"` + Count int `json:"count"` } diff --git a/src/models/configs.go b/src/models/configs.go index 31631689..7573244d 100644 --- a/src/models/configs.go +++ b/src/models/configs.go @@ -1,9 +1,11 @@ package models import ( + "encoding/json" "fmt" "log" "os" + "strings" "time" "github.com/toolkits/pkg/runner" @@ -88,3 +90,103 @@ func ConfigsGets(ckeys []string) (map[string]string, error) { return kvmap, nil } + +type AuthConfig struct { + MaxNumErr int `json:"maxNumErr"` + MaxSessionNumber int64 `json:"maxSessionNumber"` + MaxConnIdelTime int64 `json:"maxConnIdelTime" description:"minute"` + LockTime int64 `json:"lockTime" description:"minute"` + PwdHistorySize int `json:"pwdHistorySize"` + PwdMinLenght int `json:"pwdMinLenght"` + PwdExpiresIn int64 `json:"pwdExpiresIn" description:"month"` + PwdMustInclude []string `json:"pwdMustInclude" description:"upper,lower,number,specChar"` + PwdMustIncludeFlag int `json:"pwdMustIncludeFlag"` +} + +func (p AuthConfig) PwdRules() []string { + s := []string{} + if p.PwdMinLenght > 0 { + s = append(s, _s("Minimum password length %d", p.PwdMinLenght)) + } + if rule := p.MustInclude(); rule != "" { + s = append(s, rule) + } + return s +} + +func (p AuthConfig) MustInclude() string { + s := []string{} + if p.PwdMustIncludeFlag&PWD_INCLUDE_UPPER > 0 { + s = append(s, _s("Upper char")) + } + if p.PwdMustIncludeFlag&PWD_INCLUDE_LOWER > 0 { + s = append(s, _s("Lower char")) + } + if p.PwdMustIncludeFlag&PWD_INCLUDE_NUMBER > 0 { + s = append(s, _s("Number")) + } + if p.PwdMustIncludeFlag&PWD_INCLUDE_SPEC_CHAR > 0 { + s = append(s, _s("Special char")) + } + + if len(s) > 0 { + return _s("Must include %s", strings.Join(s, ",")) + } + + return "" +} + +const ( + PWD_INCLUDE_UPPER = 1 << iota + PWD_INCLUDE_LOWER + PWD_INCLUDE_NUMBER + PWD_INCLUDE_SPEC_CHAR +) + +func (p *AuthConfig) Validate() error { + for _, v := range p.PwdMustInclude { + switch v { + case "upper": + p.PwdMustIncludeFlag |= PWD_INCLUDE_UPPER + case "lower": + p.PwdMustIncludeFlag |= PWD_INCLUDE_LOWER + case "number": + p.PwdMustIncludeFlag |= PWD_INCLUDE_NUMBER + case "specChar": + p.PwdMustIncludeFlag |= PWD_INCLUDE_SPEC_CHAR + default: + return fmt.Errorf("invalid pwd flags, must be 'upper', 'lower', 'number', 'specChar'") + } + } + + return nil +} + +var DefaultAuthConfig = AuthConfig{ + MaxConnIdelTime: 30, + PwdMustInclude: []string{}, +} + +func AuthConfigGet() (*AuthConfig, error) { + buf, err := ConfigsGet("auth.config") + if err != nil { + return &DefaultAuthConfig, nil + } + c := &AuthConfig{} + if err := json.Unmarshal([]byte(buf), c); err != nil { + return &DefaultAuthConfig, nil + } + return c, nil +} + +func AuthConfigSet(config *AuthConfig) error { + if err := config.Validate(); err != nil { + return err + } + + buf, err := json.Marshal(config) + if err != nil { + return err + } + return ConfigsSet("auth.config", string(buf)) +} diff --git a/src/models/init.go b/src/models/init.go index ed5efef2..be7c041f 100644 --- a/src/models/init.go +++ b/src/models/init.go @@ -1,11 +1,21 @@ package models import ( + "fmt" "time" + "github.com/didi/nightingale/src/toolkits/i18n" "github.com/toolkits/pkg/cache" ) func init() { cache.InitMemoryCache(time.Hour) } + +func _e(format string, a ...interface{}) error { + return fmt.Errorf(i18n.Sprintf(format, a...)) +} + +func _s(format string, a ...interface{}) string { + return i18n.Sprintf(format, a...) +} diff --git a/src/models/invite.go b/src/models/invite.go index 2fd432fe..a2569875 100644 --- a/src/models/invite.go +++ b/src/models/invite.go @@ -35,3 +35,8 @@ func InviteNew(token, creator string) error { _, err := DB["rdb"].Insert(obj) return err } + +func (i *Invite) Del() error { + _, err := DB["rdb"].Where("token=?", i.Token).Delete(i) + return err +} diff --git a/src/models/login_log.go b/src/models/login_log.go index bb480e3c..5294f245 100644 --- a/src/models/login_log.go +++ b/src/models/login_log.go @@ -1,6 +1,9 @@ package models -import "time" +import ( + "fmt" + "time" +) type LoginLog struct { Id int64 `json:"id"` @@ -8,18 +11,20 @@ type LoginLog struct { Client string `json:"client"` Clock int64 `json:"clock"` Loginout string `json:"loginout"` + Err string `json:"err"` } -func LoginLogNew(username, client, inout string) error { +func LoginLogNew(username, client, inout string, err error) error { now := time.Now().Unix() obj := LoginLog{ Username: username, Client: client, Clock: now, Loginout: inout, + Err: fmt.Sprintf("%v", err), } - _, err := DB["rdb"].Insert(obj) - return err + _, e := DB["rdb"].Insert(obj) + return e } func LoginLogTotal(username string, btime, etime int64) (int64, error) { diff --git a/src/models/mon_collect.go b/src/models/mon_collect.go index 3b3ae7ad..3fead87f 100644 --- a/src/models/mon_collect.go +++ b/src/models/mon_collect.go @@ -185,12 +185,6 @@ type LogCollect struct { ParseSucc bool `xorm:"-" json:"-"` } -type ApiCollectSid struct { - Id int64 `json:"id"` - Cid int64 `json:"cid"` - Sid int64 `json:"sid"` -} - type ApiCollect struct { Id int64 `json:"id"` Nid int64 `json:"nid"` @@ -531,58 +525,6 @@ func (a *ApiCollect) Update() error { return err } -func DeleteApiCollect(id int64) error { - session := DB["mon"].NewSession() - defer session.Close() - - _, err := session.Where("id = ?", id).Delete(new(ApiCollect)) - if err != nil { - session.Rollback() - return err - } - - var relCidSid ApiCollectSid - has, err := session.Where("cid = ?", id).Get(&relCidSid) - if err != nil { - session.Rollback() - return err - } - if has { - err = StraDel(relCidSid.Sid) - if err != nil { - session.Rollback() - return err - } - } - - return session.Commit() -} - -func GetSidByCid(cid int64) (int64, error) { - var cidSid ApiCollectSid - _, err := DB["mon"].Where("cid = ?", cid).Get(&cidSid) - return cidSid.Sid, err -} - -func (a *ApiCollectSid) Add() error { - session := DB["mon"].NewSession() - defer session.Close() - - _, err := session.Where("cid = ?", a.Cid).Delete(new(ApiCollectSid)) - if err != nil { - session.Rollback() - return err - } - - _, err = session.Insert(a) - if err != nil { - session.Rollback() - return err - } - - return session.Commit() -} - func CreateCollect(collectType, creator string, collect interface{}) error { session := DB["mon"].NewSession() defer session.Close() @@ -611,126 +553,6 @@ func CreateCollect(collectType, creator string, collect interface{}) error { return session.Commit() } -func GetCollectByNid(collectType string, nids []int64) ([]interface{}, error) { - var res []interface{} - switch collectType { - case "port": - collects := []PortCollect{} - err := DB["mon"].In("nid", nids).Find(&collects) - for _, c := range collects { - res = append(res, c) - } - return res, err - - case "proc": - collects := []ProcCollect{} - err := DB["mon"].In("nid", nids).Find(&collects) - for _, c := range collects { - res = append(res, c) - } - return res, err - - case "log": - collects := []LogCollect{} - err := DB["mon"].In("nid", nids).Find(&collects) - for _, c := range collects { - c.Decode() - res = append(res, c) - } - return res, err - - case "plugin": - collects := []PluginCollect{} - err := DB["mon"].In("nid", nids).Find(&collects) - for _, c := range collects { - res = append(res, c) - } - return res, err - - default: - return nil, fmt.Errorf("采集类型不合法") - } - -} - -func GetCollectById(collectType string, cid int64) (interface{}, error) { - switch collectType { - case "port": - collect := new(PortCollect) - has, err := DB["mon"].Where("id = ?", cid).Get(collect) - if !has { - return nil, err - } - return collect, err - case "proc": - collect := new(ProcCollect) - has, err := DB["mon"].Where("id = ?", cid).Get(collect) - if !has { - return nil, err - } - return collect, err - case "log": - collect := new(LogCollect) - has, err := DB["mon"].Where("id = ?", cid).Get(collect) - if !has { - return nil, err - } - collect.Decode() - return collect, err - case "plugin": - collect := new(PluginCollect) - has, err := DB["mon"].Where("id = ?", cid).Get(collect) - if !has { - return nil, err - } - return collect, err - - default: - return nil, fmt.Errorf("采集类型不合法") - } - - return nil, nil -} - -func GetCollectByNameAndNid(collectType string, name string, nid int64) (interface{}, error) { - switch collectType { - case "port": - collect := new(PortCollect) - has, err := DB["mon"].Where("name = ? and nid = ?", name, nid).Get(collect) - if !has { - return nil, err - } - return collect, err - case "proc": - collect := new(ProcCollect) - has, err := DB["mon"].Where("name = ? and nid = ?", name, nid).Get(collect) - if !has { - return nil, err - } - return collect, err - case "log": - collect := new(LogCollect) - has, err := DB["mon"].Where("name = ? and nid = ?", name, nid).Get(collect) - if !has { - return nil, err - } - collect.Decode() - return collect, err - case "plugin": - collect := new(PluginCollect) - has, err := DB["mon"].Where("name = ? and nid = ?", name, nid).Get(collect) - if !has { - return nil, err - } - return collect, err - - default: - return nil, fmt.Errorf("采集类型不合法") - } - - return nil, nil -} - func DeleteCollectById(collectType, creator string, cid int64) error { session := DB["mon"].NewSession() defer session.Close() diff --git a/src/models/mon_collect_rule.go b/src/models/mon_collect_rule.go new file mode 100644 index 00000000..f3a75783 --- /dev/null +++ b/src/models/mon_collect_rule.go @@ -0,0 +1,104 @@ +package models + +import ( + "encoding/json" + "fmt" + "time" + + "github.com/didi/nightingale/src/common/dataobj" +) + +const ( + defaultStep = 10 +) + +type CollectRule struct { + Id int64 `json:"id"` + Nid int64 `json:"nid"` + Step int `json:"step" description:"interval"` + Timeout int `json:"timeout"` + CollectType string `json:"collect_type" description:"plugin name"` + Name string `json:"name"` + Region string `json:"region"` + Comment string `json:"comment"` + Data json.RawMessage `json:"data"` + Tags string `json:"tags" description:"k1=v1,k2=v2,k3=v3,..."` + Creator string `json:"creator" description:"just for output"` + LastUpdator string `xorm:"last_updator" json:"last_updator" description:"just for output"` + Created time.Time `xorm:"updated" json:"created" description:"just for output"` + LastUpdated time.Time `xorm:"updated" json:"last_updated" description:"just for output"` +} + +type validator interface { + Validate() error +} + +func (p *CollectRule) Validate(v ...interface{}) error { + if p.Name == "" { + return fmt.Errorf("invalid collectRule.name") + } + + if p.Step == 0 { + p.Step = defaultStep + } + + if _, err := dataobj.SplitTagsString(p.Tags); err != nil { + return err + } + + if len(v) > 0 && v[0] != nil { + if err := json.Unmarshal(p.Data, v[0]); err != nil { + return err + } + if o, ok := v[0].(validator); ok { + if err := o.Validate(); err != nil { + return err + } + } + } + + return nil +} + +func GetCollectRules() ([]*CollectRule, error) { + rules := []*CollectRule{} + err := DB["mon"].Find(&rules) + return rules, err +} + +func (p *CollectRule) Update() error { + session := DB["mon"].NewSession() + defer session.Close() + + err := session.Begin() + if err != nil { + return err + } + + if _, err = session.Id(p.Id).AllCols().Update(p); err != nil { + session.Rollback() + return err + } + + b, err := json.Marshal(p) + if err != nil { + session.Rollback() + return err + } + + if err := saveHist(p.Id, p.CollectType, "update", p.Creator, string(b), session); err != nil { + session.Rollback() + return err + } + + if err = session.Commit(); err != nil { + return err + } + + return err +} + +func DeleteCollectRule(sid int64) error { + _, err := DB["mon"].Where("id=?", sid).Delete(new(CollectRule)) + return err +} diff --git a/src/models/mon_hbs.go b/src/models/mon_hbs.go index 540533f2..c8b177a0 100644 --- a/src/models/mon_hbs.go +++ b/src/models/mon_hbs.go @@ -10,6 +10,7 @@ type Instance struct { HTTPPort string `json:"http_port" xorm:"http_port"` TS int64 `json:"ts" xorm:"ts"` Remark string `json:"remark"` + Region string `json:"region"` Active bool `xorm:"-" json:"active"` } diff --git a/src/models/session.go b/src/models/session.go new file mode 100644 index 00000000..e62fac2c --- /dev/null +++ b/src/models/session.go @@ -0,0 +1,112 @@ +package models + +import ( + "fmt" + "time" + + "github.com/toolkits/pkg/cache" + "github.com/toolkits/pkg/logger" +) + +type Session struct { + Sid string `json:"sid"` + Username string `json:"username"` + RemoteAddr string `json:"remote_addr"` + CreatedAt int64 `json:"created_at"` + UpdatedAt int64 `json:"updated_at"` +} + +func SessionAll() (int64, error) { + return DB["rdb"].Count(new(Session)) +} + +func SessionUserAll(username string) (int64, error) { + return DB["rdb"].Where("username=?", username).Count(new(Session)) +} + +func SessionGet(sid string) (*Session, error) { + var obj Session + has, err := DB["rdb"].Where("sid=?", sid).Get(&obj) + if err != nil { + return nil, fmt.Errorf("get session err %s", err) + } + if !has { + return nil, fmt.Errorf("not found") + } + + return &obj, nil +} + +func SessionInsert(in *Session) error { + _, err := DB["rdb"].Insert(in) + return err +} + +func SessionDel(sid string) error { + _, err := DB["rdb"].Where("sid=?", sid).Delete(new(Session)) + return err +} + +func SessionUpdate(in *Session) error { + _, err := DB["rdb"].Where("sid=?", in.Sid).AllCols().Update(in) + return err +} + +func SessionCleanup(ts int64) error { + n, err := DB["rdb"].Where("updated_at" + raw), nil diff --git a/src/models/user.go b/src/models/user.go index d70f2ede..a4206af6 100644 --- a/src/models/user.go +++ b/src/models/user.go @@ -21,17 +21,30 @@ import ( const ( LOGIN_T_SMS = "sms-code" LOGIN_T_EMAIL = "email-code" - LOGIN_T_RST = "rst-code" LOGIN_T_PWD = "password" LOGIN_T_LDAP = "ldap" + LOGIN_T_RST = "rst-code" + LOGIN_T_LOGIN = "login-code" LOGIN_EXPIRES_IN = 300 ) +const ( + USER_S_ACTIVE = iota + USER_S_INACTIVE + USER_S_LOCKED + USER_S_FROZEN + USER_S_WRITEN_OFF +) +const ( + USER_T_NATIVE = iota + USER_T_TEMP +) type User struct { Id int64 `json:"id"` UUID string `json:"uuid" xorm:"'uuid'"` Username string `json:"username"` Password string `json:"-"` + Passwords string `json:"-"` Dispname string `json:"dispname"` Phone string `json:"phone"` Email string `json:"email"` @@ -39,14 +52,63 @@ type User struct { Portrait string `json:"portrait"` Intro string `json:"intro"` Organization string `json:"organization"` - Typ int `json:"typ"` - Status int `json:"status"` + Type int `json:"type" xorm:"'typ'" description:"0: long-term account; 1: temporary account"` + Status int `json:"status" description:"0: active, 1: inactive, 2: locked, 3: frozen, 4: writen-off"` IsRoot int `json:"is_root"` LeaderId int64 `json:"leader_id"` LeaderName string `json:"leader_name"` + LoginErrNum int `json:"login_err_num"` + ActiveBegin int64 `json:"active_begin" description:"for temporary account"` + ActiveEnd int64 `json:"active_end" description:"for temporary account"` + LockedAt int64 `json:"locked_at" description:"locked time"` + UpdatedAt int64 `json:"updated_at" description:"user info change time"` + PwdUpdatedAt int64 `json:"pwd_updated_at" description:"password change time"` + LoggedAt int64 `json:"logged_at" description:"last logged time"` CreateAt time.Time `json:"create_at" xorm:"<-"` } +func (u *User) Validate() error { + u.Username = strings.TrimSpace(u.Username) + if u.Username == "" { + return _e("username is blank") + } + + if str.Dangerous(u.Username) { + return _e("%s %s format error", _s("username"), u.Username) + } + + if str.Dangerous(u.Dispname) { + return _e("%s %s format error", _s("dispname"), u.Dispname) + } + + if u.Phone != "" && !str.IsPhone(u.Phone) { + return _e("%s %s format error", _s("phone"), u.Phone) + } + + if u.Email != "" && !str.IsMail(u.Email) { + return _e("%s %s format error", _s("email"), u.Email) + } + + if len(u.Username) > 32 { + return _e("username too long (max:%d)", 32) + } + + if len(u.Dispname) > 32 { + return _e("dispname too long (max:%d)", 32) + } + + if strings.ContainsAny(u.Im, "%'") { + return _e("%s %s format error", "im", u.Im) + } + + cnt, _ := DB["rdb"].Where("((email <> '' and email=?) or (phone <> '' and phone=?)) and username=?", + u.Email, u.Phone, u.Username).Count(u) + if cnt > 0 { + return _e("email %s or phone %s is exists", u.Email, u.Phone) + } + return nil +} + func (u *User) CopyLdapAttr(sr *ldap.SearchResult) { attrs := config.Config.LDAP.Attributes if attrs.Dispname != "" { @@ -95,59 +157,59 @@ func InitRooter() { log.Println("user root init done") } -func LdapLogin(user, pass string) (*User, error) { - sr, err := ldapReq(user, pass) +func LdapLogin(username, pass string) (*User, error) { + sr, err := ldapReq(username, pass) if err != nil { return nil, err } - var u User - has, err := DB["rdb"].Where("username=?", user).Get(&u) + var user User + has, err := DB["rdb"].Where("username=?", username).Get(&user) if err != nil { return nil, err } - u.CopyLdapAttr(sr) + user.CopyLdapAttr(sr) if has { if config.Config.LDAP.CoverAttributes { - _, err := DB["rdb"].Where("id=?", u.Id).Update(u) - return &u, err + _, err := DB["rdb"].Where("id=?", user.Id).Update(user) + return &user, err } else { - return &u, err + return &user, err } } - u.Username = user - u.Password = "******" - u.UUID = GenUUIDForUser(user) - _, err = DB["rdb"].Insert(u) - return &u, nil + user.Username = username + user.Password = "******" + user.UUID = GenUUIDForUser(username) + _, err = DB["rdb"].Insert(user) + return &user, nil } -func PassLogin(user, pass string) (*User, error) { - var u User - has, err := DB["rdb"].Where("username=?", user).Get(&u) +func PassLogin(username, pass string) (*User, error) { + var user User + has, err := DB["rdb"].Where("username=?", username).Get(&user) if err != nil { - return nil, err + return nil, _e("Login fail, check your username and password") } if !has { - logger.Infof("password auth fail, no such user: %s", user) - return nil, fmt.Errorf("login fail, check your username and password") + logger.Infof("password auth fail, no such user: %s", username) + return nil, _e("Login fail, check your username and password") } loginPass, err := CryptoPass(pass) if err != nil { - return nil, err + return &user, err } - if loginPass != u.Password { - logger.Infof("password auth fail, password error, user: %s", user) - return nil, fmt.Errorf("login fail, check your username and password") + if loginPass != user.Password { + logger.Infof("password auth fail, password error, user: %s", username) + return &user, _e("Login fail, check your username and password") } - return &u, nil + return &user, nil } func SmsCodeLogin(phone, code string) (*User, error) { @@ -156,15 +218,15 @@ func SmsCodeLogin(phone, code string) (*User, error) { return nil, fmt.Errorf("phone %s dose not exist", phone) } - lc, err := LoginCodeGet("username=? and code=? and login_type=?", user.Username, code, LOGIN_T_SMS) + lc, err := LoginCodeGet("username=? and code=? and login_type=?", user.Username, code, LOGIN_T_LOGIN) if err != nil { - logger.Infof("sms-code auth fail, user: %s", user.Username) - return nil, fmt.Errorf("login fail, check your sms-code") + logger.Debugf("sms-code auth fail, user: %s", user.Username) + return user, _e("The code is incorrect") } if time.Now().Unix()-lc.CreatedAt > LOGIN_EXPIRES_IN { - logger.Infof("sms-code auth expired, user: %s", user.Username) - return nil, fmt.Errorf("login fail, the code has expired") + logger.Debugf("sms-code auth expired, user: %s", user.Username) + return user, _e("The code has expired") } lc.Del() @@ -178,15 +240,15 @@ func EmailCodeLogin(email, code string) (*User, error) { return nil, fmt.Errorf("email %s dose not exist", email) } - lc, err := LoginCodeGet("username=? and code=? and login_type=?", user.Username, code, LOGIN_T_EMAIL) + lc, err := LoginCodeGet("username=? and code=? and login_type=?", user.Username, code, LOGIN_T_LOGIN) if err != nil { - logger.Infof("email-code auth fail, user: %s", user.Username) - return nil, fmt.Errorf("login fail, check your email-code") + logger.Debugf("email-code auth fail, user: %s", user.Username) + return user, _e("The code is incorrect") } if time.Now().Unix()-lc.CreatedAt > LOGIN_EXPIRES_IN { - logger.Infof("email-code auth expired, user: %s", user.Username) - return nil, fmt.Errorf("login fail, the code has expired") + logger.Debugf("email-code auth expired, user: %s", user.Username) + return user, _e("The code has expired") } lc.Del() @@ -208,56 +270,40 @@ func UserGet(where string, args ...interface{}) (*User, error) { return &obj, nil } +func UserMustGet(where string, args ...interface{}) (*User, error) { + var obj User + has, err := DB["rdb"].Where(where, args...).Get(&obj) + if err != nil { + return nil, err + } + + if !has { + return nil, _e("User dose not exist") + } + + return &obj, nil +} + func (u *User) IsRooter() bool { return u.IsRoot == 1 } -func (u *User) CheckFields() { - u.Username = strings.TrimSpace(u.Username) - if u.Username == "" { - errors.Bomb("username is blank") - } - - if str.Dangerous(u.Username) { - errors.Bomb("username is dangerous") - } - - if str.Dangerous(u.Dispname) { - errors.Bomb("dispname is dangerous") - } - - if u.Phone != "" && !str.IsPhone(u.Phone) { - errors.Bomb("%s format error", u.Phone) - } - - if u.Email != "" && !str.IsMail(u.Email) { - errors.Bomb("%s format error", u.Email) - } - - if len(u.Username) > 32 { - errors.Bomb("username too long") - } - - if len(u.Dispname) > 32 { - errors.Bomb("dispname too long") - } - - if strings.ContainsAny(u.Im, "%'") { - errors.Bomb("im invalid") - } -} - func (u *User) Update(cols ...string) error { - u.CheckFields() + if err := u.Validate(); err != nil { + return err + } + _, err := DB["rdb"].Where("id=?", u.Id).Cols(cols...).Update(u) return err } func (u *User) Save() error { - u.CheckFields() + if err := u.Validate(); err != nil { + return err + } if u.Id > 0 { - return fmt.Errorf("user.id[%d] not equal 0", u.Id) + return _e("user.id[%d] not equal 0", u.Id) } if u.UUID == "" { @@ -270,9 +316,11 @@ func (u *User) Save() error { } if cnt > 0 { - return fmt.Errorf("username already exists") + return _e("Username %s already exists", u.Username) } + u.UpdatedAt = time.Now().Unix() + _, err = DB["rdb"].Insert(u) return err } @@ -440,7 +488,7 @@ func (u *User) HasPermGlobal(operation string) (bool, error) { rids, err := RoleIdsHasOp(operation) if err != nil { - return false, fmt.Errorf("[CheckPermGlobal] RoleIdsHasOp fail: %v, operation: %s", err, operation) + return false, _e("[CheckPermGlobal] RoleIdsHasOp fail: %v, operation: %s", err, operation) } if rids == nil || len(rids) == 0 { @@ -449,7 +497,7 @@ func (u *User) HasPermGlobal(operation string) (bool, error) { has, err := UserHasGlobalRole(u.Id, rids) if err != nil { - return false, fmt.Errorf("[CheckPermGlobal] UserHasGlobalRole fail: %v, username: %s", err, u.Username) + return false, _e("[CheckPermGlobal] UserHasGlobalRole fail: %v, username: %s", err, u.Username) } return has, nil @@ -531,7 +579,9 @@ func safeUserIds(ids []int64) ([]int64, error) { return ret, nil } +// Deprecated func UsernameByUUID(uuid string) string { + logger.Warningf("UsernameByUUID is Deprectaed, use UsernameBySid instead of it") if uuid == "" { return "" } diff --git a/src/models/white_list.go b/src/models/white_list.go new file mode 100644 index 00000000..44a9fa11 --- /dev/null +++ b/src/models/white_list.go @@ -0,0 +1,135 @@ +package models + +import ( + "errors" + "fmt" + "time" +) + +type WhiteList struct { + Id int64 `json:"id"` + StartIp string `json:"startIp"` + StartIpInt int64 `json:"-"` + EndIp string `json:"endIp"` + EndIpInt int64 `json:"-"` + StartTime int64 `json:"startTime"` + EndTime int64 `json:"endTime"` + CreatedAt int64 `json:"createdAt"` + UpdatedAt int64 `json:"updateAt"` + Creator string `json:"creator"` + Updater string `json:"updater"` +} + +func WhiteListAccess(addr string) error { + ip := parseIPv4(addr) + if ip == 0 { + return fmt.Errorf("invalid remote address %s", addr) + } + now := time.Now().Unix() + count, _ := DB["rdb"].Where("start_ip_int? and start_time>? and end_time= big { + return big, i, false + } + } + if i == 0 { + return 0, 0, false + } + return n, i, true +} + +func parseIPv4(s string) uint32 { + var p [4]uint32 + for i := 0; i < 4; i++ { + if len(s) == 0 { + // Missing octets. + return 0 + } + if i > 0 { + if s[0] != '.' { + return 0 + } + s = s[1:] + } + n, c, ok := dtoi(s) + if !ok || n > 0xFF { + return 0 + } + s = s[c:] + p[i] = uint32(n) + } + if len(s) != 0 { + return 0 + } + return p[0]<<24 + p[1]<<16 + p[2]<<8 + p[3] +} + +func (p *WhiteList) Validate() error { + if p.StartIpInt = int64(parseIPv4(p.StartIp)); p.StartIpInt == 0 { + return fmt.Errorf("invalid start ip %s", p.StartIp) + } + if p.EndIpInt = int64(parseIPv4(p.EndIp)); p.EndIpInt == 0 { + return fmt.Errorf("invalid end ip %s", p.EndIp) + } + return nil +} + +func WhiteListTotal(query string) (int64, error) { + if query != "" { + q := "%" + query + "%" + return DB["rdb"].Where("start_ip like ? or end_ip like ?", q, q).Count(new(NodeTrash)) + } + + return DB["rdb"].Count(new(WhiteList)) +} + +func WhiteListGets(query string, limit, offset int) ([]WhiteList, error) { + session := DB["rdb"].Desc("id").Limit(limit, offset) + if query != "" { + q := "%" + query + "%" + session = session.Where("start_ip like ? or end_ip like ?", q, q) + } + + var objs []WhiteList + err := session.Find(&objs) + return objs, err +} + +func WhiteListGet(where string, args ...interface{}) (*WhiteList, error) { + var obj WhiteList + has, err := DB["rdb"].Where(where, args...).Get(&obj) + if err != nil { + return nil, err + } + + if !has { + return nil, errors.New("whiteList not found") + } + + return &obj, nil +} + +func (p *WhiteList) Save() error { + _, err := DB["rdb"].Insert(p) + return err +} +func (p *WhiteList) Update(cols ...string) error { + _, err := DB["rdb"].Where("id=?", p.Id).Cols(cols...).Update(p) + return err +} +func (p *WhiteList) Del() error { + _, err := DB["rdb"].Where("id=?", p.Id).Delete(new(WhiteList)) + return err +} diff --git a/src/modules/ams/http/http_middleware.go b/src/modules/ams/http/http_middleware.go index 1d34339f..3dc178eb 100644 --- a/src/modules/ams/http/http_middleware.go +++ b/src/modules/ams/http/http_middleware.go @@ -47,7 +47,7 @@ func shouldBeService() gin.HandlerFunc { } func mustUsername(c *gin.Context) string { - username := cookieUsername(c) + username := sessionUsername(c) if username == "" { username = headerUsername(c) } @@ -59,8 +59,12 @@ func mustUsername(c *gin.Context) string { return username } -func cookieUsername(c *gin.Context) string { - return models.UsernameByUUID(readCookieUser(c)) +func sessionUsername(c *gin.Context) string { + sess, err := models.SessionGetWithCache(readSessionId(c)) + if err != nil { + return "" + } + return sess.Username } func headerUsername(c *gin.Context) string { @@ -84,11 +88,10 @@ func headerUsername(c *gin.Context) string { // ------------ -func readCookieUser(c *gin.Context) string { - uuid, err := c.Cookie(config.Config.HTTP.CookieName) +func readSessionId(c *gin.Context) string { + sid, err := c.Cookie(config.Config.HTTP.CookieName) if err != nil { return "" } - - return uuid + return sid } diff --git a/src/modules/job/http/http_middleware.go b/src/modules/job/http/http_middleware.go index 0eee8d9f..f5fb41ac 100644 --- a/src/modules/job/http/http_middleware.go +++ b/src/modules/job/http/http_middleware.go @@ -47,7 +47,7 @@ func shouldBeService() gin.HandlerFunc { } func mustUsername(c *gin.Context) string { - username := cookieUsername(c) + username := sessionUsername(c) if username == "" { username = headerUsername(c) } @@ -59,8 +59,12 @@ func mustUsername(c *gin.Context) string { return username } -func cookieUsername(c *gin.Context) string { - return models.UsernameByUUID(readCookieUser(c)) +func sessionUsername(c *gin.Context) string { + sess, err := models.SessionGetWithCache(readSessionId(c)) + if err != nil { + return "" + } + return sess.Username } func headerUsername(c *gin.Context) string { @@ -84,11 +88,10 @@ func headerUsername(c *gin.Context) string { // ------------ -func readCookieUser(c *gin.Context) string { - uuid, err := c.Cookie(config.Config.HTTP.CookieName) +func readSessionId(c *gin.Context) string { + sid, err := c.Cookie(config.Config.HTTP.CookieName) if err != nil { return "" } - - return uuid + return sid } diff --git a/src/modules/monapi/collector/basecollector.go b/src/modules/monapi/collector/basecollector.go new file mode 100644 index 00000000..6618eeb0 --- /dev/null +++ b/src/modules/monapi/collector/basecollector.go @@ -0,0 +1,167 @@ +package collector + +import ( + "encoding/json" + "fmt" + + "github.com/didi/nightingale/src/models" + "github.com/influxdata/telegraf" +) + +type BaseCollector struct { + name string + category Category + newRule func() interface{} +} + +func NewBaseCollector(name string, category Category, newRule func() interface{}) *BaseCollector { + return &BaseCollector{ + name: name, + category: category, + newRule: newRule, + } +} + +type telegrafPlugin interface { + TelegrafInput() (telegraf.Input, error) +} + +func (p BaseCollector) Name() string { return p.name } +func (p BaseCollector) Category() Category { return p.category } +func (p BaseCollector) Template() (interface{}, error) { return Template(p.newRule()) } + +func (p BaseCollector) TelegrafInput(rule *models.CollectRule) (telegraf.Input, error) { + r2 := p.newRule() + if err := json.Unmarshal(rule.Data, r2); err != nil { + return nil, err + } + + plugin, ok := r2.(telegrafPlugin) + if !ok { + return nil, errUnsupported + } + + return plugin.TelegrafInput() +} + +func (p BaseCollector) Get(id int64) (interface{}, error) { + collect := &models.CollectRule{} + has, err := models.DB["mon"].Where("id = ?", id).Get(collect) + if !has { + return nil, err + } + return collect, err +} + +func (p BaseCollector) Gets(nids []int64) (ret []interface{}, err error) { + collects := []models.CollectRule{} + err = models.DB["mon"].Where("collect_type=?", p.name).In("nid", nids).Find(&collects) + for _, c := range collects { + ret = append(ret, c) + } + return ret, err +} + +func (p BaseCollector) GetByNameAndNid(name string, nid int64) (interface{}, error) { + collect := &models.CollectRule{} + has, err := models.DB["mon"].Where("collect_type = ? and name = ? and nid = ?", p.name, name, nid).Get(collect) + if !has { + return nil, err + } + return collect, err +} + +func (p BaseCollector) Create(data []byte, username string) error { + collect := &models.CollectRule{CollectType: p.name} + rule := p.newRule() + + if err := json.Unmarshal(data, collect); err != nil { + return fmt.Errorf("unmarshal body %s err:%v", string(data), err) + } + + if err := collect.Validate(rule); err != nil { + return err + } + + can, err := models.UsernameCandoNodeOp(username, "mon_collect_create", collect.Nid) + if err != nil { + return err + } + if !can { + return fmt.Errorf("permission deny") + } + + collect.Creator = username + collect.LastUpdator = username + + old, err := p.GetByNameAndNid(collect.Name, collect.Nid) + if err != nil { + return err + } + if old != nil { + return fmt.Errorf("同节点下策略名称 %s 已存在", collect.Name) + } + return models.CreateCollect(p.name, username, collect) +} + +func (p BaseCollector) Update(data []byte, username string) error { + collect := &models.CollectRule{} + rule := p.newRule() + + if err := json.Unmarshal(data, collect); err != nil { + return fmt.Errorf("unmarshal body %s err:%v", string(data), err) + } + + if err := collect.Validate(rule); err != nil { + return err + } + + can, err := models.UsernameCandoNodeOp(username, "mon_collect_modify", collect.Nid) + if err != nil { + return err + } + if !can { + return fmt.Errorf("permission deny") + } + + //校验采集是否存在 + obj, err := p.Get(collect.Id) //id找不到的情况 + if err != nil { + return fmt.Errorf("采集不存在 type:%s id:%d", p.name, collect.Id) + } + + tmpId := obj.(*models.CollectRule).Id + if tmpId == 0 { + return fmt.Errorf("采集不存在 type:%s id:%d", p.name, collect.Id) + } + + collect.Creator = username + collect.LastUpdator = username + + old, err := p.GetByNameAndNid(collect.Name, collect.Nid) + if err != nil { + return err + } + if old != nil && tmpId != old.(*models.CollectRule).Id { + return fmt.Errorf("同节点下策略名称 %s 已存在", collect.Name) + } + + return collect.Update() +} + +func (p BaseCollector) Delete(id int64, username string) error { + tmp, err := p.Get(id) //id找不到的情况 + if err != nil { + return fmt.Errorf("采集不存在 type:%s id:%d", p.name, id) + } + nid := tmp.(*models.CollectRule).Nid + can, err := models.UsernameCandoNodeOp(username, "mon_collect_delete", int64(nid)) + if err != nil { + return fmt.Errorf("models.UsernameCandoNodeOp error %s", err) + } + if !can { + return fmt.Errorf("permission deny") + } + + return models.DeleteCollectRule(id) +} diff --git a/src/modules/monapi/collector/collector.go b/src/modules/monapi/collector/collector.go new file mode 100644 index 00000000..d1124cf5 --- /dev/null +++ b/src/modules/monapi/collector/collector.go @@ -0,0 +1,77 @@ +package collector + +import ( + "errors" + "fmt" + + "github.com/didi/nightingale/src/models" + "github.com/didi/nightingale/src/toolkits/i18n" + "github.com/influxdata/telegraf" + "github.com/toolkits/pkg/logger" +) + +var ( + collectors = map[string]Collector{} + remoteCollectors = []string{} + localCollectors = []string{} + errUnsupported = errors.New("unsupported") +) + +type Category string + +const ( + RemoteCategory Category = "remote" // used for prober + LocalCategory Category = "local" // used for agent +) + +type Collector interface { + Name() string + Category() Category + Get(id int64) (interface{}, error) + Gets(nids []int64) ([]interface{}, error) + GetByNameAndNid(name string, nid int64) (interface{}, error) + Create(data []byte, username string) error + Update(data []byte, username string) error + Delete(id int64, username string) error + Template() (interface{}, error) + TelegrafInput(*models.CollectRule) (telegraf.Input, error) +} + +func CollectorRegister(c Collector) error { + name := c.Name() + if _, ok := collectors[name]; ok { + return fmt.Errorf("collector %s exists", name) + } + collectors[name] = c + + if c.Category() == RemoteCategory { + remoteCollectors = append(remoteCollectors, name) + } + + if c.Category() == LocalCategory { + localCollectors = append(localCollectors, name) + } + + return nil +} + +func GetCollector(name string) (Collector, error) { + if c, ok := collectors[name]; !ok { + return nil, fmt.Errorf("collector %s does not exist", name) + } else { + return c, nil + } +} + +func GetRemoteCollectors() []string { + return remoteCollectors +} + +func GetLocalCollectors() []string { + return localCollectors +} + +func _s(format string, a ...interface{}) string { + logger.Debugf(` "%s": "%s",`, format, format) + return i18n.Sprintf(format, a...) +} diff --git a/src/modules/monapi/collector/template.go b/src/modules/monapi/collector/template.go new file mode 100644 index 00000000..716ac5b5 --- /dev/null +++ b/src/modules/monapi/collector/template.go @@ -0,0 +1,227 @@ +package collector + +import ( + "encoding/json" + "fmt" + "reflect" + "strings" + "sync" + "unicode" +) + +var fieldCache sync.Map // map[reflect.Type]structFields + +type Field struct { + skip bool `json:"-"` + // definitions map[string][]Field `json:"-"` + + Name string `json:"name,omitempty"` + Label string `json:"label,omitempty"` + Example string `json:"example,omitempty"` + Description string `json:"description,omitempty"` + Required bool `json:"required,omitempty"` + Items *Field `json:"items,omitempty" description:"arrays's items"` + Type string `json:"type,omitempty" description:"struct,boolean,integer,folat,string,array"` + Ref string `json:"$ref,omitempty" description:"name of the struct ref"` + Fields []Field `json:"fields,omitempty" description:"fields of struct type"` + Definitions map[string][]Field `json:"definitions,omitempty"` +} + +func (p Field) String() string { + return prettify(p) +} + +// cachedTypeContent is like typeFields but uses a cache to avoid repeated work. +func cachedTypeContent(t reflect.Type) Field { + if f, ok := fieldCache.Load(t); ok { + return f.(Field) + } + f, _ := fieldCache.LoadOrStore(t, typeContent(t)) + return f.(Field) +} + +func typeContent(t reflect.Type) Field { + definitions := map[string][]Field{t.String(): nil} + + ret := Field{ + // definitions: map[string][]Field{ + // t.String(): nil, + // }, + } + + for i := 0; i < t.NumField(); i++ { + sf := t.Field(i) + isUnexported := sf.PkgPath != "" + if sf.Anonymous { + panic("unsupported anonymous field") + } else if isUnexported { + // Ignore unexported non-embedded fields. + continue + } + + field := getTagOpt(sf) + if field.skip { + continue + } + ft := sf.Type + + fieldType(ft, &field, definitions) + + // Record found field and index sequence. + if field.Name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct { + ret.Fields = append(ret.Fields, field) + continue + } + + panic("unsupported anonymous, struct field") + } + + definitions[t.String()] = ret.Fields + + ret.Definitions = definitions + + return ret +} + +// tagOptions is the string following a comma in a struct field's "json" +// tag, or the empty string. It does not include the leading comma. +type tagOptions string + +// parseTag splits a struct field's json tag into its name and +// comma-separated options. +func parseTag(tag string) (string, tagOptions) { + if idx := strings.Index(tag, ","); idx != -1 { + return tag[:idx], tagOptions(tag[idx+1:]) + } + return tag, tagOptions("") +} + +// Contains reports whether a comma-separated list of options +// contains a particular substr flag. substr must be surrounded by a +// string boundary or commas. +func (o tagOptions) Contains(optionName string) bool { + if len(o) == 0 { + return false + } + s := string(o) + for s != "" { + var next string + i := strings.Index(s, ",") + if i >= 0 { + s, next = s[:i], s[i+1:] + } + if s == optionName { + return true + } + s = next + } + return false +} + +func getTagOpt(sf reflect.StructField) (opt Field) { + if sf.Anonymous { + return + } + + tag := sf.Tag.Get("json") + if tag == "-" { + opt.skip = true + return + } + + name, opts := parseTag(tag) + if opts.Contains("required") { + opt.Required = true + } + + opt.Name = name + opt.Label = _s(sf.Tag.Get("label")) + opt.Example = sf.Tag.Get("example") + opt.Description = _s(sf.Tag.Get("description")) + + return +} + +func isValidTag(s string) bool { + if s == "" { + return false + } + for _, c := range s { + switch { + case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", c): + case !unicode.IsLetter(c) && !unicode.IsDigit(c): + return false + } + } + return true +} + +func panicType(ft reflect.Type, args ...interface{}) { + msg := fmt.Sprintf("type field %s %s", ft.PkgPath(), ft.Name()) + + if len(args) > 0 { + panic(fmt.Sprint(args...) + " " + msg) + } + panic(msg) +} + +func Template(v interface{}) (interface{}, error) { + rv := reflect.Indirect(reflect.ValueOf(v)) + if rv.Kind() != reflect.Struct { + return nil, fmt.Errorf("invalid argument, must be a struct") + } + + content := cachedTypeContent(rv.Type()) + + return content, nil +} + +func prettify(in interface{}) string { + b, _ := json.MarshalIndent(in, "", " ") + return string(b) +} + +func fieldType(t reflect.Type, in *Field, definitions map[string][]Field) { + if t.Name() == "" && t.Kind() == reflect.Ptr { + // Follow pointer. + t = t.Elem() + } + + switch t.Kind() { + case reflect.Int, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint32, reflect.Uint64: + in.Type = "integer" + case reflect.Float32, reflect.Float64: + in.Type = "float" + case reflect.Bool: + in.Type = "boolean" + case reflect.String: + in.Type = "string" + case reflect.Struct: + name := t.String() + if _, ok := definitions[name]; !ok { + f := cachedTypeContent(t) + for k, v := range f.Definitions { + definitions[k] = v + } + } + in.Ref = t.String() + case reflect.Slice, reflect.Array: + t2 := t.Elem() + if t2.Kind() == reflect.Ptr { + t2 = t2.Elem() + } + if k := t2.Kind(); k == reflect.Int || k == reflect.Int32 || k == reflect.Int64 || + k == reflect.Uint || k == reflect.Uint32 || k == reflect.Uint64 || + k == reflect.Float32 || k == reflect.Float64 || + k == reflect.Bool || k == reflect.String || k == reflect.Struct { + in.Type = "array" + in.Items = &Field{} + fieldType(t2, in.Items, definitions) + } else { + panic(fmt.Sprintf("unspport type %s items %s", t.String(), t2.String())) + } + default: + panic(fmt.Sprintf("unspport type %s", t.String())) + // in.Type = "string" + } +} diff --git a/src/modules/monapi/config/const.go b/src/modules/monapi/config/const.go index e25513be..a9179fb4 100644 --- a/src/modules/monapi/config/const.go +++ b/src/modules/monapi/config/const.go @@ -3,6 +3,7 @@ package config const Version = 1 const JudgesReplicas = 500 +const ProbersReplicas = 500 const DetectorReplicas = 500 const ( diff --git a/src/modules/monapi/config/yaml.go b/src/modules/monapi/config/yaml.go index 55b2d36f..5e7106e0 100644 --- a/src/modules/monapi/config/yaml.go +++ b/src/modules/monapi/config/yaml.go @@ -94,7 +94,9 @@ type loggerSection struct { } type httpSection struct { - Listen string `yaml:"listen"` + Mode string `yaml:"mode"` + CookieName string `yaml:"cookieName"` + CookieDomain string `yaml:"cookieDomain"` } type proxySection struct { diff --git a/src/modules/monapi/http/http_middlewre.go b/src/modules/monapi/http/http_middleware.go similarity index 83% rename from src/modules/monapi/http/http_middlewre.go rename to src/modules/monapi/http/http_middleware.go index 3048f05c..2dd4dc61 100644 --- a/src/modules/monapi/http/http_middlewre.go +++ b/src/modules/monapi/http/http_middleware.go @@ -5,7 +5,6 @@ import ( "github.com/didi/nightingale/src/models" "github.com/didi/nightingale/src/modules/monapi/config" - "github.com/didi/nightingale/src/modules/monapi/tools" "github.com/gin-gonic/gin" "github.com/toolkits/pkg/errors" @@ -15,7 +14,7 @@ import ( // GetCookieUser 从cookie中获取username func GetCookieUser() gin.HandlerFunc { return func(c *gin.Context) { - username := cookieUser(c) + username := sessionUsername(c) if username == "" { username = headerUser(c) } @@ -29,15 +28,6 @@ func GetCookieUser() gin.HandlerFunc { } } -func cookieUser(c *gin.Context) string { - uuid, err := c.Cookie("ecmc-user") - if err != nil { - return "" - } - - return tools.UsernameByUUID(uuid) -} - func headerUser(c *gin.Context) string { token := c.GetHeader("X-User-Token") if token == "" { @@ -88,3 +78,19 @@ func getUserByToken(token string) (user *models.User, err error) { return } + +func sessionUsername(c *gin.Context) string { + sess, err := models.SessionGetWithCache(readSessionId(c)) + if err != nil { + return "" + } + return sess.Username +} + +func readSessionId(c *gin.Context) string { + sid, err := c.Cookie(config.Get().HTTP.CookieName) + if err != nil { + return "" + } + return sid +} diff --git a/src/modules/monapi/http/http_server.go b/src/modules/monapi/http/http_server.go index 08aaa688..3552ffff 100644 --- a/src/modules/monapi/http/http_server.go +++ b/src/modules/monapi/http/http_server.go @@ -2,9 +2,11 @@ package http import ( "context" - "log" + "fmt" "net/http" _ "net/http/pprof" + "os" + "strings" "time" "github.com/didi/nightingale/src/common/address" @@ -31,7 +33,7 @@ func Start() { loggerMid := middleware.LoggerWithConfig(middleware.LoggerConfig{SkipPaths: skipPaths}) recoveryMid := middleware.Recovery() - if c.Logger.Level != "DEBUG" { + if strings.ToLower(c.HTTP.Mode) == "release" { gin.SetMode(gin.ReleaseMode) middleware.DisableConsoleColor() } else { @@ -47,9 +49,10 @@ func Start() { srv.Handler = r go func() { - log.Println("starting http server, listening on:", srv.Addr) + fmt.Println("http.listening:", srv.Addr) if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { - log.Fatalf("listening %s occur error: %s\n", srv.Addr, err) + fmt.Printf("listening %s occur error: %s\n", srv.Addr, err) + os.Exit(3) } }() } @@ -59,14 +62,15 @@ func Shutdown() { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := srv.Shutdown(ctx); err != nil { - log.Fatalln("cannot shutdown http server:", err) + fmt.Println("cannot shutdown http server:", err) + os.Exit(2) } // catching ctx.Done(). timeout of 5 seconds. select { case <-ctx.Done(): - log.Println("shutdown http server timeout of 5 seconds.") + fmt.Println("shutdown http server timeout of 5 seconds.") default: - log.Println("http server stopped") + fmt.Println("http server stopped") } } diff --git a/src/modules/monapi/http/router.go b/src/modules/monapi/http/router.go index 4a18573e..f40ae558 100644 --- a/src/modules/monapi/http/router.go +++ b/src/modules/monapi/http/router.go @@ -83,20 +83,41 @@ func Config(r *gin.Engine) { event.POST("/cur/claim", eventCurClaim) } + // TODO: merge to collect-rule collect := r.Group("/api/mon/collect").Use(GetCookieUser()) { - collect.POST("", collectPost) - collect.GET("/list", collectsGet) - collect.GET("", collectGet) - collect.PUT("", collectPut) - collect.DELETE("", collectsDel) - collect.POST("/check", regExpCheck) + collect.POST("", collectRulePost) // create a collect rule + collect.GET("/list", collectRulesGet) // get collect rules + collect.GET("", collectRuleGet) // get collect rule by type & id + collect.PUT("", collectRulePut) // update collect rule by type & id + collect.DELETE("", collectsRuleDel) // delete collect rules by type & ids + collect.POST("/check", regExpCheck) // check collect rule } + // TODO: merge to collect-rules, used by agent collects := r.Group("/api/mon/collects") { - collects.GET("/:endpoint", collectGetByEndpoint) - collects.GET("", collectsGet) + collects.GET("/:endpoint", collectRulesGetByLocalEndpoint) // get collect rules by endpoint, for agent + collects.GET("", collectRulesGet) // get collect rules + } + + collectRules := r.Group("/api/mon/collect-rules").Use(GetCookieUser()) + { + collectRules.POST("", collectRulePost) // create a collect rule + collectRules.GET("/list", collectRulesGet) // get collect rules + collectRules.GET("", collectRuleGet) // get collect rule by type & id + collectRules.PUT("", collectRulePut) // update collect rule by type & id + collectRules.DELETE("", collectsRuleDel) // delete collect rules by type & ids + collectRules.POST("/check", regExpCheck) // check collect rule + collectRules.GET("/types", collectRuleTypesGet) // get collect types, category: local|remote + collectRules.GET("/types/:type/template", collectRuleTemplateGet) // get collect teplate by type + + } + + collectRulesAnonymous := r.Group("/api/mon/collect-rules") + { + collectRulesAnonymous.GET("/endpoints/:endpoint/remote", collectRulesGetByRemoteEndpoint) // for prober + collectRulesAnonymous.GET("/endpoints/:endpoint/local", collectRulesGetByLocalEndpoint) // for agent } stra := r.Group("/api/mon/stra").Use(GetCookieUser()) @@ -151,6 +172,15 @@ func Config(r *gin.Engine) { indexProxy.POST("/counter/detail", indexReq) } + /* + v1 := r.Group("/v1/mon") + { + v1.POST("/report-detector-heartbeat", detectorHeartBeat) + v1.GET("/detectors", detectorInstanceGets) + v1.GET("/rules", collectRulesGet) + } + */ + if config.Get().Logger.Level == "DEBUG" { pprof.Register(r, "/api/monapi/debug/pprof") } diff --git a/src/modules/monapi/http/router_collect.go b/src/modules/monapi/http/router_collect.go index 556d550f..223e7ac3 100644 --- a/src/modules/monapi/http/router_collect.go +++ b/src/modules/monapi/http/router_collect.go @@ -6,8 +6,7 @@ import ( "regexp" "strings" - "github.com/didi/nightingale/src/models" - "github.com/didi/nightingale/src/modules/monapi/config" + "github.com/didi/nightingale/src/modules/monapi/collector" "github.com/didi/nightingale/src/modules/monapi/scache" "github.com/gin-gonic/gin" @@ -16,201 +15,44 @@ import ( ) type CollectRecv struct { - Type string `json:"type"` - Data interface{} `json:"data"` + Type string `json:"type"` + Data json.RawMessage `json:"data"` } -//此处实现需要重构 -func collectPost(c *gin.Context) { - creator := loginUsername(c) +func collectRulePost(c *gin.Context) { var recv []CollectRecv errors.Dangerous(c.ShouldBind(&recv)) + creator := loginUsername(c) for _, obj := range recv { - switch obj.Type { - case "port": - collect := new(models.PortCollect) + cl, err := collector.GetCollector(obj.Type) + errors.Dangerous(err) - b, err := json.Marshal(obj.Data) - if err != nil { - bomb("marshal body %s err:%v", obj, err) - } - - err = json.Unmarshal(b, collect) - if err != nil { - bomb("unmarshal body %s err:%v", string(b), err) - } - - can, err := models.UsernameCandoNodeOp(loginUsername(c), "mon_collect_create", collect.Nid) - errors.Dangerous(err) - if !can { - bomb("permission deny") - } - - collect.Creator = creator - collect.LastUpdator = creator - - nid := collect.Nid - name := collect.Name - - old, err := models.GetCollectByNameAndNid(obj.Type, name, nid) - errors.Dangerous(err) - if old != nil { - bomb("same stra name %s in node", name) - } - errors.Dangerous(models.CreateCollect(obj.Type, creator, collect)) - - case "proc": - collect := new(models.ProcCollect) - - b, err := json.Marshal(obj.Data) - if err != nil { - bomb("marshal body %s err:%v", obj, err) - } - - err = json.Unmarshal(b, collect) - if err != nil { - bomb("unmarshal body %s err:%v", string(b), err) - } - - can, err := models.UsernameCandoNodeOp(loginUsername(c), "mon_collect_create", collect.Nid) - errors.Dangerous(err) - if !can { - bomb("permission deny") - } - - collect.Creator = creator - collect.LastUpdator = creator - - nid := collect.Nid - name := collect.Name - - old, err := models.GetCollectByNameAndNid(obj.Type, name, nid) - errors.Dangerous(err) - if old != nil { - bomb("same stra name %s in node", name) - } - errors.Dangerous(models.CreateCollect(obj.Type, creator, collect)) - case "log": - collect := new(models.LogCollect) - - b, err := json.Marshal(obj.Data) - if err != nil { - bomb("marshal body %s err:%v", obj, err) - } - - err = json.Unmarshal(b, collect) - if err != nil { - bomb("unmarshal body %s err:%v", string(b), err) - } - - can, err := models.UsernameCandoNodeOp(loginUsername(c), "mon_collect_create", collect.Nid) - errors.Dangerous(err) - if !can { - bomb("permission deny") - } - - collect.Encode() - collect.Creator = creator - collect.LastUpdator = creator - - nid := collect.Nid - name := collect.Name - - old, err := models.GetCollectByNameAndNid(obj.Type, name, nid) - errors.Dangerous(err) - if old != nil { - bomb("same stra name %s in node", name) - } - errors.Dangerous(models.CreateCollect(obj.Type, creator, collect)) - case "plugin": - collect := new(models.PluginCollect) - - b, err := json.Marshal(obj.Data) - if err != nil { - bomb("marshal body %s err:%v", obj, err) - } - - err = json.Unmarshal(b, collect) - if err != nil { - bomb("unmarshal body %s err:%v", string(b), err) - } - - can, err := models.UsernameCandoNodeOp(loginUsername(c), "mon_collect_create", collect.Nid) - errors.Dangerous(err) - if !can { - bomb("permission deny") - } - - collect.Creator = creator - collect.LastUpdator = creator - - nid := collect.Nid - name := collect.Name - - old, err := models.GetCollectByNameAndNid(obj.Type, name, nid) - errors.Dangerous(err) - if old != nil { - bomb("same stra name %s in node", name) - } - errors.Dangerous(models.CreateCollect(obj.Type, creator, collect)) - - case "api": - collect := new(models.ApiCollect) - - b, err := json.Marshal(obj.Data) - if err != nil { - bomb("marshal body %s err:%v", obj, err) - } - - err = json.Unmarshal(b, collect) - if err != nil { - bomb("unmarshal body %s err:%v", string(b), err) - } - - can, err := models.UsernameCandoNodeOp(loginUsername(c), "mon_collect_create", collect.Nid) - errors.Dangerous(err) - if !can { - bomb("permission deny") - } - - collect.Encode() - collect.Creator = creator - collect.LastUpdator = creator - - nid := collect.Nid - name := collect.Name - - old, err := models.GetCollectByNameAndNid(obj.Type, name, nid) - errors.Dangerous(err) - if old != nil { - bomb("same stra name %s in node", name) - } - errors.Dangerous(models.CreateCollect(obj.Type, creator, collect)) - - default: - bomb("collect type not support") + if err := cl.Create([]byte(obj.Data), creator); err != nil { + errors.Bomb("%s add rule err %s", obj.Type, err) } } renderData(c, "ok", nil) } -func collectGetByEndpoint(c *gin.Context) { +func collectRulesGetByLocalEndpoint(c *gin.Context) { collect := scache.CollectCache.GetBy(urlParamStr(c, "endpoint")) renderData(c, collect, nil) } -func collectGet(c *gin.Context) { +func collectRuleGet(c *gin.Context) { t := mustQueryStr(c, "type") - nid := mustQueryInt64(c, "id") - collect, err := models.GetCollectById(t, nid) + id := mustQueryInt64(c, "id") + + cl, err := collector.GetCollector(t) errors.Dangerous(err) - renderData(c, collect, nil) + ret, err := cl.Get(id) + renderData(c, ret, err) } -func collectsGet(c *gin.Context) { +func collectRulesGet(c *gin.Context) { nid := queryInt64(c, "nid", -1) tp := queryStr(c, "type", "") var resp []interface{} @@ -222,275 +64,36 @@ func collectsGet(c *gin.Context) { types = []string{tp} } - if nid == -1 && tp != "" { //没有nid参数,tp不为空 - if tp == "api" { - collects, err := models.GetApiCollects() - errors.Dangerous(err) - for _, c := range collects { - c.Decode() - resp = append(resp, c) - } + nids := []int64{nid} + for _, t := range types { + cl, err := collector.GetCollector(t) + if err != nil { + logger.Warning(t, err) + continue } - } else { - nids := []int64{nid} - for _, t := range types { - collects, err := models.GetCollectByNid(t, nids) - if err != nil { - logger.Warning(t, err) - continue - } - resp = append(resp, collects...) + + ret, err := cl.Gets(nids) + if err != nil { + logger.Warning(t, err) + continue } + resp = append(resp, ret...) } renderData(c, resp, nil) } -func collectPut(c *gin.Context) { - creator := loginUsername(c) - +func collectRulePut(c *gin.Context) { var recv CollectRecv errors.Dangerous(c.ShouldBind(&recv)) - switch recv.Type { - case "port": - collect := new(models.PortCollect) + cl, err := collector.GetCollector(recv.Type) + errors.Dangerous(err) - b, err := json.Marshal(recv.Data) - if err != nil { - bomb("marshal body %s err:%v", recv, err) - } - - err = json.Unmarshal(b, collect) - if err != nil { - bomb("unmarshal body %s err:%v", string(b), err) - } - - can, err := models.UsernameCandoNodeOp(loginUsername(c), "mon_collect_modify", collect.Nid) - errors.Dangerous(err) - if !can { - bomb("permission deny") - } - - nid := collect.Nid - name := collect.Name - - //校验采集是否存在 - obj, err := models.GetCollectById(recv.Type, collect.Id) //id找不到的情况 - if err != nil { - bomb("采集不存在 type:%s id:%d", recv.Type, collect.Id) - } - - tmpId := obj.(*models.PortCollect).Id - if tmpId == 0 { - bomb("采集不存在 type:%s id:%d", recv.Type, collect.Id) - } - - collect.Creator = creator - collect.LastUpdator = creator - - old, err := models.GetCollectByNameAndNid(recv.Type, name, nid) - errors.Dangerous(err) - if old != nil && tmpId != old.(*models.PortCollect).Id { - bomb("same stra name %s in node", name) - } - - errors.Dangerous(collect.Update()) - renderData(c, "ok", nil) - return - case "proc": - collect := new(models.ProcCollect) - - b, err := json.Marshal(recv.Data) - if err != nil { - bomb("marshal body %s err:%v", recv, err) - } - - err = json.Unmarshal(b, collect) - if err != nil { - bomb("unmarshal body %s err:%v", string(b), err) - } - - can, err := models.UsernameCandoNodeOp(loginUsername(c), "mon_collect_modify", collect.Nid) - errors.Dangerous(err) - if !can { - bomb("permission deny") - } - - nid := collect.Nid - name := collect.Name - - //校验采集是否存在 - obj, err := models.GetCollectById(recv.Type, collect.Id) //id找不到的情况 - if err != nil { - bomb("采集不存在 type:%s id:%d", recv.Type, collect.Id) - } - - tmpId := obj.(*models.ProcCollect).Id - if tmpId == 0 { - bomb("采集不存在 type:%s id:%d", recv.Type, collect.Id) - } - - can, err = models.UsernameCandoNodeOp(loginUsername(c), "mon_collect_modify", collect.Nid) - errors.Dangerous(err) - if !can { - bomb("permission deny") - } - - collect.Creator = creator - collect.LastUpdator = creator - - old, err := models.GetCollectByNameAndNid(recv.Type, name, nid) - errors.Dangerous(err) - if old != nil && tmpId != old.(*models.ProcCollect).Id { - bomb("same stra name %s in node", name) - } - - errors.Dangerous(collect.Update()) - renderData(c, "ok", nil) - return - case "log": - collect := new(models.LogCollect) - - b, err := json.Marshal(recv.Data) - if err != nil { - bomb("marshal body %s err:%v", recv, err) - } - - err = json.Unmarshal(b, collect) - if err != nil { - bomb("unmarshal body %s err:%v", string(b), err) - } - - can, err := models.UsernameCandoNodeOp(loginUsername(c), "mon_collect_modify", collect.Nid) - errors.Dangerous(err) - if !can { - bomb("permission deny") - } - - collect.Encode() - nid := collect.Nid - name := collect.Name - - //校验采集是否存在 - obj, err := models.GetCollectById(recv.Type, collect.Id) //id找不到的情况 - if err != nil { - bomb("采集不存在 type:%s id:%d", recv.Type, collect.Id) - } - - tmpId := obj.(*models.LogCollect).Id - if tmpId == 0 { - bomb("采集不存在 type:%s id:%d", recv.Type, collect.Id) - } - - collect.Creator = creator - collect.LastUpdator = creator - - old, err := models.GetCollectByNameAndNid(recv.Type, name, nid) - errors.Dangerous(err) - if old != nil && tmpId != old.(*models.LogCollect).Id { - bomb("same stra name %s in node", name) - } - - errors.Dangerous(collect.Update()) - renderData(c, "ok", nil) - return - case "plugin": - collect := new(models.PluginCollect) - - b, err := json.Marshal(recv.Data) - if err != nil { - bomb("marshal body %s err:%v", recv, err) - } - - err = json.Unmarshal(b, collect) - if err != nil { - bomb("unmarshal body %s err:%v", string(b), err) - } - - can, err := models.UsernameCandoNodeOp(loginUsername(c), "mon_collect_modify", collect.Nid) - errors.Dangerous(err) - if !can { - bomb("permission deny") - } - - nid := collect.Nid - name := collect.Name - - //校验采集是否存在 - obj, err := models.GetCollectById(recv.Type, collect.Id) //id找不到的情况 - if err != nil { - bomb("采集不存在 type:%s id:%d", recv.Type, collect.Id) - } - - tmpId := obj.(*models.PluginCollect).Id - if tmpId == 0 { - bomb("采集不存在 type:%s id:%d", recv.Type, collect.Id) - } - - collect.Creator = creator - collect.LastUpdator = creator - - old, err := models.GetCollectByNameAndNid(recv.Type, name, nid) - errors.Dangerous(err) - if old != nil && tmpId != old.(*models.PluginCollect).Id { - bomb("same stra name %s in node", name) - } - - errors.Dangerous(collect.Update()) - renderData(c, "ok", nil) - return - case "api": - collect := new(models.ApiCollect) - - b, err := json.Marshal(recv.Data) - if err != nil { - bomb("marshal body %s err:%v", recv, err) - } - - err = json.Unmarshal(b, collect) - if err != nil { - bomb("unmarshal body %s err:%v", string(b), err) - } - - can, err := models.UsernameCandoNodeOp(loginUsername(c), "mon_collect_modify", collect.Nid) - errors.Dangerous(err) - if !can { - bomb("permission deny") - } - - collect.Encode() - nid := collect.Nid - name := collect.Name - - //校验采集是否存在 - obj, err := models.GetCollectById(recv.Type, collect.Id) //id找不到的情况 - if err != nil { - bomb("采集不存在 type:%s id:%d", recv.Type, collect.Id) - } - - tmpId := obj.(*models.ApiCollect).Id - if tmpId == 0 { - bomb("采集不存在 type:%s id:%d", recv.Type, collect.Id) - } - - collect.Creator = creator - collect.LastUpdator = creator - - old, err := models.GetCollectByNameAndNid(recv.Type, name, nid) - errors.Dangerous(err) - if old != nil && tmpId != old.(*models.ApiCollect).Id { - bomb("same stra name %s in node", name) - } - - errors.Dangerous(collect.Update()) - renderData(c, "ok", nil) - return - - default: - bomb("采集类型不合法") + creator := loginUsername(c) + if err := cl.Update([]byte(recv.Data), creator); err != nil { + errors.Bomb("%s update rule err %s", recv.Type, err) } - renderData(c, "ok", nil) } @@ -499,89 +102,44 @@ type CollectsDelRev struct { Ids []int64 `json:"ids"` } -func collectsDel(c *gin.Context) { - username := loginUsername(c) +func collectsRuleDel(c *gin.Context) { var recv []CollectsDelRev errors.Dangerous(c.ShouldBind(&recv)) + + username := loginUsername(c) for _, obj := range recv { - for i := 0; i < len(obj.Ids); i++ { - tmp, err := models.GetCollectById(obj.Type, obj.Ids[i]) //id找不到的情况 - if err != nil { - bomb("采集不存在 type:%s id:%d", obj.Type, obj.Ids[i]) - } - - if tmp == nil { - bomb("采集不存在 type:%s id:%d", obj.Type, obj.Ids[i]) - } - - var nid int64 - switch obj.Type { - case "log": - nid = tmp.(*models.LogCollect).Nid - case "proc": - nid = tmp.(*models.ProcCollect).Nid - case "port": - nid = tmp.(*models.PortCollect).Nid - case "plugin": - nid = tmp.(*models.PluginCollect).Nid - } - - can, err := models.UsernameCandoNodeOp(loginUsername(c), "mon_collect_delete", int64(nid)) + cl, err := collector.GetCollector(obj.Type) errors.Dangerous(err) - if !can { - bomb("permission deny") - } - } - for i := 0; i < len(obj.Ids); i++ { - switch obj.Type { - case "api": - err := models.DeleteApiCollect(obj.Ids[i]) - errors.Dangerous(err) - default: - err := models.DeleteCollectById(obj.Type, username, obj.Ids[i]) + if err := cl.Delete(obj.Ids[i], username); err != nil { errors.Dangerous(err) } - } } renderData(c, "ok", nil) } -type ApiStraRev struct { - Sid int64 `json:"sid"` - Cid int64 `json:"cid"` +func collectRuleTypesGet(c *gin.Context) { + category := mustQueryStr(c, "category") + switch category { + case "remote": + renderData(c, collector.GetRemoteCollectors(), nil) + case "local": + renderData(c, collector.GetLocalCollectors(), nil) + default: + renderData(c, nil, nil) + } } -type ApiStraRes struct { - Has bool `json:"has"` - Sid int64 `json:"sid"` -} - -func ApiStraGet(c *gin.Context) { - cid := mustQueryInt64(c, "cid") - sid, err := models.GetSidByCid(cid) - var res ApiStraRes +func collectRuleTemplateGet(c *gin.Context) { + t := urlParamStr(c, "type") + collector, err := collector.GetCollector(t) errors.Dangerous(err) - if sid != 0 { - res.Has = true - res.Sid = sid - } - renderData(c, res, nil) -} -func ApiStraPost(c *gin.Context) { - recv := new(models.ApiCollectSid) - errors.Dangerous(c.ShouldBind(&recv)) - errors.Dangerous(recv.Add()) - renderData(c, "ok", nil) - return -} - -func ApiRegionGet(c *gin.Context) { - renderData(c, config.Get().Region, nil) + tpl, err := collector.Template() + renderData(c, tpl, err) } type RegExpCheckDto struct { @@ -788,3 +346,9 @@ func genSubErrMsg(sign string) string { func genIllegalCharErrMsg() string { return fmt.Sprintf(`正则匹配成功。但是tag的key或者value包含非法字符:[:,/=\r\n\t], 请重新调整`) } + +func collectRulesGetByRemoteEndpoint(c *gin.Context) { + rules := scache.CollectRuleCache.GetBy(urlParamStr(c, "endpoint")) + renderData(c, rules, nil) + +} diff --git a/src/modules/monapi/http/router_funcs.go b/src/modules/monapi/http/router_funcs.go index 8ebaab2d..00850f67 100644 --- a/src/modules/monapi/http/router_funcs.go +++ b/src/modules/monapi/http/router_funcs.go @@ -142,22 +142,13 @@ func renderData(c *gin.Context, data interface{}, err error) { renderMessage(c, err.Error()) } -func cookieUsername(c *gin.Context) string { - uuid, err := c.Cookie("ecmc-user") - if err != nil { - return "" - } - - return models.UsernameByUUID(uuid) -} - func loginUsername(c *gin.Context) string { username1, has := c.Get("username") if has { return username1.(string) } - username2 := cookieUsername(c) + username2 := sessionUsername(c) if username2 == "" { bomb("unauthorized") } diff --git a/src/modules/monapi/http/router_stra.go b/src/modules/monapi/http/router_stra.go index 85516715..421db0a2 100644 --- a/src/modules/monapi/http/router_stra.go +++ b/src/modules/monapi/http/router_stra.go @@ -129,7 +129,7 @@ func effectiveStrasGet(c *gin.Context) { if queryInt(c, "all", 0) == 1 { stras = scache.StraCache.GetAll() } else if instance != "" { - node, err := scache.ActiveNode.GetNodeBy(instance) + node, err := scache.ActiveJudgeNode.GetNodeBy(instance) errors.Dangerous(err) stras = scache.StraCache.GetByNode(node) diff --git a/src/modules/monapi/monapi.go b/src/modules/monapi/monapi.go index cea61f15..af4efcd6 100644 --- a/src/modules/monapi/monapi.go +++ b/src/modules/monapi/monapi.go @@ -18,6 +18,7 @@ import ( "github.com/didi/nightingale/src/modules/monapi/scache" "github.com/didi/nightingale/src/toolkits/i18n" + _ "github.com/didi/nightingale/src/modules/monapi/plugins/all" _ "github.com/go-sql-driver/mysql" "github.com/toolkits/pkg/cache" diff --git a/src/modules/monapi/plugins/.gitignore b/src/modules/monapi/plugins/.gitignore new file mode 100644 index 00000000..6dd5e90c --- /dev/null +++ b/src/modules/monapi/plugins/.gitignore @@ -0,0 +1 @@ +prometheus/ diff --git a/src/modules/monapi/plugins/all/all.go b/src/modules/monapi/plugins/all/all.go new file mode 100644 index 00000000..e7f1e0bd --- /dev/null +++ b/src/modules/monapi/plugins/all/all.go @@ -0,0 +1,16 @@ +package all + +import ( + // remote + _ "github.com/didi/nightingale/src/modules/monapi/plugins/api" + _ "github.com/didi/nightingale/src/modules/monapi/plugins/github" + _ "github.com/didi/nightingale/src/modules/monapi/plugins/mysql" + // _ "github.com/didi/nightingale/src/modules/monapi/plugins/prometheus" + _ "github.com/didi/nightingale/src/modules/monapi/plugins/redis" + + // local + _ "github.com/didi/nightingale/src/modules/monapi/plugins/log" + _ "github.com/didi/nightingale/src/modules/monapi/plugins/plugin" + _ "github.com/didi/nightingale/src/modules/monapi/plugins/port" + _ "github.com/didi/nightingale/src/modules/monapi/plugins/proc" +) diff --git a/src/modules/monapi/plugins/api/api.go b/src/modules/monapi/plugins/api/api.go new file mode 100644 index 00000000..81431a52 --- /dev/null +++ b/src/modules/monapi/plugins/api/api.go @@ -0,0 +1,147 @@ +package api + +import ( + "encoding/json" + "errors" + "fmt" + + "github.com/didi/nightingale/src/models" + "github.com/didi/nightingale/src/modules/monapi/collector" + "github.com/influxdata/telegraf" +) + +func init() { + collector.CollectorRegister(&ApiCollector{}) +} + +type ApiCollector struct{} + +func (p ApiCollector) Name() string { return "api" } +func (p ApiCollector) Category() collector.Category { return collector.RemoteCategory } +func (p ApiCollector) Template() (interface{}, error) { return nil, nil } +func (p ApiCollector) TelegrafInput(*models.CollectRule) (telegraf.Input, error) { + return nil, errors.New("unsupported") +} + +func (p ApiCollector) Get(id int64) (interface{}, error) { + collect := new(models.ApiCollect) + has, err := models.DB["mon"].Where("id = ?", id).Get(collect) + if !has { + return nil, err + } + return collect, err +} + +func (p ApiCollector) Gets(nids []int64) (ret []interface{}, err error) { + collects := []models.ApiCollect{} + err = models.DB["mon"].In("nid", nids).Find(&collects) + for _, c := range collects { + c.Decode() + ret = append(ret, c) + } + return ret, err +} + +func (p ApiCollector) GetByNameAndNid(name string, nid int64) (interface{}, error) { + collect := new(models.ApiCollect) + has, err := models.DB["mon"].Where("name = ? and nid = ?", name, nid).Get(collect) + if !has { + return nil, err + } + return collect, err +} + +func (p ApiCollector) Create(data []byte, username string) error { + collect := new(models.ApiCollect) + + err := json.Unmarshal(data, collect) + if err != nil { + return fmt.Errorf("unmarshal body %s err:%v", string(data), err) + } + + can, err := models.UsernameCandoNodeOp(username, "mon_collect_create", collect.Nid) + if err != nil { + return err + } + if !can { + return fmt.Errorf("permission deny") + } + + collect.Encode() + collect.Creator = username + collect.LastUpdator = username + + nid := collect.Nid + name := collect.Name + + old, err := p.GetByNameAndNid(name, nid) + if err != nil { + return err + } + if old != nil { + return fmt.Errorf("同节点下策略名称 %s 已存在", name) + } + return models.CreateCollect(p.Name(), username, collect) +} + +func (p ApiCollector) Update(data []byte, username string) error { + collect := new(models.ApiCollect) + + err := json.Unmarshal(data, collect) + if err != nil { + return fmt.Errorf("unmarshal body %s err:%v", string(data), err) + } + + can, err := models.UsernameCandoNodeOp(username, "mon_collect_modify", collect.Nid) + if err != nil { + return err + } + if !can { + return fmt.Errorf("permission deny") + } + + collect.Encode() + nid := collect.Nid + name := collect.Name + + //校验采集是否存在 + obj, err := p.Get(collect.Id) //id找不到的情况 + if err != nil { + return fmt.Errorf("采集不存在 type:%s id:%d", p.Name(), collect.Id) + } + + tmpId := obj.(*models.ApiCollect).Id + if tmpId == 0 { + return fmt.Errorf("采集不存在 type:%s id:%d", p.Name(), collect.Id) + } + + collect.Creator = username + collect.LastUpdator = username + + old, err := p.GetByNameAndNid(name, nid) + if err != nil { + return err + } + if old != nil && tmpId != old.(*models.ApiCollect).Id { + return fmt.Errorf("同节点下策略名称 %s 已存在", name) + } + + return collect.Update() +} + +func (p ApiCollector) Delete(id int64, username string) error { + tmp, err := p.Get(id) //id找不到的情况 + if err != nil { + return fmt.Errorf("采集不存在 type:%s id:%d", p.Name(), id) + } + nid := tmp.(*models.ApiCollect).Nid + can, err := models.UsernameCandoNodeOp(username, "mon_collect_delete", int64(nid)) + if err != nil { + return err + } + if !can { + return fmt.Errorf("permission deny") + } + + return models.DeleteCollectById(p.Name(), username, id) +} diff --git a/src/modules/monapi/plugins/github/README.md b/src/modules/monapi/plugins/github/README.md new file mode 100644 index 00000000..46127082 --- /dev/null +++ b/src/modules/monapi/plugins/github/README.md @@ -0,0 +1,64 @@ +# GitHub Input Plugin + +Gather repository information from [GitHub][] hosted repositories. + +**Note:** Telegraf also contains the [webhook][] input which can be used as an +alternative method for collecting repository information. + +### Configuration + +```toml +[[inputs.github]] + ## List of repositories to monitor + repositories = [ + "influxdata/telegraf", + "influxdata/influxdb" + ] + + ## Github API access token. Unauthenticated requests are limited to 60 per hour. + # access_token = "" + + ## Github API enterprise url. Github Enterprise accounts must specify their base url. + # enterprise_base_url = "" + + ## Timeout for HTTP requests. + # http_timeout = "5s" +``` + +### Metrics + +- github_repository + - tags: + - name - The repository name + - owner - The owner of the repository + - language - The primary language of the repository + - license - The license set for the repository + - fields: + - forks (int) + - open_issues (int) + - networks (int) + - size (int) + - subscribers (int) + - stars (int) + - watchers (int) + +When the [internal][] input is enabled: + ++ internal_github + - tags: + - access_token - An obfuscated reference to the configured access token or "Unauthenticated" + - fields: + - limit - How many requests you are limited to (per hour) + - remaining - How many requests you have remaining (per hour) + - blocks - How many requests have been blocked due to rate limit + +### Example Output + +``` +github_repository,language=Go,license=MIT\ License,name=telegraf,owner=influxdata forks=2679i,networks=2679i,open_issues=794i,size=23263i,stars=7091i,subscribers=316i,watchers=7091i 1563901372000000000 +internal_github,access_token=Unauthenticated rate_limit_remaining=59i,rate_limit_limit=60i,rate_limit_blocks=0i 1552653551000000000 +``` + +[GitHub]: https://www.github.com +[internal]: /plugins/inputs/internal +[webhook]: /plugins/inputs/webhooks/github diff --git a/src/modules/monapi/plugins/github/github.go b/src/modules/monapi/plugins/github/github.go new file mode 100644 index 00000000..b60dcf17 --- /dev/null +++ b/src/modules/monapi/plugins/github/github.go @@ -0,0 +1,55 @@ +package github + +import ( + "fmt" + "time" + + "github.com/didi/nightingale/src/modules/monapi/collector" + "github.com/influxdata/telegraf" +) + +func init() { + collector.CollectorRegister(NewGitHubCollector()) // for monapi +} + +type GitHubCollector struct { + *collector.BaseCollector +} + +func NewGitHubCollector() *GitHubCollector { + return &GitHubCollector{BaseCollector: collector.NewBaseCollector( + "github", + collector.RemoteCategory, + func() interface{} { return &GitHubRule{} }, + )} +} + +type GitHubRule struct { + Repositories []string `label:"Repositories" json:"repositories" description:"List of repositories to monitor"` + AccessToken string `label:"Access token" json:"access_token" description:"Github API access token. Unauthenticated requests are limited to 60 per hour"` + EnterpriseBaseURL string `label:"Enterprise base url" json:"enterprise_base_url" description:"Github API enterprise url. Github Enterprise accounts must specify their base url"` + HTTPTimeout int `label:"HTTP timeout" json:"http_timeout" description:"Timeout for HTTP requests"` +} + +func (p *GitHubRule) Validate() error { + if len(p.Repositories) == 0 || p.Repositories[0] == "" { + return fmt.Errorf("github.rule.repositories must be set") + } + if p.HTTPTimeout == 0 { + p.HTTPTimeout = 5 + } + return nil +} + +func (p *GitHubRule) TelegrafInput() (telegraf.Input, error) { + if err := p.Validate(); err != nil { + return nil, err + } + + return &GitHub{ + Repositories: p.Repositories, + AccessToken: p.AccessToken, + EnterpriseBaseURL: p.EnterpriseBaseURL, + HTTPTimeout: time.Second * time.Duration(p.HTTPTimeout), + }, nil +} diff --git a/src/modules/monapi/plugins/github/github.telegraf.go b/src/modules/monapi/plugins/github/github.telegraf.go new file mode 100644 index 00000000..856e685b --- /dev/null +++ b/src/modules/monapi/plugins/github/github.telegraf.go @@ -0,0 +1,200 @@ +package github + +import ( + "context" + "fmt" + "net/http" + "strings" + "sync" + "time" + + "github.com/google/go-github/v32/github" + "github.com/influxdata/telegraf" + "github.com/influxdata/telegraf/selfstat" + "golang.org/x/oauth2" +) + +// GitHub - plugin main structure +type GitHub struct { + Repositories []string `toml:"repositories"` + AccessToken string `toml:"access_token"` + EnterpriseBaseURL string `toml:"enterprise_base_url"` + HTTPTimeout time.Duration `toml:"http_timeout"` + githubClient *github.Client + + obfuscatedToken string + + RateLimit selfstat.Stat + RateLimitErrors selfstat.Stat + RateRemaining selfstat.Stat +} + +const sampleConfig = ` + ## List of repositories to monitor. + repositories = [ + "influxdata/telegraf", + "influxdata/influxdb" + ] + + ## Github API access token. Unauthenticated requests are limited to 60 per hour. + # access_token = "" + + ## Github API enterprise url. Github Enterprise accounts must specify their base url. + # enterprise_base_url = "" + + ## Timeout for HTTP requests. + # http_timeout = "5s" +` + +// SampleConfig returns sample configuration for this plugin. +func (g *GitHub) SampleConfig() string { + return sampleConfig +} + +// Description returns the plugin description. +func (g *GitHub) Description() string { + return "Gather repository information from GitHub hosted repositories." +} + +// Create GitHub Client +func (g *GitHub) createGitHubClient(ctx context.Context) (*github.Client, error) { + httpClient := &http.Client{ + Transport: &http.Transport{ + Proxy: http.ProxyFromEnvironment, + }, + Timeout: g.HTTPTimeout, + } + + g.obfuscatedToken = "Unauthenticated" + + if g.AccessToken != "" { + tokenSource := oauth2.StaticTokenSource( + &oauth2.Token{AccessToken: g.AccessToken}, + ) + oauthClient := oauth2.NewClient(ctx, tokenSource) + _ = context.WithValue(ctx, oauth2.HTTPClient, oauthClient) + + g.obfuscatedToken = g.AccessToken[0:4] + "..." + g.AccessToken[len(g.AccessToken)-3:] + + return g.newGithubClient(oauthClient) + } + + return g.newGithubClient(httpClient) +} + +func (g *GitHub) newGithubClient(httpClient *http.Client) (*github.Client, error) { + if g.EnterpriseBaseURL != "" { + return github.NewEnterpriseClient(g.EnterpriseBaseURL, "", httpClient) + } + return github.NewClient(httpClient), nil +} + +// Gather GitHub Metrics +func (g *GitHub) Gather(acc telegraf.Accumulator) error { + ctx := context.Background() + + if g.githubClient == nil { + githubClient, err := g.createGitHubClient(ctx) + + if err != nil { + return err + } + + g.githubClient = githubClient + + tokenTags := map[string]string{ + "access_token": g.obfuscatedToken, + } + + g.RateLimitErrors = selfstat.Register("github", "rate_limit_blocks", tokenTags) + g.RateLimit = selfstat.Register("github", "rate_limit_limit", tokenTags) + g.RateRemaining = selfstat.Register("github", "rate_limit_remaining", tokenTags) + } + + var wg sync.WaitGroup + wg.Add(len(g.Repositories)) + + for _, repository := range g.Repositories { + go func(repositoryName string, acc telegraf.Accumulator) { + defer wg.Done() + + owner, repository, err := splitRepositoryName(repositoryName) + if err != nil { + acc.AddError(err) + return + } + + repositoryInfo, response, err := g.githubClient.Repositories.Get(ctx, owner, repository) + + if _, ok := err.(*github.RateLimitError); ok { + g.RateLimitErrors.Incr(1) + } + + if err != nil { + acc.AddError(err) + return + } + + g.RateLimit.Set(int64(response.Rate.Limit)) + g.RateRemaining.Set(int64(response.Rate.Remaining)) + + now := time.Now() + tags := getTags(repositoryInfo) + fields := getFields(repositoryInfo) + + acc.AddFields("github_repository", fields, tags, now) + }(repository, acc) + } + + wg.Wait() + return nil +} + +func splitRepositoryName(repositoryName string) (string, string, error) { + splits := strings.SplitN(repositoryName, "/", 2) + + if len(splits) != 2 { + return "", "", fmt.Errorf("%v is not of format 'owner/repository'", repositoryName) + } + + return splits[0], splits[1], nil +} + +func getLicense(rI *github.Repository) string { + if licenseName := rI.GetLicense().GetName(); licenseName != "" { + return licenseName + } + + return "None" +} + +func getTags(repositoryInfo *github.Repository) map[string]string { + return map[string]string{ + "owner": repositoryInfo.GetOwner().GetLogin(), + "name": repositoryInfo.GetName(), + "language": repositoryInfo.GetLanguage(), + "license": getLicense(repositoryInfo), + } +} + +func getFields(repositoryInfo *github.Repository) map[string]interface{} { + return map[string]interface{}{ + "stars": repositoryInfo.GetStargazersCount(), + "subscribers": repositoryInfo.GetSubscribersCount(), + "watchers": repositoryInfo.GetWatchersCount(), + "networks": repositoryInfo.GetNetworkCount(), + "forks": repositoryInfo.GetForksCount(), + "open_issues": repositoryInfo.GetOpenIssuesCount(), + "size": repositoryInfo.GetSize(), + } +} + +/* +func init() { + inputs.Add("github", func() telegraf.Input { + return &GitHub{ + HTTPTimeout: time.Second * 5, + } + }) +} +*/ diff --git a/src/modules/monapi/plugins/github/github_test.go b/src/modules/monapi/plugins/github/github_test.go new file mode 100644 index 00000000..7803558f --- /dev/null +++ b/src/modules/monapi/plugins/github/github_test.go @@ -0,0 +1,140 @@ +package github + +import ( + "net/http" + "reflect" + "testing" + + gh "github.com/google/go-github/v32/github" + "github.com/stretchr/testify/require" +) + +func TestNewGithubClient(t *testing.T) { + httpClient := &http.Client{} + g := &GitHub{} + client, err := g.newGithubClient(httpClient) + require.NoError(t, err) + require.Contains(t, client.BaseURL.String(), "api.github.com") + g.EnterpriseBaseURL = "api.example.com/" + enterpriseClient, err := g.newGithubClient(httpClient) + require.NoError(t, err) + require.Contains(t, enterpriseClient.BaseURL.String(), "api.example.com") +} + +func TestSplitRepositoryNameWithWorkingExample(t *testing.T) { + var validRepositoryNames = []struct { + fullName string + owner string + repository string + }{ + {"influxdata/telegraf", "influxdata", "telegraf"}, + {"influxdata/influxdb", "influxdata", "influxdb"}, + {"rawkode/saltstack-dotfiles", "rawkode", "saltstack-dotfiles"}, + } + + for _, tt := range validRepositoryNames { + t.Run(tt.fullName, func(t *testing.T) { + owner, repository, _ := splitRepositoryName(tt.fullName) + + require.Equal(t, tt.owner, owner) + require.Equal(t, tt.repository, repository) + }) + } +} + +func TestSplitRepositoryNameWithNoSlash(t *testing.T) { + var invalidRepositoryNames = []string{ + "influxdata-influxdb", + } + + for _, tt := range invalidRepositoryNames { + t.Run(tt, func(t *testing.T) { + _, _, err := splitRepositoryName(tt) + + require.Error(t, err) + }) + } +} + +func TestGetLicenseWhenExists(t *testing.T) { + licenseName := "MIT" + license := gh.License{Name: &licenseName} + repository := gh.Repository{License: &license} + + getLicenseReturn := getLicense(&repository) + + require.Equal(t, "MIT", getLicenseReturn) +} + +func TestGetLicenseWhenMissing(t *testing.T) { + repository := gh.Repository{} + + getLicenseReturn := getLicense(&repository) + + require.Equal(t, "None", getLicenseReturn) +} + +func TestGetTags(t *testing.T) { + licenseName := "MIT" + license := gh.License{Name: &licenseName} + + ownerName := "influxdata" + owner := gh.User{Login: &ownerName} + + fullName := "influxdata/influxdb" + repositoryName := "influxdb" + + language := "Go" + + repository := gh.Repository{ + FullName: &fullName, + Name: &repositoryName, + License: &license, + Owner: &owner, + Language: &language, + } + + getTagsReturn := getTags(&repository) + + correctTagsReturn := map[string]string{ + "owner": ownerName, + "name": repositoryName, + "language": language, + "license": licenseName, + } + + require.Equal(t, true, reflect.DeepEqual(getTagsReturn, correctTagsReturn)) +} + +func TestGetFields(t *testing.T) { + stars := 1 + forks := 2 + openIssues := 3 + size := 4 + subscribers := 5 + watchers := 6 + + repository := gh.Repository{ + StargazersCount: &stars, + ForksCount: &forks, + OpenIssuesCount: &openIssues, + Size: &size, + NetworkCount: &forks, + SubscribersCount: &subscribers, + WatchersCount: &watchers, + } + + getFieldsReturn := getFields(&repository) + + correctFieldReturn := make(map[string]interface{}) + + correctFieldReturn["stars"] = 1 + correctFieldReturn["forks"] = 2 + correctFieldReturn["networks"] = 2 + correctFieldReturn["open_issues"] = 3 + correctFieldReturn["size"] = 4 + correctFieldReturn["subscribers"] = 5 + correctFieldReturn["watchers"] = 6 + + require.Equal(t, true, reflect.DeepEqual(getFieldsReturn, correctFieldReturn)) +} diff --git a/src/modules/monapi/plugins/log/log.go b/src/modules/monapi/plugins/log/log.go new file mode 100644 index 00000000..06b7c362 --- /dev/null +++ b/src/modules/monapi/plugins/log/log.go @@ -0,0 +1,147 @@ +package log + +import ( + "encoding/json" + "errors" + "fmt" + + "github.com/didi/nightingale/src/models" + "github.com/didi/nightingale/src/modules/monapi/collector" + "github.com/influxdata/telegraf" +) + +func init() { + collector.CollectorRegister(&LogCollector{}) +} + +type LogCollector struct{} + +func (p LogCollector) Name() string { return "log" } +func (p LogCollector) Category() collector.Category { return collector.LocalCategory } +func (p LogCollector) Template() (interface{}, error) { return nil, nil } +func (p LogCollector) TelegrafInput(*models.CollectRule) (telegraf.Input, error) { + return nil, errors.New("unsupported") +} + +func (p LogCollector) Get(id int64) (interface{}, error) { + collect := new(models.LogCollect) + has, err := models.DB["mon"].Where("id = ?", id).Get(collect) + if !has { + return nil, err + } + return collect, err +} + +func (p LogCollector) Gets(nids []int64) (ret []interface{}, err error) { + collects := []models.LogCollect{} + err = models.DB["mon"].In("nid", nids).Find(&collects) + for _, c := range collects { + c.Decode() + ret = append(ret, c) + } + return ret, err +} + +func (p LogCollector) GetByNameAndNid(name string, nid int64) (interface{}, error) { + collect := new(models.LogCollect) + has, err := models.DB["mon"].Where("name = ? and nid = ?", name, nid).Get(collect) + if !has { + return nil, err + } + return collect, err +} + +func (p LogCollector) Create(data []byte, username string) error { + collector := new(models.LogCollect) + + err := json.Unmarshal(data, collector) + if err != nil { + return fmt.Errorf("unmarshal body %s err:%v", string(data), err) + } + + can, err := models.UsernameCandoNodeOp(username, "mon_collect_create", collector.Nid) + if err != nil { + return err + } + if !can { + return fmt.Errorf("permission deny") + } + + collector.Encode() + collector.Creator = username + collector.LastUpdator = username + + nid := collector.Nid + name := collector.Name + + old, err := p.GetByNameAndNid(name, nid) + if err != nil { + return err + } + if old != nil { + return fmt.Errorf("同节点下策略名称 %s 已存在", name) + } + return models.CreateCollect(p.Name(), username, collector) +} + +func (p LogCollector) Update(data []byte, username string) error { + collector := new(models.LogCollect) + + err := json.Unmarshal(data, collector) + if err != nil { + return fmt.Errorf("unmarshal body %s err:%v", string(data), err) + } + + can, err := models.UsernameCandoNodeOp(username, "mon_collect_modify", collector.Nid) + if err != nil { + return err + } + if !can { + return fmt.Errorf("permission deny") + } + + collector.Encode() + nid := collector.Nid + name := collector.Name + + //校验采集是否存在 + obj, err := p.Get(collector.Id) //id找不到的情况 + if err != nil { + return fmt.Errorf("采集不存在 type:%s id:%d", p.Name(), collector.Id) + } + + tmpId := obj.(*models.LogCollect).Id + if tmpId == 0 { + return fmt.Errorf("采集不存在 type:%s id:%d", p.Name(), collector.Id) + } + + collector.Creator = username + collector.LastUpdator = username + + old, err := p.GetByNameAndNid(name, nid) + if err != nil { + return err + } + if old != nil && tmpId != old.(*models.LogCollect).Id { + return fmt.Errorf("同节点下策略名称 %s 已存在", name) + } + + return collector.Update() +} + +func (p LogCollector) Delete(id int64, username string) error { + tmp, err := p.Get(id) //id找不到的情况 + if err != nil { + return fmt.Errorf("采集不存在 type:%s id:%d", p.Name(), id) + } + nid := tmp.(*models.LogCollect).Nid + can, err := models.UsernameCandoNodeOp(username, "mon_collect_delete", int64(nid)) + if err != nil { + return err + } + if !can { + return fmt.Errorf("permission deny") + } + + return models.DeleteCollectById(p.Name(), username, id) +} diff --git a/src/modules/monapi/plugins/mongodb/mongodb.go b/src/modules/monapi/plugins/mongodb/mongodb.go new file mode 100644 index 00000000..f64b3f35 --- /dev/null +++ b/src/modules/monapi/plugins/mongodb/mongodb.go @@ -0,0 +1,38 @@ +package redis + +import ( + "github.com/didi/nightingale/src/modules/monapi/collector" + "github.com/influxdata/telegraf" + "github.com/influxdata/telegraf/plugins/inputs/redis" +) + +func init() { + collector.CollectorRegister(NewRedisCollector()) // for monapi +} + +type RedisCollector struct { + *collector.BaseCollector +} + +func NewRedisCollector() *RedisCollector { + return &RedisCollector{BaseCollector: collector.NewBaseCollector( + "redis", + collector.RemoteCategory, + func() interface{} { return &RedisRule{} }, + )} +} + +type RedisRule struct { +} + +func (p *RedisRule) Validate() error { + return nil +} + +func (p *RedisRule) TelegrafInput() (telegraf.Input, error) { + if err := p.Validate(); err != nil { + return nil, err + } + + return &redis.Redis{}, nil +} diff --git a/src/modules/monapi/plugins/mysql/mysql.go b/src/modules/monapi/plugins/mysql/mysql.go new file mode 100644 index 00000000..b17de5f9 --- /dev/null +++ b/src/modules/monapi/plugins/mysql/mysql.go @@ -0,0 +1,86 @@ +package mysql + +import ( + "fmt" + + "github.com/didi/nightingale/src/modules/monapi/collector" + "github.com/influxdata/telegraf" + "github.com/influxdata/telegraf/plugins/inputs/mysql" +) + +func init() { + collector.CollectorRegister(NewMysqlCollector()) // for monapi +} + +type MysqlCollector struct { + *collector.BaseCollector +} + +func NewMysqlCollector() *MysqlCollector { + return &MysqlCollector{BaseCollector: collector.NewBaseCollector( + "mysql", + collector.RemoteCategory, + func() interface{} { return &MysqlRule{} }, + )} +} + +type MysqlRule struct { + Servers []string `label:"Servers" json:"servers,required" description:"specify servers via a url matching\n[username[:password]@][protocol[(address)]]/[?tls=[true|false|skip-verify|custom]]\nsee https://github.com/go-sql-driver/mysql#dsn-data-source-name" example:"servers = ['user:passwd@tcp(127.0.0.1:3306)/?tls=false']\nservers = ["user@tcp(127.0.0.1:3306)/?tls=false"]"` + PerfEventsStatementsDigestTextLimit int64 `label:"-" json:"-"` + PerfEventsStatementsLimit int64 `label:"-" json:"-"` + PerfEventsStatementsTimeLimit int64 `label:"-" json:"-"` + TableSchemaDatabases []string `label:"Databases" json:"table_schema_databases" description:"if the list is empty, then metrics are gathered from all database tables"` + GatherProcessList bool `label:"Process List" json:"gather_process_list" description:"gather thread state counts from INFORMATION_SCHEMA.PROCESSLIST"` + GatherUserStatistics bool `label:"User Statistics" json:"gather_user_statistics" description:"gather user statistics from INFORMATION_SCHEMA.USER_STATISTICS"` + GatherInfoSchemaAutoInc bool `label:"Auto Increment" json:"gather_info_schema_auto_inc" description:"gather auto_increment columns and max values from information schema"` + GatherInnoDBMetrics bool `label:"Innodb Metrics" json:"gather_innodb_metrics" description:"gather metrics from INFORMATION_SCHEMA.INNODB_METRICS"` + GatherSlaveStatus bool `label:"Slave Status" json:"gather_slave_status" description:"gather metrics from SHOW SLAVE STATUS command output"` + GatherBinaryLogs bool `label:"Binary Logs" json:"gather_binary_logs" description:"gather metrics from SHOW BINARY LOGS command output"` + GatherTableIOWaits bool `label:"Table IO Waits" json:"gather_table_io_waits" description:"gather metrics from PERFORMANCE_SCHEMA.TABLE_IO_WAITS_SUMMARY_BY_TABLE"` + GatherTableLockWaits bool `label:"Table Lock Waits" json:"gather_table_lock_waits" description:"gather metrics from PERFORMANCE_SCHEMA.TABLE_LOCK_WAITS"` + GatherIndexIOWaits bool `label:"Index IO Waits" json:"gather_index_io_waits" description:"gather metrics from PERFORMANCE_SCHEMA.TABLE_IO_WAITS_SUMMARY_BY_INDEX_USAGE"` + GatherEventWaits bool `label:"Event Waits" json:"gather_event_waits" description:"gather metrics from PERFORMANCE_SCHEMA.EVENT_WAITS"` + GatherTableSchema bool `label:"Tables" json:"gather_table_schema" description:"gather metrics from INFORMATION_SCHEMA.TABLES for databases provided above list"` + GatherFileEventsStats bool `label:"File Events Stats" json:"gather_file_events_stats" description:"gather metrics from PERFORMANCE_SCHEMA.FILE_SUMMARY_BY_EVENT_NAME"` + GatherPerfEventsStatements bool `label:"Perf Events Statements" json:"gather_perf_events_statements" description:"gather metrics from PERFORMANCE_SCHEMA.EVENTS_STATEMENTS_SUMMARY_BY_DIGEST"` + GatherGlobalVars bool `label:"-" json:"-"` + IntervalSlow string `label:"Interval Slow" json:"interval_slow" desc:"Some queries we may want to run less often (such as SHOW GLOBAL VARIABLES)" example:"interval_slow = '30m'" json:"-"` + MetricVersion int `label:"-" json:"-"` +} + +func (p *MysqlRule) Validate() error { + if len(p.Servers) == 0 || p.Servers[0] == "" { + return fmt.Errorf("mysql.rule.servers must be set") + } + return nil +} + +func (p *MysqlRule) TelegrafInput() (telegraf.Input, error) { + if err := p.Validate(); err != nil { + return nil, err + } + + return &mysql.Mysql{ + Servers: p.Servers, + PerfEventsStatementsDigestTextLimit: 120, + PerfEventsStatementsLimit: 250, + PerfEventsStatementsTimeLimit: 86400, + TableSchemaDatabases: p.TableSchemaDatabases, + GatherProcessList: p.GatherProcessList, + GatherUserStatistics: p.GatherUserStatistics, + GatherInfoSchemaAutoInc: p.GatherInfoSchemaAutoInc, + GatherInnoDBMetrics: p.GatherInnoDBMetrics, + GatherSlaveStatus: p.GatherSlaveStatus, + GatherBinaryLogs: p.GatherBinaryLogs, + GatherTableIOWaits: p.GatherTableIOWaits, + GatherTableLockWaits: p.GatherTableLockWaits, + GatherIndexIOWaits: p.GatherIndexIOWaits, + GatherEventWaits: p.GatherEventWaits, + GatherTableSchema: p.GatherTableSchema, + GatherFileEventsStats: p.GatherFileEventsStats, + GatherPerfEventsStatements: p.GatherPerfEventsStatements, + GatherGlobalVars: true, + IntervalSlow: "0m", + MetricVersion: 2, + }, nil +} diff --git a/src/modules/monapi/plugins/plugin/plugin.go b/src/modules/monapi/plugins/plugin/plugin.go new file mode 100644 index 00000000..8a745c25 --- /dev/null +++ b/src/modules/monapi/plugins/plugin/plugin.go @@ -0,0 +1,144 @@ +package collector + +import ( + "encoding/json" + "errors" + "fmt" + + "github.com/didi/nightingale/src/models" + "github.com/didi/nightingale/src/modules/monapi/collector" + "github.com/influxdata/telegraf" +) + +func init() { + collector.CollectorRegister(&PluginCollector{}) +} + +type PluginCollector struct{} + +func (p PluginCollector) Name() string { return "plugin" } +func (p PluginCollector) Category() collector.Category { return collector.LocalCategory } +func (p PluginCollector) Template() (interface{}, error) { return nil, nil } +func (p PluginCollector) TelegrafInput(*models.CollectRule) (telegraf.Input, error) { + return nil, errors.New("unsupported") +} + +func (p PluginCollector) Get(id int64) (interface{}, error) { + collect := new(models.PluginCollect) + has, err := models.DB["mon"].Where("id = ?", id).Get(collect) + if !has { + return nil, err + } + return collect, err +} + +func (p PluginCollector) Gets(nids []int64) (ret []interface{}, err error) { + collects := []models.PluginCollect{} + err = models.DB["mon"].In("nid", nids).Find(&collects) + for _, c := range collects { + ret = append(ret, c) + } + return ret, err +} + +func (p PluginCollector) GetByNameAndNid(name string, nid int64) (interface{}, error) { + collect := new(models.PluginCollect) + has, err := models.DB["mon"].Where("name = ? and nid = ?", name, nid).Get(collect) + if !has { + return nil, err + } + return collect, err +} + +func (p PluginCollector) Create(data []byte, username string) error { + collect := new(models.PluginCollect) + + err := json.Unmarshal(data, collect) + if err != nil { + return fmt.Errorf("unmarshal body %s err:%v", string(data), err) + } + + can, err := models.UsernameCandoNodeOp(username, "mon_collect_create", collect.Nid) + if err != nil { + return err + } + if !can { + return fmt.Errorf("permission deny") + } + + collect.Creator = username + collect.LastUpdator = username + + nid := collect.Nid + name := collect.Name + + old, err := p.GetByNameAndNid(name, nid) + if err != nil { + return err + } + if old != nil { + return fmt.Errorf("同节点下策略名称 %s 已存在", name) + } + return models.CreateCollect(p.Name(), username, collect) +} + +func (p PluginCollector) Update(data []byte, username string) error { + collect := new(models.PluginCollect) + + err := json.Unmarshal(data, collect) + if err != nil { + return fmt.Errorf("unmarshal body %s err:%v", string(data), err) + } + + can, err := models.UsernameCandoNodeOp(username, "mon_collect_modify", collect.Nid) + if err != nil { + return err + } + if !can { + return fmt.Errorf("permission deny") + } + + nid := collect.Nid + name := collect.Name + + //校验采集是否存在 + obj, err := p.Get(collect.Id) //id找不到的情况 + if err != nil { + return fmt.Errorf("采集不存在 type:%s id:%d", p.Name(), collect.Id) + } + + tmpId := obj.(*models.PluginCollect).Id + if tmpId == 0 { + return fmt.Errorf("采集不存在 type:%s id:%d", p.Name(), collect.Id) + } + + collect.Creator = username + collect.LastUpdator = username + + old, err := p.GetByNameAndNid(name, nid) + if err != nil { + return err + } + if old != nil && tmpId != old.(*models.PluginCollect).Id { + return fmt.Errorf("同节点下策略名称 %s 已存在", name) + } + + return collect.Update() +} + +func (p PluginCollector) Delete(id int64, username string) error { + tmp, err := p.Get(id) //id找不到的情况 + if err != nil { + return fmt.Errorf("采集不存在 type:%s id:%d", p.Name(), id) + } + nid := tmp.(*models.PluginCollect).Nid + can, err := models.UsernameCandoNodeOp(username, "mon_collect_delete", int64(nid)) + if err != nil { + return err + } + if !can { + return fmt.Errorf("permission deny") + } + + return models.DeleteCollectById(p.Name(), username, id) +} diff --git a/src/modules/monapi/plugins/port/port.go b/src/modules/monapi/plugins/port/port.go new file mode 100644 index 00000000..e63b24ff --- /dev/null +++ b/src/modules/monapi/plugins/port/port.go @@ -0,0 +1,144 @@ +package collector + +import ( + "encoding/json" + "errors" + "fmt" + + "github.com/didi/nightingale/src/models" + "github.com/didi/nightingale/src/modules/monapi/collector" + "github.com/influxdata/telegraf" +) + +func init() { + collector.CollectorRegister(&PortCollector{}) +} + +type PortCollector struct{} + +func (p PortCollector) Name() string { return "port" } +func (p PortCollector) Category() collector.Category { return collector.LocalCategory } +func (p PortCollector) Template() (interface{}, error) { return nil, nil } +func (p PortCollector) TelegrafInput(*models.CollectRule) (telegraf.Input, error) { + return nil, errors.New("unsupported") +} + +func (p PortCollector) Get(id int64) (interface{}, error) { + collect := new(models.PortCollect) + has, err := models.DB["mon"].Where("id = ?", id).Get(collect) + if !has { + return nil, err + } + return collect, err +} + +func (p PortCollector) Gets(nids []int64) (ret []interface{}, err error) { + collects := []models.PortCollect{} + err = models.DB["mon"].In("nid", nids).Find(&collects) + for _, c := range collects { + ret = append(ret, c) + } + return ret, err +} + +func (p PortCollector) GetByNameAndNid(name string, nid int64) (interface{}, error) { + collect := new(models.PortCollect) + has, err := models.DB["mon"].Where("name = ? and nid = ?", name, nid).Get(collect) + if !has { + return nil, err + } + return collect, err +} + +func (p PortCollector) Create(data []byte, username string) error { + collect := new(models.PortCollect) + + err := json.Unmarshal(data, collect) + if err != nil { + return fmt.Errorf("unmarshal body %s err:%v", string(data), err) + } + + can, err := models.UsernameCandoNodeOp(username, "mon_collect_create", collect.Nid) + if err != nil { + return err + } + if !can { + return fmt.Errorf("permission deny") + } + + collect.Creator = username + collect.LastUpdator = username + + nid := collect.Nid + name := collect.Name + + old, err := p.GetByNameAndNid(name, nid) + if err != nil { + return err + } + if old != nil { + return fmt.Errorf("同节点下策略名称 %s 已存在", name) + } + return models.CreateCollect(p.Name(), username, collect) +} + +func (p PortCollector) Update(data []byte, username string) error { + collect := new(models.PortCollect) + + err := json.Unmarshal(data, collect) + if err != nil { + return fmt.Errorf("unmarshal body %s err:%v", string(data), err) + } + + can, err := models.UsernameCandoNodeOp(username, "mon_collect_modify", collect.Nid) + if err != nil { + return err + } + if !can { + return fmt.Errorf("permission deny") + } + + nid := collect.Nid + name := collect.Name + + //校验采集是否存在 + obj, err := p.Get(collect.Id) //id找不到的情况 + if err != nil { + return fmt.Errorf("采集不存在 type:%s id:%d", p.Name(), collect.Id) + } + + tmpId := obj.(*models.PortCollect).Id + if tmpId == 0 { + return fmt.Errorf("采集不存在 type:%s id:%d", p.Name(), collect.Id) + } + + collect.Creator = username + collect.LastUpdator = username + + old, err := p.GetByNameAndNid(name, nid) + if err != nil { + return err + } + if old != nil && tmpId != old.(*models.PortCollect).Id { + return fmt.Errorf("同节点下策略名称 %s 已存在", name) + } + + return collect.Update() +} + +func (p PortCollector) Delete(id int64, username string) error { + tmp, err := p.Get(id) //id找不到的情况 + if err != nil { + return fmt.Errorf("采集不存在 type:%s id:%d", p.Name(), id) + } + nid := tmp.(*models.PortCollect).Nid + can, err := models.UsernameCandoNodeOp(username, "mon_collect_delete", int64(nid)) + if err != nil { + return err + } + if !can { + return fmt.Errorf("permission deny") + } + + return models.DeleteCollectById(p.Name(), username, id) +} diff --git a/src/modules/monapi/plugins/proc/proc.go b/src/modules/monapi/plugins/proc/proc.go new file mode 100644 index 00000000..2303c437 --- /dev/null +++ b/src/modules/monapi/plugins/proc/proc.go @@ -0,0 +1,144 @@ +package collector + +import ( + "encoding/json" + "errors" + "fmt" + + "github.com/didi/nightingale/src/models" + "github.com/didi/nightingale/src/modules/monapi/collector" + "github.com/influxdata/telegraf" +) + +func init() { + collector.CollectorRegister(&ProcCollector{}) +} + +type ProcCollector struct{} + +func (p ProcCollector) Name() string { return "proc" } +func (p ProcCollector) Category() collector.Category { return collector.LocalCategory } +func (p ProcCollector) Template() (interface{}, error) { return nil, nil } +func (p ProcCollector) TelegrafInput(*models.CollectRule) (telegraf.Input, error) { + return nil, errors.New("unsupported") +} + +func (p ProcCollector) Get(id int64) (interface{}, error) { + collect := new(models.ProcCollect) + has, err := models.DB["mon"].Where("id = ?", id).Get(collect) + if !has { + return nil, err + } + return collect, err +} + +func (p ProcCollector) Gets(nids []int64) (ret []interface{}, err error) { + collects := []models.ProcCollect{} + err = models.DB["mon"].In("nid", nids).Find(&collects) + for _, c := range collects { + ret = append(ret, c) + } + return ret, err +} + +func (p ProcCollector) GetByNameAndNid(name string, nid int64) (interface{}, error) { + collect := new(models.ProcCollect) + has, err := models.DB["mon"].Where("name = ? and nid = ?", name, nid).Get(collect) + if !has { + return nil, err + } + return collect, err +} + +func (p ProcCollector) Create(data []byte, username string) error { + collect := new(models.ProcCollect) + + err := json.Unmarshal(data, collect) + if err != nil { + return fmt.Errorf("unmarshal body %s err:%v", string(data), err) + } + + can, err := models.UsernameCandoNodeOp(username, "mon_collect_create", collect.Nid) + if err != nil { + return err + } + if !can { + return fmt.Errorf("permission deny") + } + + collect.Creator = username + collect.LastUpdator = username + + nid := collect.Nid + name := collect.Name + + old, err := p.GetByNameAndNid(name, nid) + if err != nil { + return err + } + if old != nil { + return fmt.Errorf("同节点下策略名称 %s 已存在", name) + } + return models.CreateCollect(p.Name(), username, collect) +} + +func (p ProcCollector) Update(data []byte, username string) error { + collect := new(models.ProcCollect) + + err := json.Unmarshal(data, collect) + if err != nil { + return fmt.Errorf("unmarshal body %s err:%v", string(data), err) + } + + can, err := models.UsernameCandoNodeOp(username, "mon_collect_modify", collect.Nid) + if err != nil { + return err + } + if !can { + return fmt.Errorf("permission deny") + } + + nid := collect.Nid + name := collect.Name + + //校验采集是否存在 + obj, err := p.Get(collect.Id) //id找不到的情况 + if err != nil { + return fmt.Errorf("采集不存在 type:%s id:%d", p.Name(), collect.Id) + } + + tmpId := obj.(*models.ProcCollect).Id + if tmpId == 0 { + return fmt.Errorf("采集不存在 type:%s id:%d", p.Name(), collect.Id) + } + + collect.Creator = username + collect.LastUpdator = username + + old, err := p.GetByNameAndNid(name, nid) + if err != nil { + return err + } + if old != nil && tmpId != old.(*models.ProcCollect).Id { + return fmt.Errorf("同节点下策略名称 %s 已存在", name) + } + + return collect.Update() +} + +func (p ProcCollector) Delete(id int64, username string) error { + tmp, err := p.Get(id) //id找不到的情况 + if err != nil { + return fmt.Errorf("采集不存在 type:%s id:%d", p.Name(), id) + } + nid := tmp.(*models.ProcCollect).Nid + can, err := models.UsernameCandoNodeOp(username, "mon_collect_delete", int64(nid)) + if err != nil { + return err + } + if !can { + return fmt.Errorf("permission deny") + } + + return models.DeleteCollectById(p.Name(), username, id) +} diff --git a/src/modules/monapi/plugins/redis/redis.go b/src/modules/monapi/plugins/redis/redis.go new file mode 100644 index 00000000..99553cf8 --- /dev/null +++ b/src/modules/monapi/plugins/redis/redis.go @@ -0,0 +1,63 @@ +package redis + +import ( + "github.com/didi/nightingale/src/modules/monapi/collector" + "github.com/influxdata/telegraf" + "github.com/influxdata/telegraf/plugins/inputs/redis" +) + +func init() { + collector.CollectorRegister(NewRedisCollector()) // for monapi +} + +type RedisCollector struct { + *collector.BaseCollector +} + +func NewRedisCollector() *RedisCollector { + return &RedisCollector{BaseCollector: collector.NewBaseCollector( + "redis", + collector.RemoteCategory, + func() interface{} { return &RedisRule{} }, + )} +} + +type RedisCommand struct { + Command []string `label:"Command" json:"command,required" description:"" ` + Field string `label:"Field" json:"field,required" description:"metric name"` + Type string `label:"Type" json:"type" description:"integer|string|float"` +} + +type RedisRule struct { + Servers []string `label:"Servers" json:"servers,required" description:"If no servers are specified, then localhost is used as the host." example:"tcp://localhost:6379"` + Commands []*RedisCommand `label:"Commands" json:"commands" description:"Optional. Specify redis commands to retrieve values"` + Password string `label:"Password" json:"password" description:"specify server password"` +} + +func (p *RedisRule) Validate() error { + return nil +} + +func (p *RedisRule) TelegrafInput() (telegraf.Input, error) { + if err := p.Validate(); err != nil { + return nil, err + } + + commands := make([]*redis.RedisCommand, len(p.Commands)) + for i, c := range p.Commands { + cmd := &redis.RedisCommand{ + Field: c.Field, + Type: c.Type, + } + for _, v := range c.Command { + cmd.Command = append(cmd.Command, v) + } + commands[i] = cmd + } + + return &redis.Redis{ + Servers: p.Servers, + Commands: commands, + Password: p.Password, + }, nil +} diff --git a/src/modules/monapi/scache/collectrule.go b/src/modules/monapi/scache/collectrule.go new file mode 100644 index 00000000..9a0ff70d --- /dev/null +++ b/src/modules/monapi/scache/collectrule.go @@ -0,0 +1,210 @@ +package scache + +import ( + "context" + "encoding/json" + "fmt" + "strconv" + "sync" + "time" + + "github.com/didi/nightingale/src/common/report" + "github.com/didi/nightingale/src/models" + "github.com/didi/nightingale/src/modules/monapi/config" + "github.com/toolkits/pkg/consistent" + "github.com/toolkits/pkg/logger" +) + +type collectRuleCache struct { + sync.RWMutex + Region []string + Data map[string][]*models.CollectRule // map: node Identity + HashRing map[string]*ConsistentHashRing // map: region +} + +func NewCollectRuleCache() *collectRuleCache { + return &collectRuleCache{ + Region: config.Get().Region, + Data: make(map[string][]*models.CollectRule), + HashRing: make(map[string]*ConsistentHashRing), + } + +} + +func (p *collectRuleCache) Start(ctx context.Context) { + go func() { + p.initHashRing() + + p.syncPlacement() + go p.syncPlacementLoop(ctx) + + p.syncCollectRules() + go p.syncCollectRulesLoop(ctx) + }() +} + +func (p *collectRuleCache) initHashRing() { + for _, region := range p.Region { + p.HashRing[region] = NewConsistentHashRing(int32(config.DetectorReplicas), []string{}) + } +} + +func (p *collectRuleCache) GetBy(node string) []*models.CollectRule { + p.RLock() + defer p.RUnlock() + + return p.Data[node] +} + +func (p *collectRuleCache) Set(node string, rules []*models.CollectRule) { + p.Lock() + defer p.Unlock() + + p.Data[node] = rules + return +} + +func (p *collectRuleCache) SetAll(data map[string][]*models.CollectRule) { + p.Lock() + defer p.Unlock() + + p.Data = data + return +} + +func (p *collectRuleCache) GetAll() []*models.CollectRule { + p.RLock() + defer p.RUnlock() + + data := []*models.CollectRule{} + for nodeId, rules := range p.Data { + logger.Debugf("get nodeId %s rules %d", nodeId, len(rules)) + for _, s := range rules { + data = append(data, s) + } + } + + return data +} + +func (p *collectRuleCache) syncCollectRulesLoop(ctx context.Context) { + t1 := time.NewTicker(time.Duration(CHECK_INTERVAL) * time.Second) + defer t1.Stop() + + for { + select { + case <-ctx.Done(): + return + case <-t1.C: + p.syncCollectRules() + } + } +} + +func str(in interface{}) string { + b, _ := json.Marshal(in) + return string(b) +} + +func (p *collectRuleCache) syncCollectRules() { + rules, err := models.GetCollectRules() + if err != nil { + logger.Warningf("get log collectRules err:%v", err) + } + + logger.Debugf("get collectRules %d %s", len(rules), str(rules)) + + rulesMap := make(map[string][]*models.CollectRule) + for _, rule := range rules { + if _, exists := p.HashRing[rule.Region]; !exists { + logger.Warningf("get node err, hash ring do noe exists %v", rule) + continue + } + + node, err := p.HashRing[rule.Region].GetNode(strconv.FormatInt(rule.Id, 10)) + if err != nil { + logger.Warningf("get node err:%v %v", err, rule) + continue + } + key := node + if _, exists := rulesMap[key]; exists { + rulesMap[key] = append(rulesMap[key], rule) + } else { + rulesMap[key] = []*models.CollectRule{rule} + } + + } + + CollectRuleCache.SetAll(rulesMap) +} + +func (p *collectRuleCache) syncPlacementLoop(ctx context.Context) { + t1 := time.NewTicker(time.Duration(CHECK_INTERVAL) * time.Second) + defer t1.Stop() + + for { + select { + case <-ctx.Done(): + return + case <-t1.C: + p.syncPlacement() + } + } +} + +func (p *collectRuleCache) syncPlacement() error { + instances, err := report.GetAlive("prober", "rdb") + if err != nil { + logger.Warning("get prober err:", err) + return fmt.Errorf("report.GetAlive prober fail: %v", err) + } + + logger.Debugf("get placement %d %s", len(instances), str(instances)) + + if len(instances) < 1 { + logger.Warningf("probers count is zero") + return nil + } + + nodesMap := make(map[string]map[string]struct{}) + for _, d := range instances { + if d.Active { + if _, exists := nodesMap[d.Region]; !exists { + nodesMap[d.Region] = make(map[string]struct{}) + } + nodesMap[d.Region][d.Identity+":"+d.RPCPort] = struct{}{} + } + } + + for region, nodes := range nodesMap { + rehash := false + if _, exists := p.HashRing[region]; !exists { + logger.Warningf("hash ring do not exists %v", region) + continue + } + oldNodes := p.HashRing[region].GetRing().Members() + if len(oldNodes) != len(nodes) { + rehash = true + } else { + for _, node := range oldNodes { + if _, exists := nodes[node]; !exists { + rehash = true + break + } + } + } + + if rehash { + //重建 hash环 + r := consistent.New() + r.NumberOfReplicas = config.DetectorReplicas + for node, _ := range nodes { + r.Add(node) + } + logger.Warningf("detector hash ring rebuild old:%v new:%v", oldNodes, r.Members()) + p.HashRing[region].Set(r) + } + } + + return nil +} diff --git a/src/modules/monapi/scache/init.go b/src/modules/monapi/scache/init.go index 1d2e0a94..28f3dd0a 100644 --- a/src/modules/monapi/scache/init.go +++ b/src/modules/monapi/scache/init.go @@ -1,6 +1,7 @@ package scache import ( + "context" "strconv" "github.com/didi/nightingale/src/common/report" @@ -9,8 +10,9 @@ import ( "github.com/toolkits/pkg/logger" ) +var CollectRuleCache *collectRuleCache var JudgeHashRing *ConsistentHashRing -var ActiveNode = NewNodeMap() +var ActiveJudgeNode = NewNodeMap() const CHECK_INTERVAL = 9 @@ -22,8 +24,10 @@ func Init() { InitJudgeHashRing() - go CheckJudgeNodes() + CollectRuleCache = NewCollectRuleCache() + CollectRuleCache.Start(context.Background()) + go CheckJudgeNodes() go SyncStras() go SyncCollects() go CleanCollectLoop() diff --git a/src/modules/monapi/scache/judge.go b/src/modules/monapi/scache/judge.go index d5cc3f74..9a15563e 100644 --- a/src/modules/monapi/scache/judge.go +++ b/src/modules/monapi/scache/judge.go @@ -40,11 +40,11 @@ func CheckJudge() error { } rehash := false - if ActiveNode.Len() != len(judgeNode) { //scache.ActiveNode中的node数量和新获取的不同,重新rehash + if ActiveJudgeNode.Len() != len(judgeNode) { //scache.ActiveJudgeNode中的node数量和新获取的不同,重新rehash rehash = true } else { for node, instance := range judgeNode { - v, exists := ActiveNode.GetInstanceBy(node) + v, exists := ActiveJudgeNode.GetInstanceBy(node) if !exists || (exists && instance != v) { rehash = true break @@ -52,12 +52,12 @@ func CheckJudge() error { } } if rehash { - ActiveNode.Set(judgeNode) + ActiveJudgeNode.Set(judgeNode) //重建judge hash环 r := consistent.New() r.NumberOfReplicas = config.JudgesReplicas - nodes := ActiveNode.GetNodes() + nodes := ActiveJudgeNode.GetNodes() for _, node := range nodes { r.Add(node) } diff --git a/src/modules/monapi/scache/stra_cache.go b/src/modules/monapi/scache/stra_cache.go index c6d7c296..02ee25eb 100644 --- a/src/modules/monapi/scache/stra_cache.go +++ b/src/modules/monapi/scache/stra_cache.go @@ -46,7 +46,7 @@ func (s *StraCacheMap) GetAll() []*models.Stra { data := []*models.Stra{} for node, stras := range s.Data { - instance, exists := ActiveNode.GetInstanceBy(node) + instance, exists := ActiveJudgeNode.GetInstanceBy(node) if !exists { continue } diff --git a/src/modules/prober/cache/cache.go b/src/modules/prober/cache/cache.go new file mode 100644 index 00000000..9c5f306b --- /dev/null +++ b/src/modules/prober/cache/cache.go @@ -0,0 +1,18 @@ +package cache + +import ( + "context" + + "github.com/didi/nightingale/src/modules/prober/config" +) + +var CollectRule *CollectRuleCache // collectrule.go +var MetricHistory *history // history.go + +func Init(ctx context.Context) error { + CollectRule = NewCollectRuleCache(&config.Config.CollectRule) + CollectRule.start(ctx) + MetricHistory = NewHistory() + InitPluginsConfig(config.Config) + return nil +} diff --git a/src/modules/prober/cache/collect_rule.go b/src/modules/prober/cache/collect_rule.go new file mode 100644 index 00000000..429c68cb --- /dev/null +++ b/src/modules/prober/cache/collect_rule.go @@ -0,0 +1,159 @@ +package cache + +import ( + "context" + "fmt" + "math/rand" + "sync" + "time" + + "github.com/didi/nightingale/src/common/address" + "github.com/didi/nightingale/src/common/identity" + "github.com/didi/nightingale/src/common/report" + "github.com/didi/nightingale/src/models" + "github.com/didi/nightingale/src/modules/prober/config" + "github.com/didi/nightingale/src/toolkits/stats" + "github.com/toolkits/pkg/logger" + "github.com/toolkits/pkg/net/httplib" +) + +type CollectRuleCache struct { + sync.RWMutex + *config.CollectRuleSection + Data map[int64]*models.CollectRule + TS map[int64]int64 + C chan time.Time + timeout time.Duration +} + +func NewCollectRuleCache(cf *config.CollectRuleSection) *CollectRuleCache { + return &CollectRuleCache{ + CollectRuleSection: cf, + Data: make(map[int64]*models.CollectRule), + TS: make(map[int64]int64), + timeout: time.Duration(cf.Timeout) * time.Millisecond, + C: make(chan time.Time, 1), + } +} + +func (p *CollectRuleCache) start(ctx context.Context) error { + go func() { + p.syncCollectRule() + p.syncCollectRuleLoop(ctx) + }() + return nil +} + +func (p *CollectRuleCache) Set(id int64, rule *models.CollectRule) { + p.Lock() + defer p.Unlock() + p.Data[id] = rule + p.TS[id] = time.Now().Unix() +} + +func (p *CollectRuleCache) Get(id int64) (*models.CollectRule, bool) { + p.RLock() + defer p.RUnlock() + + rule, exists := p.Data[id] + return rule, exists +} + +func (p *CollectRuleCache) GetAll() []*models.CollectRule { + p.RLock() + defer p.RUnlock() + var rules []*models.CollectRule + for _, rule := range p.Data { + rules = append(rules, rule) + } + return rules +} + +func (p *CollectRuleCache) Clean() { + p.Lock() + defer p.Unlock() + now := time.Now().Unix() + for id, ts := range p.TS { + if now-ts > 60 { + stats.Counter.Set("collectrule.clean", 1) + delete(p.Data, id) + delete(p.TS, id) + } + } +} + +func (p *CollectRuleCache) syncCollectRuleLoop(ctx context.Context) { + t1 := time.NewTicker(time.Duration(p.UpdateInterval) * time.Millisecond) + defer t1.Stop() + + for { + select { + case <-ctx.Done(): + return + case t := <-t1.C: + if err := p.syncCollectRule(); err != nil { + logger.Errorf("syncCollectRule err %s", err) + } else { + p.C <- t + } + } + } +} + +type collectRulesResp struct { + Data []*models.CollectRule `json:"dat"` + Err string `json:"err"` +} + +func (p *CollectRuleCache) syncCollectRule() error { + addrs := address.GetHTTPAddresses(p.Mod) + if len(addrs) == 0 { + return fmt.Errorf("empty config addr") + } + + var resp collectRulesResp + perm := rand.Perm(len(addrs)) + for i := range perm { + ident, err := identity.GetIdent() + if err != nil { + return fmt.Errorf("getIdent err %s", err) + } + + url := fmt.Sprintf("http://%s/api/mon/collect-rules/endpoints/%s:%s/remote", + addrs[perm[i]], ident, report.Config.RPCPort) + err = httplib.Get(url).SetTimeout(p.timeout).ToJSON(&resp) + if err != nil { + logger.Warningf("get %s collect rule from remote failed, error:%v", url, err) + stats.Counter.Set("collectrule.get.err", 1) + continue + } + + if resp.Err != "" { + logger.Warningf("get collect rule from remote failed, error:%v", resp.Err) + stats.Counter.Set("collectrule.get.err", 1) + continue + } + + if len(resp.Data) > 0 { + break + } + } + + collectRuleCount := len(resp.Data) + stats.Counter.Set("collectrule.count", collectRuleCount) + if collectRuleCount == 0 { //获取策略数为0,不正常,不更新策略缓存 + return fmt.Errorf("clloect rule count is 0") + } + + for _, rule := range resp.Data { + if err := rule.Validate(); err != nil { + logger.Debugf("rule.Validate err %s", err) + continue + } + stats.Counter.Set("collectrule.common", 1) + p.Set(rule.Id, rule) + } + + p.Clean() + return nil +} diff --git a/src/modules/prober/cache/history.go b/src/modules/prober/cache/history.go new file mode 100644 index 00000000..b636e688 --- /dev/null +++ b/src/modules/prober/cache/history.go @@ -0,0 +1,57 @@ +package cache + +import ( + "sync" + "time" + + "github.com/didi/nightingale/src/common/dataobj" +) + +func NewHistory() *history { + h := history{ + Data: make(map[string]dataobj.MetricValue), + } + + go h.Clean() + return &h +} + +type history struct { + sync.RWMutex + Data map[string]dataobj.MetricValue +} + +func (h *history) Set(key string, item dataobj.MetricValue) { + h.Lock() + defer h.Unlock() + h.Data[key] = item +} + +func (h *history) Get(key string) (dataobj.MetricValue, bool) { + h.RLock() + defer h.RUnlock() + + item, exists := h.Data[key] + return item, exists +} + +func (h *history) Clean() { + ticker := time.NewTicker(10 * time.Minute) + for { + select { + case <-ticker.C: + h.clean() + } + } +} + +func (h *history) clean() { + h.Lock() + defer h.Unlock() + now := time.Now().Unix() + for key, item := range h.Data { + if now-item.Timestamp > 10*item.Step { + delete(h.Data, key) + } + } +} diff --git a/src/modules/prober/cache/plugins_config.go b/src/modules/prober/cache/plugins_config.go new file mode 100644 index 00000000..0558069b --- /dev/null +++ b/src/modules/prober/cache/plugins_config.go @@ -0,0 +1,118 @@ +package cache + +import ( + "fmt" + "io/ioutil" + "path/filepath" + + "github.com/didi/nightingale/src/modules/monapi/collector" + "github.com/didi/nightingale/src/modules/prober/config" + "github.com/didi/nightingale/src/modules/prober/expr" + "github.com/influxdata/telegraf" + "github.com/toolkits/pkg/logger" + "gopkg.in/yaml.v2" +) + +type MetricConfig struct { + Name string `yaml:"name"` + Type string `yaml:"type"` + Comment string `yaml:"comment"` + Expr string `yaml:"expr"` + notations expr.Notations `yaml:"-"` +} + +type PluginConfig struct { + Metrics []MetricConfig `metrics` +} + +var ( + metricsConfig map[string]MetricConfig + metricsExpr map[string]map[string]MetricConfig + ignoreConfig bool +) + +func InitPluginsConfig(cf *config.ConfYaml) { + metricsConfig = make(map[string]MetricConfig) + metricsExpr = make(map[string]map[string]MetricConfig) + ignoreConfig = cf.IgnoreConfig + plugins := collector.GetRemoteCollectors() + for _, plugin := range plugins { + metricsExpr[plugin] = make(map[string]MetricConfig) + pluginConfig := PluginConfig{} + + file := filepath.Join(cf.PluginsConfig, plugin+".yml") + b, err := ioutil.ReadFile(file) + if err != nil { + logger.Debugf("readfile %s err %s", plugin, err) + continue + } + + if err := yaml.Unmarshal(b, &pluginConfig); err != nil { + logger.Warningf("yaml.Unmarshal %s err %s", plugin, err) + continue + } + + for _, v := range pluginConfig.Metrics { + if _, ok := metricsConfig[v.Name]; ok { + panic(fmt.Sprintf("plugin %s metrics %s is already exists", plugin, v.Name)) + } + if v.Expr == "" { + // nomore + metricsConfig[v.Name] = v + } else { + err := v.parse() + if err != nil { + panic(fmt.Sprintf("plugin %s metrics %s expr %s parse err %s", + plugin, v.Name, v.Expr, err)) + } + metricsExpr[plugin][v.Name] = v + + } + } + logger.Infof("loaded plugin config %s", file) + } +} + +func (p *MetricConfig) parse() (err error) { + p.notations, err = expr.NewNotations([]byte(p.Expr)) + return +} + +func (p *MetricConfig) Calc(vars map[string]float64) (float64, error) { + return p.notations.Calc(vars) +} + +func Metric(metric string, typ telegraf.ValueType) (c MetricConfig, ok bool) { + c, ok = metricsConfig[metric] + if !ok && !ignoreConfig { + return + } + + if c.Type == "" { + c.Type = metricType(typ) + } + + return +} + +func GetMetricExprs(pluginName string) (c map[string]MetricConfig, ok bool) { + c, ok = metricsExpr[pluginName] + return +} + +func metricType(typ telegraf.ValueType) string { + switch typ { + case telegraf.Counter: + return "COUNTER" + case telegraf.Gauge: + return "GAUGE" + case telegraf.Untyped: + return "GAUGE" + case telegraf.Summary: // TODO + return "SUMMARY" + case telegraf.Histogram: // TODO + return "HISTOGRAM" + default: + return "GAUGE" + } +} diff --git a/src/modules/prober/config/config.go b/src/modules/prober/config/config.go new file mode 100644 index 00000000..a7b492ac --- /dev/null +++ b/src/modules/prober/config/config.go @@ -0,0 +1,94 @@ +package config + +import ( + "bytes" + "fmt" + "strconv" + + "github.com/didi/nightingale/src/common/address" + "github.com/didi/nightingale/src/common/loggeri" + "github.com/didi/nightingale/src/common/report" + + // "github.com/didi/nightingale/src/modules/prober/backend/transfer" + + "github.com/spf13/viper" + "github.com/toolkits/pkg/file" +) + +type ConfYaml struct { + CollectRule CollectRuleSection `yaml:"collectRule"` + Logger loggeri.Config `yaml:"logger"` + Report report.ReportSection `yaml:"report"` + WorkerProcesses int `yaml:"workerProcesses"` + PluginsConfig string `yaml:"pluginsConfig"` + IgnoreConfig bool `yaml:"ignoreConfig"` + HTTP HTTPSection `yaml:"http"` +} + +type CollectRuleSection struct { + Timeout int `yaml:"timeout"` + Token string `yaml:"token"` + UpdateInterval int `yaml:"updateInterval"` + IndexInterval int `yaml:"indexInterval"` + ReportInterval int `yaml:"reportInterval"` + Mod string `yaml:"mod"` +} + +var ( + Config *ConfYaml +) + +type HTTPSection struct { + Mode string `yaml:"mode"` + CookieName string `yaml:"cookieName"` + CookieDomain string `yaml:"cookieDomain"` +} + +func Parse(conf string) error { + bs, err := file.ReadBytes(conf) + if err != nil { + return fmt.Errorf("cannot read yml[%s]: %v", conf, err) + } + + viper.SetConfigType("yaml") + err = viper.ReadConfig(bytes.NewBuffer(bs)) + if err != nil { + return fmt.Errorf("cannot read yml[%s]: %v", conf, err) + } + + viper.SetDefault("http.enabled", true) + + viper.SetDefault("collectRule", map[string]interface{}{ + "updateInterval": 9000, + "indexInterval": 60000, + "timeout": 5000, + "mod": "monapi", + "eventPrefix": "n9e", + }) + + viper.SetDefault("report", map[string]interface{}{ + "mod": "prober", + "enabled": true, + "interval": 4000, + "timeout": 3000, + "api": "api/hbs/heartbeat", + "remark": "", + "region": "default", + }) + + viper.SetDefault("workerProcesses", 5) + + viper.SetDefault("pluginsConfig", "etc/plugins") + + viper.SetDefault("pushUrl", "http://127.0.0.1:2058/v1/push") + + err = viper.Unmarshal(&Config) + if err != nil { + return fmt.Errorf("cannot read yml[%s]: %v\n", conf, err) + } + + Config.Report.HTTPPort = strconv.Itoa(address.GetHTTPPort("prober")) + Config.Report.RPCPort = strconv.Itoa(address.GetRPCPort("prober")) + + return err +} diff --git a/src/modules/prober/core/clients.go b/src/modules/prober/core/clients.go new file mode 100644 index 00000000..789c32d3 --- /dev/null +++ b/src/modules/prober/core/clients.go @@ -0,0 +1,51 @@ +package core + +import ( + "net/rpc" + "sync" +) + +type RpcClientContainer struct { + M map[string]*rpc.Client + sync.RWMutex +} + +var rpcClients *RpcClientContainer + +func InitRpcClients() { + rpcClients = &RpcClientContainer{ + M: make(map[string]*rpc.Client), + } +} + +func (rcc *RpcClientContainer) Get(addr string) *rpc.Client { + rcc.RLock() + defer rcc.RUnlock() + + client, has := rcc.M[addr] + if !has { + return nil + } + + return client +} + +// Put 返回的bool表示affected,确实把自己塞进去了 +func (rcc *RpcClientContainer) Put(addr string, client *rpc.Client) bool { + rcc.Lock() + defer rcc.Unlock() + + oc, has := rcc.M[addr] + if has && oc != nil { + return false + } + + rcc.M[addr] = client + return true +} + +func (rcc *RpcClientContainer) Del(addr string) { + rcc.Lock() + defer rcc.Unlock() + delete(rcc.M, addr) +} diff --git a/src/modules/prober/core/common.go b/src/modules/prober/core/common.go new file mode 100644 index 00000000..b49fdec5 --- /dev/null +++ b/src/modules/prober/core/common.go @@ -0,0 +1,31 @@ +package core + +import ( + "strings" + + "github.com/didi/nightingale/src/common/dataobj" +) + +func NewMetricValue(metric string, val interface{}, dataType string, tags ...string) *dataobj.MetricValue { + mv := dataobj.MetricValue{ + Metric: metric, + ValueUntyped: val, + CounterType: dataType, + } + + size := len(tags) + + if size > 0 { + mv.Tags = strings.Join(tags, ",") + } + + return &mv +} + +func GaugeValue(metric string, val interface{}, tags ...string) *dataobj.MetricValue { + return NewMetricValue(metric, val, "GAUGE", tags...) +} + +func CounterValue(metric string, val interface{}, tags ...string) *dataobj.MetricValue { + return NewMetricValue(metric, val, "COUNTER", tags...) +} diff --git a/src/modules/prober/core/push.go b/src/modules/prober/core/push.go new file mode 100644 index 00000000..e69bb5bd --- /dev/null +++ b/src/modules/prober/core/push.go @@ -0,0 +1,196 @@ +package core + +import ( + "bufio" + "fmt" + "io" + "math/rand" + "net" + "net/rpc" + "reflect" + "time" + + "github.com/toolkits/pkg/logger" + "github.com/ugorji/go/codec" + + "github.com/didi/nightingale/src/common/address" + "github.com/didi/nightingale/src/common/dataobj" + "github.com/didi/nightingale/src/modules/prober/cache" +) + +func Push(metricItems []*dataobj.MetricValue) { + var err error + var items []*dataobj.MetricValue + now := time.Now().Unix() + + for _, item := range metricItems { + // logger.Debugf("->recv:%+v", item) + err = item.CheckValidity(now) + if err != nil { + msg := fmt.Errorf("metric:%v err:%v", item, err) + logger.Warning(msg) + // 如果数据有问题,直接跳过吧,比如mymon采集的到的数据,其实只有一个有问题,剩下的都没问题 + continue + } + if item.CounterType == dataobj.COUNTER { + item = CounterToGauge(item) + if item == nil { + continue + } + } + if item.CounterType == dataobj.SUBTRACT { + item = SubtractToGauge(item) + if item == nil { + continue + } + } + // logger.Debugf("push item: %+v", item) + items = append(items, item) + } + + addrs := address.GetRPCAddresses("transfer") + count := len(addrs) + retry := 0 + for { + for _, i := range rand.Perm(count) { + addr := addrs[i] + reply, err := rpcCall(addr, items) + if err != nil { + logger.Error(err) + continue + } else { + if reply.Msg != "ok" { + err = fmt.Errorf("some item push err: %s", reply.Msg) + logger.Error(err) + } + return + } + } + + time.Sleep(time.Millisecond * 500) + + retry += 1 + if retry == 3 { + break + } + } +} + +func rpcCall(addr string, items []*dataobj.MetricValue) (dataobj.TransferResp, error) { + var reply dataobj.TransferResp + var err error + + client := rpcClients.Get(addr) + if client == nil { + client, err = rpcClient(addr) + if err != nil { + return reply, err + } + affected := rpcClients.Put(addr, client) + if !affected { + defer func() { + // 我尝试把自己这个client塞进map失败,说明已经有一个client塞进去了,那我自己用完了就关闭 + client.Close() + }() + + } + } + + timeout := time.Duration(8) * time.Second + done := make(chan error, 1) + + go func() { + err := client.Call("Transfer.Push", items, &reply) + done <- err + }() + + select { + case <-time.After(timeout): + logger.Warningf("rpc call timeout, transfer addr: %s\n", addr) + rpcClients.Put(addr, nil) + client.Close() + return reply, fmt.Errorf("%s rpc call timeout", addr) + case err := <-done: + if err != nil { + rpcClients.Del(addr) + client.Close() + return reply, fmt.Errorf("%s rpc call done, but fail: %v", addr, err) + } + } + + return reply, nil +} + +func rpcClient(addr string) (*rpc.Client, error) { + conn, err := net.DialTimeout("tcp", addr, time.Second*3) + if err != nil { + err = fmt.Errorf("dial transfer %s fail: %v", addr, err) + logger.Error(err) + return nil, err + } + + var bufConn = struct { + io.Closer + *bufio.Reader + *bufio.Writer + }{conn, bufio.NewReader(conn), bufio.NewWriter(conn)} + + var mh codec.MsgpackHandle + mh.MapType = reflect.TypeOf(map[string]interface{}(nil)) + + rpcCodec := codec.MsgpackSpecRpc.ClientCodec(bufConn, &mh) + client := rpc.NewClientWithCodec(rpcCodec) + return client, nil +} + +func CounterToGauge(item *dataobj.MetricValue) *dataobj.MetricValue { + key := item.PK() + + old, exists := cache.MetricHistory.Get(key) + cache.MetricHistory.Set(key, *item) + + if !exists { + logger.Debugf("not found old item:%v, maybe this is the first item", item) + return nil + } + + if old.Value > item.Value { + logger.Warningf("item:%v old value:%v greater than new value:%v", item, old.Value, item.Value) + return nil + } + + if old.Timestamp >= item.Timestamp { + logger.Warningf("item:%v old timestamp:%v greater than new timestamp:%v", item, old.Timestamp, item.Timestamp) + return nil + } + + item.ValueUntyped = (item.Value - old.Value) / float64(item.Timestamp-old.Timestamp) + item.CounterType = dataobj.GAUGE + return item +} + +func SubtractToGauge(item *dataobj.MetricValue) *dataobj.MetricValue { + key := item.PK() + + old, exists := cache.MetricHistory.Get(key) + cache.MetricHistory.Set(key, *item) + + if !exists { + logger.Debugf("not found old item:%v, maybe this is the first item", item) + return nil + } + + if old.Timestamp >= item.Timestamp { + logger.Warningf("item:%v old timestamp:%v greater than new timestamp:%v", item, old.Timestamp, item.Timestamp) + return nil + } + + if old.Timestamp <= item.Timestamp-2*item.Step { + logger.Warningf("item:%v old timestamp:%v too old <= %v = (new timestamp: %v - 2 * step: %v), maybe some point lost", item, old.Timestamp, item.Timestamp-2*item.Step, item.Timestamp, item.Step) + return nil + } + + item.ValueUntyped = item.Value - old.Value + item.CounterType = dataobj.GAUGE + return item +} diff --git a/src/modules/prober/expr/expr.go b/src/modules/prober/expr/expr.go new file mode 100644 index 00000000..5ca46c69 --- /dev/null +++ b/src/modules/prober/expr/expr.go @@ -0,0 +1,174 @@ +package expr + +import ( + "bytes" + "fmt" + "go/scanner" + "go/token" + "strconv" + + "github.com/toolkits/pkg/logger" +) + +type tokenType int + +const ( + tokenOperator tokenType = iota + tokenVar + tokenConst +) + +type TokenNotation struct { + tokenType tokenType + o token.Token // operator + v string // variable + c float64 // const +} + +type Notations []*TokenNotation + +func (s *Notations) Push(tn *TokenNotation) { *s = append(*s, tn) } +func (s *Notations) Pop() *TokenNotation { n := (*s)[len(*s)-1]; *s = (*s)[:len(*s)-1]; return n } +func (s *Notations) Top() *TokenNotation { return (*s)[len(*s)-1] } +func (s *Notations) Len() int { return len(*s) } + +func (s Notations) String() string { + out := bytes.NewBuffer(nil) + for i := 0; i < len(s); i++ { + tn := s[i] + switch tn.tokenType { + case tokenOperator: + out.WriteString(tn.o.String() + " ") + case tokenVar: + out.WriteString(tn.v + " ") + case tokenConst: + out.WriteString(fmt.Sprintf("%.0f ", tn.c)) + } + } + return out.String() +} + +type StackOp []token.Token + +func (s *StackOp) Push(t token.Token) { *s = append(*s, t) } +func (s *StackOp) Pop() token.Token { n := (*s)[len(*s)-1]; *s = (*s)[:len(*s)-1]; return n } +func (s *StackOp) Top() token.Token { return (*s)[len(*s)-1] } +func (s *StackOp) Len() int { return len(*s) } + +type StackFloat []float64 + +func (s *StackFloat) Push(f float64) { *s = append(*s, f) } +func (s *StackFloat) Pop() float64 { n := (*s)[len(*s)-1]; *s = (*s)[:len(*s)-1]; return n } +func (s *StackFloat) Len() int { return len(*s) } + +func (rpn Notations) Calc(vars map[string]float64) (float64, error) { + var s StackFloat + for i := 0; i < rpn.Len(); i++ { + tn := rpn[i] + switch tn.tokenType { + case tokenVar: + if v, ok := vars[tn.v]; !ok { + return 0, fmt.Errorf("variable %s is not set", tn.v) + } else { + logger.Debugf("get %s %f", tn.v, v) + s.Push(v) + } + case tokenConst: + s.Push(tn.c) + case tokenOperator: + op2 := s.Pop() + op1 := s.Pop() + switch tn.o { + case token.ADD: + s.Push(op1 + op2) + case token.SUB: + s.Push(op1 - op2) + case token.MUL: + s.Push(op1 * op2) + case token.QUO: + s.Push(op1 / op2) + } + } + } + if s.Len() == 1 { + return s[0], nil + } + + return 0, fmt.Errorf("invalid calc, stack len %d expect 1", s.Len()) +} + +// return reverse polish notation stack +func NewNotations(src []byte) (output Notations, err error) { + var scan scanner.Scanner + var s StackOp + + fset := token.NewFileSet() + file := fset.AddFile("", fset.Base(), len(src)) + + scan.Init(file, src, errorHandler, scanner.ScanComments) + var ( + pos token.Pos + tok token.Token + lit string + ) + for { + pos, tok, lit = scan.Scan() + + switch tok { + case token.EOF, token.SEMICOLON: + goto out + case token.INT, token.FLOAT: + c, err := strconv.ParseFloat(lit, 64) + if err != nil { + return nil, fmt.Errorf("parseFloat error %s\t%s\t%q", + fset.Position(pos), tok, lit) + } + output.Push(&TokenNotation{tokenType: tokenConst, c: c}) + case token.IDENT: + output.Push(&TokenNotation{tokenType: tokenVar, v: lit}) + case token.LPAREN: // ( + s.Push(tok) + case token.ADD, token.SUB, token.MUL, token.QUO: // + - * / + opRetry: + if s.Len() == 0 { + s.Push(tok) + } else if op := s.Top(); op == token.LPAREN || priority(tok) > priority(op) { + s.Push(tok) + } else { + output.Push(&TokenNotation{tokenType: tokenOperator, o: s.Pop()}) + goto opRetry + } + case token.RPAREN: // ) + for s.Len() > 0 { + if op := s.Pop(); op == token.LPAREN { + break + } else { + output.Push(&TokenNotation{tokenType: tokenOperator, o: op}) + } + } + default: + return nil, fmt.Errorf("unsupport token %s", tok) + } + } + +out: + for i, l := 0, s.Len(); i < l; i++ { + output.Push(&TokenNotation{tokenType: tokenOperator, o: s.Pop()}) + } + return +} + +func errorHandler(pos token.Position, msg string) { + logger.Errorf("error %s\t%s\n", pos, msg) +} + +func priority(tok token.Token) int { + switch tok { + case token.ADD, token.SUB: + return 1 + case token.MUL, token.QUO: + return 2 + default: + return 0 + } +} diff --git a/src/modules/prober/http/http_server.go b/src/modules/prober/http/http_server.go new file mode 100644 index 00000000..e4c50a3a --- /dev/null +++ b/src/modules/prober/http/http_server.go @@ -0,0 +1,67 @@ +package http + +import ( + "context" + "fmt" + "net/http" + "os" + "strings" + "time" + + "github.com/gin-gonic/gin" + + "github.com/didi/nightingale/src/common/address" + "github.com/didi/nightingale/src/common/middleware" + "github.com/didi/nightingale/src/modules/prober/config" +) + +var srv = &http.Server{ + ReadTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + MaxHeaderBytes: 1 << 20, +} + +func Start() { + c := config.Config + + recoveryMid := middleware.Recovery() + + if strings.ToLower(c.HTTP.Mode) == "release" { + gin.SetMode(gin.ReleaseMode) + middleware.DisableConsoleColor() + } + + r := gin.New() + r.Use(recoveryMid) + + Config(r) + + srv.Addr = address.GetHTTPListen("prober") + srv.Handler = r + + go func() { + fmt.Println("http.listening:", srv.Addr) + if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { + fmt.Printf("listening %s occur error: %s\n", srv.Addr, err) + os.Exit(3) + } + }() +} + +// Shutdown http server +func Shutdown() { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + if err := srv.Shutdown(ctx); err != nil { + fmt.Println("cannot shutdown http server:", err) + os.Exit(2) + } + + // catching ctx.Done(). timeout of 5 seconds. + select { + case <-ctx.Done(): + fmt.Println("shutdown http server timeout of 5 seconds.") + default: + fmt.Println("http server stopped") + } +} diff --git a/src/modules/prober/http/router.go b/src/modules/prober/http/router.go new file mode 100644 index 00000000..e924b27f --- /dev/null +++ b/src/modules/prober/http/router.go @@ -0,0 +1,53 @@ +package http + +import ( + "fmt" + "os" + + "github.com/didi/nightingale/src/modules/prober/cache" + "github.com/gin-contrib/pprof" + "github.com/gin-gonic/gin" +) + +func Config(r *gin.Engine) { + + notLogin := r.Group("/api/rdb") + { + notLogin.GET("/ping", ping) + notLogin.GET("/pid", pid) + notLogin.GET("/addr", addr) + notLogin.GET("/collect-rule/:id", getCollectRule) + // notLogin.POST("/data", getData) + } + + pprof.Register(r, "/api/prober/debug/pprof") +} + +func ping(c *gin.Context) { + c.String(200, "pong") +} + +func addr(c *gin.Context) { + c.String(200, c.Request.RemoteAddr) +} + +func pid(c *gin.Context) { + c.String(200, fmt.Sprintf("%d", os.Getpid())) +} + +func getCollectRule(c *gin.Context) { + rule, _ := cache.CollectRule.Get(urlParamInt64(c, "id")) + renderData(c, rule, nil) +} + +/* +// TODO: get last collect data +func getData(c *gin.Context) { + var input dataobj.JudgeItem + errors.Dangerous(c.ShouldBind(&input)) + pk := input.MD5() + linkedList, _ := cache.HistoryBigMap[pk[0:2]].Get(pk) + data := linkedList.HistoryData() + renderData(c, data, nil) +} +*/ diff --git a/src/modules/prober/http/router_funcs.go b/src/modules/prober/http/router_funcs.go new file mode 100644 index 00000000..2c9afe5d --- /dev/null +++ b/src/modules/prober/http/router_funcs.go @@ -0,0 +1,282 @@ +package http + +import ( + "fmt" + "strconv" + + "github.com/gin-gonic/gin" + "github.com/toolkits/pkg/errors" + + "github.com/didi/nightingale/src/models" + "github.com/didi/nightingale/src/toolkits/i18n" +) + +func dangerous(v interface{}) { + errors.Dangerous(v) +} + +func bomb(format string, a ...interface{}) { + errors.Bomb(i18n.Sprintf(format, a...)) +} + +func bind(c *gin.Context, ptr interface{}) { + dangerous(c.ShouldBindJSON(ptr)) +} + +func urlParamStr(c *gin.Context, field string) string { + val := c.Param(field) + + if val == "" { + bomb("url param[%s] is blank", field) + } + + return val +} + +func urlParamInt64(c *gin.Context, field string) int64 { + strval := urlParamStr(c, field) + intval, err := strconv.ParseInt(strval, 10, 64) + if err != nil { + bomb("cannot convert %s to int64", strval) + } + + return intval +} + +func urlParamInt(c *gin.Context, field string) int { + return int(urlParamInt64(c, field)) +} + +func queryStr(c *gin.Context, key string, defaultVal ...string) string { + val := c.Query(key) + if val != "" { + return val + } + + if len(defaultVal) == 0 { + bomb("query param[%s] is necessary", key) + } + + return defaultVal[0] +} + +func queryInt(c *gin.Context, key string, defaultVal ...int) int { + strv := c.Query(key) + if strv != "" { + intv, err := strconv.Atoi(strv) + if err != nil { + bomb("cannot convert [%s] to int", strv) + } + return intv + } + + if len(defaultVal) == 0 { + bomb("query param[%s] is necessary", key) + } + + return defaultVal[0] +} + +func queryInt64(c *gin.Context, key string, defaultVal ...int64) int64 { + strv := c.Query(key) + if strv != "" { + intv, err := strconv.ParseInt(strv, 10, 64) + if err != nil { + bomb("cannot convert [%s] to int64", strv) + } + return intv + } + + if len(defaultVal) == 0 { + bomb("query param[%s] is necessary", key) + } + + return defaultVal[0] +} + +func offset(c *gin.Context, limit int) int { + if limit <= 0 { + limit = 10 + } + + page := queryInt(c, "p", 1) + return (page - 1) * limit +} + +func renderMessage(c *gin.Context, v interface{}) { + if v == nil { + c.JSON(200, gin.H{"err": ""}) + return + } + + switch t := v.(type) { + case string: + c.JSON(200, gin.H{"err": i18n.Sprintf(t)}) + case error: + c.JSON(200, gin.H{"err": t.Error()}) + } +} + +func renderData(c *gin.Context, data interface{}, err error) { + if err == nil { + c.JSON(200, gin.H{"dat": data, "err": ""}) + return + } + + renderMessage(c, err.Error()) +} + +func renderZeroPage(c *gin.Context) { + renderData(c, gin.H{ + "list": []int{}, + "total": 0, + }, nil) +} + +// ------------ + +type idsForm struct { + Ids []int64 `json:"ids"` +} + +func checkPassword(passwd string) error { + indNum := [4]int{0, 0, 0, 0} + spCode := []byte{'!', '@', '#', '$', '%', '^', '&', '*', '_', '-', '~', '.', ',', '<', '>', '/', ';', ':', '|', '?', '+', '='} + + if len(passwd) < 6 { + return fmt.Errorf("password too short") + } + + passwdByte := []byte(passwd) + + for _, i := range passwdByte { + + if i >= 'A' && i <= 'Z' { + indNum[0] = 1 + continue + } + + if i >= 'a' && i <= 'z' { + indNum[1] = 1 + continue + } + + if i >= '0' && i <= '9' { + indNum[2] = 1 + continue + } + + has := false + for _, s := range spCode { + if i == s { + indNum[3] = 1 + has = true + break + } + } + + if !has { + return fmt.Errorf("character: %s not supported", string(i)) + } + + } + + codeCount := 0 + + for _, i := range indNum { + codeCount += i + } + + if codeCount < 4 { + return fmt.Errorf("password too simple") + } + + return nil +} + +// ------------ + +func loginUsername(c *gin.Context) string { + value, has := c.Get("username") + if !has { + bomb("unauthorized") + } + + if value == nil { + bomb("unauthorized") + } + + return value.(string) +} + +func loginUser(c *gin.Context) *models.User { + username := loginUsername(c) + + user, err := models.UserGet("username=?", username) + dangerous(err) + + if user == nil { + bomb("unauthorized") + } + + return user +} + +func loginRoot(c *gin.Context) *models.User { + value, has := c.Get("user") + if !has { + bomb("unauthorized") + } + + return value.(*models.User) +} + +func User(id int64) *models.User { + user, err := models.UserGet("id=?", id) + if err != nil { + bomb("cannot retrieve user[%d]: %v", id, err) + } + + if user == nil { + bomb("no such user[%d]", id) + } + + return user +} + +func Team(id int64) *models.Team { + team, err := models.TeamGet("id=?", id) + if err != nil { + bomb("cannot retrieve team[%d]: %v", id, err) + } + + if team == nil { + bomb("no such team[%d]", id) + } + + return team +} + +func Role(id int64) *models.Role { + role, err := models.RoleGet("id=?", id) + if err != nil { + bomb("cannot retrieve role[%d]: %v", id, err) + } + + if role == nil { + bomb("no such role[%d]", id) + } + + return role +} + +func Node(id int64) *models.Node { + node, err := models.NodeGet("id=?", id) + dangerous(err) + + if node == nil { + bomb("no such node[%d]", id) + } + + return node +} diff --git a/src/modules/prober/manager/heap.go b/src/modules/prober/manager/heap.go new file mode 100644 index 00000000..ff93fc03 --- /dev/null +++ b/src/modules/prober/manager/heap.go @@ -0,0 +1,34 @@ +package manager + +type ruleSummary struct { + id int64 // collect rule id + executeAt int64 +} + +type ruleSummaryHeap []*ruleSummary + +func (h ruleSummaryHeap) Len() int { + return len(h) +} + +func (h ruleSummaryHeap) Less(i, j int) bool { + return h[i].executeAt < h[j].executeAt +} + +func (h ruleSummaryHeap) Swap(i, j int) { + h[i], h[j] = h[j], h[i] +} + +func (h *ruleSummaryHeap) Push(x interface{}) { + *h = append(*h, x.(*ruleSummary)) +} + +func (h *ruleSummaryHeap) Pop() interface{} { + x := (*h)[len(*h)-1] + *h = (*h)[:len(*h)-1] + return x +} + +func (h *ruleSummaryHeap) Top() *ruleSummary { + return (*h)[len(*h)-1] +} diff --git a/src/modules/prober/manager/manager.go b/src/modules/prober/manager/manager.go new file mode 100644 index 00000000..96f0263b --- /dev/null +++ b/src/modules/prober/manager/manager.go @@ -0,0 +1,194 @@ +package manager + +import ( + "container/heap" + "context" + "log" + "time" + + "github.com/didi/nightingale/src/models" + "github.com/didi/nightingale/src/modules/monapi/collector" + "github.com/didi/nightingale/src/modules/prober/cache" + "github.com/didi/nightingale/src/modules/prober/config" + "github.com/didi/nightingale/src/modules/prober/core" + "github.com/influxdata/telegraf" + "github.com/toolkits/pkg/logger" +) + +type manager struct { + ctx context.Context + cache *cache.CollectRuleCache + config *config.ConfYaml + heap ruleSummaryHeap + index map[int64]*ruleEntity // add at cache.C , del at executeAt check + worker []worker + tx chan *ruleEntity +} + +func NewManager(cfg *config.ConfYaml, cache *cache.CollectRuleCache) *manager { + return &manager{ + cache: cache, + config: cfg, + index: make(map[int64]*ruleEntity), + } +} + +func (p *manager) Start(ctx context.Context) error { + workerProcesses := p.config.WorkerProcesses + + p.ctx = ctx + p.tx = make(chan *ruleEntity, 1) + heap.Init(&p.heap) + + p.worker = make([]worker, workerProcesses) + for i := 0; i < workerProcesses; i++ { + p.worker[i].rx = p.tx + p.worker[i].ctx = ctx + // p.worker[i].acc = p.acc + + p.worker[i].loop(i) + } + + p.loop() + + return nil +} + +// loop schedule collect job and send the metric to transfer +func (p *manager) loop() { + // main + go func() { + tick := time.NewTicker(1 * time.Second) + defer tick.Stop() + for { + select { + case <-p.ctx.Done(): + return + case <-p.cache.C: + if err := p.SyncRules(); err != nil { + log.Printf("manager.SyncRules err %s", err) + } + case <-tick.C: + if err := p.schedule(); err != nil { + log.Printf("manager.schedule err %s", err) + } + } + } + }() +} + +// schedule return until there are no jobs +func (p *manager) schedule() error { + for { + now := time.Now().Unix() + if p.heap.Len() == 0 { + return nil + } + if p.heap.Top().executeAt > now { + return nil + } + + summary := heap.Pop(&p.heap).(*ruleSummary) + rule, ok := p.cache.Get(summary.id) + if !ok { + // drop it if not exist in cache + delete(p.index, summary.id) + continue + } + + entity, ok := p.index[rule.Id] + if !ok { + // impossible + log.Printf("manager.index[%d] not exists", rule.Id) + // let's fix it + p.index[entity.rule.Id] = entity + } + + // update rule + if err := entity.update(rule); err != nil { + logger.Warningf("ruleEntity update err %s", err) + } + + p.tx <- entity + + summary.executeAt = now + int64(rule.Step) + heap.Push(&p.heap, summary) + + continue + } +} + +func (p *manager) SyncRules() error { + for _, v := range p.cache.GetAll() { + if _, ok := p.index[v.Id]; !ok { + p.AddRule(v) + } + } + return nil +} + +func (p *manager) AddRule(rule *models.CollectRule) error { + ruleEntity, err := newRuleEntity(rule) + if err != nil { + return err + } + + p.index[rule.Id] = ruleEntity + heap.Push(&p.heap, &ruleSummary{ + id: rule.Id, + executeAt: time.Now().Unix() + int64(rule.Step), + }) + return nil +} + +type collectRule interface { + telegraf.Input + tags() map[string]string +} + +func telegrafInput(rule *models.CollectRule) (telegraf.Input, error) { + c, err := collector.GetCollector(rule.CollectType) + if err != nil { + return nil, err + } + return c.TelegrafInput(rule) +} + +type worker struct { + ctx context.Context + cache *cache.CollectRuleCache + rx chan *ruleEntity +} + +func (p *worker) loop(id int) { + go func() { + for { + select { + case <-p.ctx.Done(): + return + case entity := <-p.rx: + if err := p.do(entity); err != nil { + log.Printf("work[%d].do err %s", id, err) + } + } + } + }() +} + +func (p *worker) do(entity *ruleEntity) error { + entity.metrics = entity.metrics[:0] + + // telegraf + err := entity.Input.Gather(entity) + if len(entity.metrics) == 0 { + return err + } + + // eval expression metrics + entity.calc() + + // send + core.Push(entity.metrics) + + return err +} diff --git a/src/modules/prober/manager/metric.go b/src/modules/prober/manager/metric.go new file mode 100644 index 00000000..77e699a7 --- /dev/null +++ b/src/modules/prober/manager/metric.go @@ -0,0 +1,398 @@ +package manager + +import ( + "fmt" + "hash/fnv" + "sort" + "strconv" + "time" + + "github.com/influxdata/telegraf" +) + +type metric struct { + name string + tags []*telegraf.Tag + fields []*telegraf.Field + tm time.Time + + tp telegraf.ValueType + aggregate bool +} + +func NewMetric( + name string, + tags map[string]string, + fields map[string]interface{}, + tm time.Time, + tp ...telegraf.ValueType, +) (telegraf.Metric, error) { + var vtype telegraf.ValueType + if len(tp) > 0 { + vtype = tp[0] + } else { + vtype = telegraf.Untyped + } + + m := &metric{ + name: name, + tags: nil, + fields: nil, + tm: tm, + tp: vtype, + } + + if len(tags) > 0 { + m.tags = make([]*telegraf.Tag, 0, len(tags)) + for k, v := range tags { + m.tags = append(m.tags, + &telegraf.Tag{Key: k, Value: v}) + } + sort.Slice(m.tags, func(i, j int) bool { return m.tags[i].Key < m.tags[j].Key }) + } + + if len(fields) > 0 { + m.fields = make([]*telegraf.Field, 0, len(fields)) + for k, v := range fields { + v := convertField(v) + if v == nil { + continue + } + m.AddField(k, v) + } + } + + return m, nil +} + +// FromMetric returns a deep copy of the metric with any tracking information +// removed. +func FromMetric(other telegraf.Metric) telegraf.Metric { + m := &metric{ + name: other.Name(), + tags: make([]*telegraf.Tag, len(other.TagList())), + fields: make([]*telegraf.Field, len(other.FieldList())), + tm: other.Time(), + tp: other.Type(), + aggregate: other.IsAggregate(), + } + + for i, tag := range other.TagList() { + m.tags[i] = &telegraf.Tag{Key: tag.Key, Value: tag.Value} + } + + for i, field := range other.FieldList() { + m.fields[i] = &telegraf.Field{Key: field.Key, Value: field.Value} + } + return m +} + +func (m *metric) String() string { + return fmt.Sprintf("%s %v %v %d", m.name, m.Tags(), m.Fields(), m.tm.UnixNano()) +} + +func (m *metric) Name() string { + return m.name +} + +func (m *metric) Tags() map[string]string { + tags := make(map[string]string, len(m.tags)) + for _, tag := range m.tags { + tags[tag.Key] = tag.Value + } + return tags +} + +func (m *metric) TagList() []*telegraf.Tag { + return m.tags +} + +func (m *metric) Fields() map[string]interface{} { + fields := make(map[string]interface{}, len(m.fields)) + for _, field := range m.fields { + fields[field.Key] = field.Value + } + + return fields +} + +func (m *metric) FieldList() []*telegraf.Field { + return m.fields +} + +func (m *metric) Time() time.Time { + return m.tm +} + +func (m *metric) Type() telegraf.ValueType { + return m.tp +} + +func (m *metric) SetName(name string) { + m.name = name +} + +func (m *metric) AddPrefix(prefix string) { + m.name = prefix + m.name +} + +func (m *metric) AddSuffix(suffix string) { + m.name = m.name + suffix +} + +func (m *metric) AddTag(key, value string) { + for i, tag := range m.tags { + if key > tag.Key { + continue + } + + if key == tag.Key { + tag.Value = value + return + } + + m.tags = append(m.tags, nil) + copy(m.tags[i+1:], m.tags[i:]) + m.tags[i] = &telegraf.Tag{Key: key, Value: value} + return + } + + m.tags = append(m.tags, &telegraf.Tag{Key: key, Value: value}) +} + +func (m *metric) HasTag(key string) bool { + for _, tag := range m.tags { + if tag.Key == key { + return true + } + } + return false +} + +func (m *metric) GetTag(key string) (string, bool) { + for _, tag := range m.tags { + if tag.Key == key { + return tag.Value, true + } + } + return "", false +} + +func (m *metric) RemoveTag(key string) { + for i, tag := range m.tags { + if tag.Key == key { + copy(m.tags[i:], m.tags[i+1:]) + m.tags[len(m.tags)-1] = nil + m.tags = m.tags[:len(m.tags)-1] + return + } + } +} + +func (m *metric) AddField(key string, value interface{}) { + for i, field := range m.fields { + if key == field.Key { + m.fields[i] = &telegraf.Field{Key: key, Value: convertField(value)} + return + } + } + m.fields = append(m.fields, &telegraf.Field{Key: key, Value: convertField(value)}) +} + +func (m *metric) HasField(key string) bool { + for _, field := range m.fields { + if field.Key == key { + return true + } + } + return false +} + +func (m *metric) GetField(key string) (interface{}, bool) { + for _, field := range m.fields { + if field.Key == key { + return field.Value, true + } + } + return nil, false +} + +func (m *metric) RemoveField(key string) { + for i, field := range m.fields { + if field.Key == key { + copy(m.fields[i:], m.fields[i+1:]) + m.fields[len(m.fields)-1] = nil + m.fields = m.fields[:len(m.fields)-1] + return + } + } +} + +func (m *metric) SetTime(t time.Time) { + m.tm = t +} + +func (m *metric) Copy() telegraf.Metric { + m2 := &metric{ + name: m.name, + tags: make([]*telegraf.Tag, len(m.tags)), + fields: make([]*telegraf.Field, len(m.fields)), + tm: m.tm, + tp: m.tp, + aggregate: m.aggregate, + } + + for i, tag := range m.tags { + m2.tags[i] = &telegraf.Tag{Key: tag.Key, Value: tag.Value} + } + + for i, field := range m.fields { + m2.fields[i] = &telegraf.Field{Key: field.Key, Value: field.Value} + } + return m2 +} + +func (m *metric) SetAggregate(b bool) { + m.aggregate = true +} + +func (m *metric) IsAggregate() bool { + return m.aggregate +} + +func (m *metric) HashID() uint64 { + h := fnv.New64a() + h.Write([]byte(m.name)) + h.Write([]byte("\n")) + for _, tag := range m.tags { + h.Write([]byte(tag.Key)) + h.Write([]byte("\n")) + h.Write([]byte(tag.Value)) + h.Write([]byte("\n")) + } + return h.Sum64() +} + +func (m *metric) Accept() { +} + +func (m *metric) Reject() { +} + +func (m *metric) Drop() { +} + +// Convert field to a supported type or nil if unconvertible +// tranfer to float64 +func convertField(v interface{}) interface{} { + switch v := v.(type) { + case float64: + return v + case int64: + return float64(v) + case string: + return atof(v) + case bool: + return btof(v) + case int: + return float64(v) + case uint: + return float64(v) + case uint64: + return float64(v) + case []byte: + return atof(string(v)) + case int32: + return float64(v) + case int16: + return float64(v) + case int8: + return float64(v) + case uint32: + return float64(v) + case uint16: + return float64(v) + case uint8: + return float64(v) + case float32: + return float64(v) + case *float64: + if v != nil { + return float64(*v) + } + case *int64: + if v != nil { + return float64(*v) + } + case *string: + if v != nil { + return atof(*v) + } + case *bool: + if v != nil { + return btof(*v) + } + case *int: + if v != nil { + return float64(*v) + } + case *uint: + if v != nil { + return float64(*v) + } + case *uint64: + if v != nil { + return float64(*v) + } + case *[]byte: + if v != nil { + return atof(string(*v)) + } + case *int32: + if v != nil { + return float64(*v) + } + case *int16: + if v != nil { + return float64(*v) + } + case *int8: + if v != nil { + return float64(*v) + } + case *uint32: + if v != nil { + return float64(*v) + } + case *uint16: + if v != nil { + return float64(*v) + } + case *uint8: + if v != nil { + return float64(*v) + } + case *float32: + if v != nil { + return float64(*v) + } + default: + return nil + } + return nil +} + +func atof(s string) interface{} { + if f, err := strconv.ParseFloat(s, 64); err != nil { + return nil + } else { + return f + } +} + +func btof(b bool) interface{} { + if b { + return float64(1) + } + return float64(0) +} diff --git a/src/modules/prober/manager/rule_entity.go b/src/modules/prober/manager/rule_entity.go new file mode 100644 index 00000000..d607b298 --- /dev/null +++ b/src/modules/prober/manager/rule_entity.go @@ -0,0 +1,258 @@ +package manager + +import ( + "log" + "strconv" + "sync" + "time" + + "github.com/didi/nightingale/src/common/dataobj" + "github.com/didi/nightingale/src/models" + "github.com/didi/nightingale/src/modules/monapi/collector" + "github.com/didi/nightingale/src/modules/prober/cache" + "github.com/influxdata/telegraf" + "github.com/toolkits/pkg/logger" +) + +// not thread-safe +type ruleEntity struct { + sync.RWMutex + telegraf.Input + rule *models.CollectRule + tags map[string]string + precision time.Duration + metrics []*dataobj.MetricValue +} + +func newRuleEntity(rule *models.CollectRule) (*ruleEntity, error) { + c, err := collector.GetCollector(rule.CollectType) + if err != nil { + return nil, err + } + + input, err := c.TelegrafInput(rule) + if err != nil { + return nil, err + } + + tags, err := dataobj.SplitTagsString(rule.Tags) + if err != nil { + return nil, err + } + + return &ruleEntity{ + Input: input, + rule: rule, + tags: tags, + metrics: []*dataobj.MetricValue{}, + precision: time.Second, + }, nil +} + +// calc metrics with expression +func (p *ruleEntity) calc() error { + if len(p.metrics) == 0 { + return nil + } + sample := p.metrics[0] + + configs, ok := cache.GetMetricExprs(p.rule.CollectType) + if !ok { + return nil + } + + vars := map[string]float64{} + for _, v := range p.metrics { + logger.Debugf("get v[%s] %f", v.Metric, v.Value) + vars[v.Metric] = v.Value + } + + // TODO: add some variable from system or rule + for _, config := range configs { + f, err := config.Calc(vars) + if err != nil { + logger.Debugf("calc err %s", err) + continue + } + p.metrics = append(p.metrics, &dataobj.MetricValue{ + Nid: sample.Nid, + Metric: config.Name, + Timestamp: sample.Timestamp, + Step: sample.Step, + CounterType: config.Type, + TagsMap: sample.TagsMap, + Value: f, + ValueUntyped: f, + }) + } + return nil +} + +func (p *ruleEntity) update(rule *models.CollectRule) error { + if p.rule.LastUpdated == rule.LastUpdated { + return nil + } + + input, err := telegrafInput(rule) + if err != nil { + // ignore error, use old config + log.Printf("telegrafInput() id %d type %s name %s err %s", + rule.Id, rule.CollectType, rule.Name, err) + } + + tags, err := dataobj.SplitTagsString(rule.Tags) + if err != nil { + return err + } + + p.Input = input + p.rule = rule + p.tags = tags + + return nil +} + +// https://docs.influxdata.com/telegraf/v1.14/data_formats/output/prometheus/ +func (p *ruleEntity) MakeMetric(metric telegraf.Metric) []*dataobj.MetricValue { + tags := map[string]string{} + for _, v := range metric.TagList() { + tags[v.Key] = v.Value + } + + for k, v := range p.tags { + tags[k] = v + } + + nid := strconv.FormatInt(p.rule.Nid, 10) + name := metric.Name() + ts := metric.Time().Unix() + step := int64(p.rule.Step) // deprecated + + fields := metric.Fields() + ms := make([]*dataobj.MetricValue, 0, len(fields)) + for k, v := range fields { + f, ok := v.(float64) + if !ok { + continue + } + + c, ok := cache.Metric(name+"_"+k, metric.Type()) + if !ok { + continue + } + + ms = append(ms, &dataobj.MetricValue{ + Nid: nid, + Metric: c.Name, + Timestamp: ts, + Step: step, + CounterType: c.Type, + TagsMap: tags, + Value: f, + ValueUntyped: f, + }) + } + + return ms +} + +func (p *ruleEntity) AddFields( + measurement string, + fields map[string]interface{}, + tags map[string]string, + t ...time.Time, +) { + p.addFields(measurement, tags, fields, telegraf.Untyped, t...) +} + +func (p *ruleEntity) AddGauge( + measurement string, + fields map[string]interface{}, + tags map[string]string, + t ...time.Time, +) { + p.addFields(measurement, tags, fields, telegraf.Gauge, t...) +} + +func (p *ruleEntity) AddCounter( + measurement string, + fields map[string]interface{}, + tags map[string]string, + t ...time.Time, +) { + p.addFields(measurement, tags, fields, telegraf.Counter, t...) +} + +func (p *ruleEntity) AddSummary( + measurement string, + fields map[string]interface{}, + tags map[string]string, + t ...time.Time, +) { + p.addFields(measurement, tags, fields, telegraf.Summary, t...) +} + +func (p *ruleEntity) AddHistogram( + measurement string, + fields map[string]interface{}, + tags map[string]string, + t ...time.Time, +) { + p.addFields(measurement, tags, fields, telegraf.Histogram, t...) +} + +func (p *ruleEntity) AddMetric(m telegraf.Metric) { + m.SetTime(m.Time().Round(p.precision)) + if metrics := p.MakeMetric(m); m != nil { + p.pushMetrics(metrics) + } +} + +func (p *ruleEntity) pushMetrics(metrics []*dataobj.MetricValue) { + p.Lock() + defer p.Unlock() + p.metrics = append(p.metrics, metrics...) +} + +func (p *ruleEntity) addFields( + measurement string, + tags map[string]string, + fields map[string]interface{}, + tp telegraf.ValueType, + t ...time.Time, +) { + m, err := NewMetric(measurement, tags, fields, p.getTime(t), tp) + if err != nil { + return + } + if metrics := p.MakeMetric(m); m != nil { + p.pushMetrics(metrics) + } +} + +// AddError passes a runtime error to the accumulator. +// The error will be tagged with the plugin name and written to the log. +func (p *ruleEntity) AddError(err error) { + if err == nil { + return + } + log.Printf("Error in plugin: %v", err) +} + +func (p *ruleEntity) SetPrecision(precision time.Duration) { + p.precision = precision +} + +func (p *ruleEntity) getTime(t []time.Time) time.Time { + var timestamp time.Time + if len(t) > 0 { + timestamp = t[0] + } else { + timestamp = time.Now() + } + return timestamp.Round(p.precision) +} + +func (p *ruleEntity) WithTracking(maxTracked int) telegraf.TrackingAccumulator { + return nil +} diff --git a/src/modules/prober/prober.go b/src/modules/prober/prober.go new file mode 100644 index 00000000..08127e0d --- /dev/null +++ b/src/modules/prober/prober.go @@ -0,0 +1,132 @@ +package main + +import ( + "context" + "flag" + "fmt" + "os" + "os/signal" + "syscall" + + "github.com/didi/nightingale/src/common/identity" + "github.com/didi/nightingale/src/common/loggeri" + "github.com/didi/nightingale/src/common/report" + "github.com/didi/nightingale/src/toolkits/stats" + + "github.com/didi/nightingale/src/modules/prober/cache" + "github.com/didi/nightingale/src/modules/prober/config" + "github.com/didi/nightingale/src/modules/prober/core" + "github.com/didi/nightingale/src/modules/prober/http" + "github.com/didi/nightingale/src/modules/prober/manager" + + _ "github.com/didi/nightingale/src/modules/monapi/plugins/all" + _ "github.com/go-sql-driver/mysql" + + "github.com/gin-gonic/gin" + "github.com/toolkits/pkg/file" + "github.com/toolkits/pkg/logger" + "github.com/toolkits/pkg/runner" +) + +var ( + vers *bool + help *bool + conf *string + + version = "No Version Provided" +) + +func init() { + vers = flag.Bool("v", false, "display the version.") + help = flag.Bool("h", false, "print this help.") + conf = flag.String("f", "", "specify configuration file.") + flag.Parse() + + if *vers { + fmt.Println("Version:", version) + os.Exit(0) + } + + if *help { + flag.Usage() + os.Exit(0) + } +} + +func main() { + aconf() + pconf() + start() + + ctx, cancel := context.WithCancel(context.Background()) + + cfg := config.Config + identity.Parse() + loggeri.Init(cfg.Logger) + + go stats.Init("n9e.prober") + go report.Init(cfg.Report, "rdb") + + cache.Init(ctx) + + if cfg.Logger.Level != "DEBUG" { + gin.SetMode(gin.ReleaseMode) + } + + core.InitRpcClients() + + manager.NewManager(cfg, cache.CollectRule).Start(ctx) + + http.Start() + + ending(cancel) +} + +// auto detect configuration file +func aconf() { + if *conf != "" && file.IsExist(*conf) { + return + } + + *conf = "etc/prober.local.yml" + if file.IsExist(*conf) { + return + } + + *conf = "etc/prober.yml" + if file.IsExist(*conf) { + return + } + + fmt.Println("no configuration file for prober") + os.Exit(1) +} + +// parse configuration file +func pconf() { + if err := config.Parse(*conf); err != nil { + fmt.Println("cannot parse configuration file:", err) + os.Exit(1) + } +} + +func start() { + runner.Init() + fmt.Println("prober start, use configuration file:", *conf) + fmt.Println("runner.Cwd:", runner.Cwd) + fmt.Println("runner.Hostname:", runner.Hostname) +} + +func ending(cancel context.CancelFunc) { + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) + select { + case <-c: + fmt.Printf("stop signal caught, stopping... pid=%d\n", os.Getpid()) + } + + cancel() + logger.Close() + http.Shutdown() + fmt.Printf("%s stopped successfully\n", os.Args[0]) +} diff --git a/src/modules/rdb/auth/auth.go b/src/modules/rdb/auth/auth.go new file mode 100644 index 00000000..d01cc93e --- /dev/null +++ b/src/modules/rdb/auth/auth.go @@ -0,0 +1,39 @@ +package auth + +import ( + "github.com/didi/nightingale/src/models" + "github.com/didi/nightingale/src/modules/rdb/config" +) + +var defaultAuth Authenticator + +func Init(cf config.AuthExtraSection) { + defaultAuth = *New(cf) +} + +func WhiteListAccess(remoteAddr string) error { + return defaultAuth.WhiteListAccess(remoteAddr) +} + +// PostLogin check user status after login +func PostLogin(user *models.User, loginErr error) error { + return defaultAuth.PostLogin(user, loginErr) +} + +func ChangePassword(user *models.User, password string) error { + return defaultAuth.ChangePassword(user, password) +} + +func CheckPassword(password string) error { + return defaultAuth.CheckPassword(password) +} + +// ChangePasswordRedirect check user should change password before login +// return change password redirect url +func ChangePasswordRedirect(user *models.User, redirect string) string { + return defaultAuth.ChangePasswordRedirect(user, redirect) +} + +func Start() error { + return defaultAuth.Start() +} diff --git a/src/modules/rdb/auth/authenticator.go b/src/modules/rdb/auth/authenticator.go new file mode 100644 index 00000000..56c97248 --- /dev/null +++ b/src/modules/rdb/auth/authenticator.go @@ -0,0 +1,363 @@ +package auth + +import ( + "encoding/json" + "fmt" + "net/url" + "time" + + "github.com/didi/nightingale/src/models" + "github.com/didi/nightingale/src/modules/rdb/cache" + "github.com/didi/nightingale/src/modules/rdb/config" + "github.com/didi/nightingale/src/toolkits/i18n" + "github.com/toolkits/pkg/logger" +) + +const ( + ChangePasswordURL = "/change-password" +) + +type Authenticator struct { + extraMode bool + whiteList bool + frozenTime int64 + writenOffTime int64 + userExpire bool +} + +// description:"enable user expire control, active -> frozen -> writen-off" +func New(cf config.AuthExtraSection) *Authenticator { + if !cf.Enable { + return &Authenticator{} + } + + return &Authenticator{ + extraMode: true, + whiteList: cf.WhiteList, + frozenTime: 86400 * int64(cf.FrozenDays), + writenOffTime: 86400 * int64(cf.WritenOffDays), + } +} + +func (p *Authenticator) WhiteListAccess(remoteAddr string) error { + if !p.whiteList { + return nil + } + return models.WhiteListAccess(remoteAddr) +} + +// ChangePasswordRedirect check user should change password before login +// return change password redirect url +func (p *Authenticator) ChangePasswordRedirect(user *models.User, redirect string) string { + if !p.extraMode { + return "" + } + + cf := cache.AuthConfig() + + var reason string + if user.PwdUpdatedAt == 0 { + reason = _s("First Login, please change the password in time") + } else if user.PwdUpdatedAt+cf.PwdExpiresIn*86400*30 < time.Now().Unix() { + reason = _s("Password expired, please change the password in time") + } else { + return "" + } + + v := url.Values{ + "redirect": {redirect}, + "username": {user.Username}, + "reason": {reason}, + "pwdRules": cf.PwdRules(), + } + return ChangePasswordURL + "?" + v.Encode() +} + +func (p *Authenticator) PostLogin(user *models.User, loginErr error) (err error) { + now := time.Now().Unix() + defer func() { + if user == nil { + return + } + if err == nil { + user.LoggedAt = now + } + user.Update("status", "login_err_num", "locked_at", "updated_at", "logged_at") + }() + + if !p.extraMode || user == nil { + err = loginErr + return + } + + cf := cache.AuthConfig() + + if user.Type == models.USER_T_TEMP && (now < user.ActiveBegin || user.ActiveEnd < now) { + err = _e("Temporary user has expired") + return + } + + status := user.Status +retry: + switch user.Status { + case models.USER_S_ACTIVE: + err = activeUserAccess(cf, user, loginErr) + case models.USER_S_INACTIVE: + err = inactiveUserAccess(cf, user, loginErr) + case models.USER_S_LOCKED: + err = lockedUserAccess(cf, user, loginErr) + case models.USER_S_FROZEN: + err = frozenUserAccess(cf, user, loginErr) + case models.USER_S_WRITEN_OFF: + err = writenOffUserAccess(cf, user, loginErr) + default: + err = _e("Invalid user status %d", user.Status) + } + + // if user's status has been changed goto retry + if user.Status != status { + status = user.Status + goto retry + } + return +} + +func (p *Authenticator) ChangePassword(user *models.User, password string) (err error) { + defer func() { + if err == nil { + err = user.Update("password", "passwords", + "pwd_updated_at", "updated_at") + } + }() + + changePassword := func() error { + pwd, err := models.CryptoPass(password) + if err != nil { + return err + } + + now := time.Now().Unix() + user.Password = pwd + user.PwdUpdatedAt = now + user.UpdatedAt = now + return nil + } + + if !p.extraMode { + return changePassword() + } + + // precheck + cf := cache.AuthConfig() + if err = checkPassword(cf, password); err != nil { + return + } + + if err = changePassword(); err != nil { + return + } + + var passwords []string + err = json.Unmarshal([]byte(user.Passwords), &passwords) + if err != nil { + // reset passwords + passwords = []string{user.Password} + b, _ := json.Marshal(passwords) + user.Passwords = string(b) + err = nil + return + } + + for _, v := range passwords { + if user.Password == v { + err = _e("The password is the same as the old password") + return + } + } + + passwords = append(passwords, user.Password) + if n := len(passwords) - cf.PwdHistorySize; n > 0 { + passwords = passwords[n:] + } + + b, _ := json.Marshal(passwords) + user.Passwords = string(b) + return +} + +func (p *Authenticator) CheckPassword(password string) error { + if !p.extraMode { + return nil + } + return checkPassword(cache.AuthConfig(), password) +} + +func (p *Authenticator) Start() error { + if !p.extraMode { + return nil + } + + go func() { + for { + now := time.Now().Unix() + if p.frozenTime > 0 { + // 3个月以上未登录,用户自动变为休眠状态 + if _, err := models.DB["rdb"].Exec("update user set status=?, updated_at=?, locked_at=? where ((logged_at > 0 and logged_at 0 { + // 变为休眠状态后1年未激活,用户自动变为已注销状态 + if _, err := models.DB["rdb"].Exec("update user set status=?, updated_at=? where locked_at0", now, now-86400); err != nil { + logger.Errorf("update user login err num error %s", err) + } + + time.Sleep(time.Hour) + } + }() + return nil +} + +func activeUserAccess(cf *models.AuthConfig, user *models.User, loginErr error) error { + now := time.Now().Unix() + + if loginErr != nil { + if cf.MaxNumErr > 0 { + user.UpdatedAt = now + user.LoginErrNum++ + if user.LoginErrNum >= cf.MaxNumErr { + user.Status = models.USER_S_LOCKED + user.LockedAt = now + return nil + } + return _e("Incorrect login/password %s times, you still have %s chances", + user.LoginErrNum, cf.MaxNumErr-user.LoginErrNum) + } else { + return loginErr + } + } + + user.LoginErrNum = 0 + user.UpdatedAt = now + + if cf.MaxSessionNumber > 0 { + if n, err := models.SessionUserAll(user.Username); err != nil { + return err + } else if n >= cf.MaxSessionNumber { + return _e("The limited sessions %d", cf.MaxSessionNumber) + } + } + + if cf.PwdExpiresIn > 0 && user.PwdUpdatedAt > 0 { + // debug account + // TODO: remove me + if user.Username == "Demo.2022" { + if now-user.PwdUpdatedAt > cf.PwdExpiresIn*60 { + return _e("Password has been expired") + } + } + if now-user.PwdUpdatedAt > cf.PwdExpiresIn*30*86400 { + return _e("Password has been expired") + } + } + return nil +} +func inactiveUserAccess(cf *models.AuthConfig, user *models.User, loginErr error) error { + return _e("User is inactive") +} +func lockedUserAccess(cf *models.AuthConfig, user *models.User, loginErr error) error { + now := time.Now().Unix() + if now-user.LockedAt > cf.LockTime*60 { + user.Status = models.USER_S_ACTIVE + user.LoginErrNum = 0 + user.UpdatedAt = now + return nil + } + return _e("User is locked") +} + +func frozenUserAccess(cf *models.AuthConfig, user *models.User, loginErr error) error { + return _e("User is frozen") +} + +func writenOffUserAccess(cf *models.AuthConfig, user *models.User, loginErr error) error { + return _e("User is writen off") +} + +func checkPassword(cf *models.AuthConfig, passwd string) error { + indNum := [4]int{0, 0, 0, 0} + spCode := []byte{'!', '@', '#', '$', '%', '^', '&', '*', '_', '-', '~', '.', ',', '<', '>', '/', ';', ':', '|', '?', '+', '='} + + if cf.PwdMinLenght > 0 && len(passwd) < cf.PwdMinLenght { + return _e("Password too short (min:%d) %s", cf.PwdMinLenght, cf.MustInclude()) + } + + passwdByte := []byte(passwd) + + for _, i := range passwdByte { + + if i >= 'A' && i <= 'Z' { + indNum[0] = 1 + continue + } + + if i >= 'a' && i <= 'z' { + indNum[1] = 1 + continue + } + + if i >= '0' && i <= '9' { + indNum[2] = 1 + continue + } + + has := false + for _, s := range spCode { + if i == s { + indNum[3] = 1 + has = true + break + } + } + + if !has { + return _e("character: %s not supported", string(i)) + } + } + + if cf.PwdMustIncludeFlag&models.PWD_INCLUDE_UPPER > 0 && indNum[0] == 0 { + return _e("Invalid Password, %s", cf.MustInclude()) + } + + if cf.PwdMustIncludeFlag&models.PWD_INCLUDE_LOWER > 0 && indNum[1] == 0 { + return _e("Invalid Password, %s", cf.MustInclude()) + } + + if cf.PwdMustIncludeFlag&models.PWD_INCLUDE_NUMBER > 0 && indNum[2] == 0 { + return _e("Invalid Password, %s", cf.MustInclude()) + } + + if cf.PwdMustIncludeFlag&models.PWD_INCLUDE_SPEC_CHAR > 0 && indNum[3] == 0 { + return _e("Invalid Password, %s", cf.MustInclude()) + } + + return nil +} + +func _e(format string, a ...interface{}) error { + return fmt.Errorf(i18n.Sprintf(format, a...)) +} + +func _s(format string, a ...interface{}) string { + return i18n.Sprintf(format, a...) +} diff --git a/src/modules/rdb/cache/cache.go b/src/modules/rdb/cache/cache.go new file mode 100644 index 00000000..f195c933 --- /dev/null +++ b/src/modules/rdb/cache/cache.go @@ -0,0 +1,54 @@ +package cache + +import ( + "context" + + "github.com/didi/nightingale/src/models" +) + +var ( + DefaultCache = &Cache{ + interval: 10, // Seconds + config: configCache{ + authConfig: &models.DefaultAuthConfig, + }, + } +) + +func NewCache(interval int) *Cache { + return &Cache{ + interval: interval, + } +} + +func AuthConfig() *models.AuthConfig { + return DefaultCache.config.AuthConfig() +} + +func Start() { + DefaultCache.Start() +} + +func Stop() { + DefaultCache.Stop() +} + +type Cache struct { + session sessionCache + config configCache + + interval int + ctx context.Context + cancel context.CancelFunc +} + +func (p *Cache) Start() { + p.ctx, p.cancel = context.WithCancel(context.Background()) + + p.config.loop(p.ctx, p.interval) + // p.session.loop(ctx, p.interval) +} + +func (p *Cache) Stop() { + p.cancel() +} diff --git a/src/modules/rdb/cache/config.go b/src/modules/rdb/cache/config.go new file mode 100644 index 00000000..565a9072 --- /dev/null +++ b/src/modules/rdb/cache/config.go @@ -0,0 +1,52 @@ +package cache + +import ( + "context" + "sync" + "time" + + "github.com/didi/nightingale/src/models" + "github.com/toolkits/pkg/logger" +) + +type configCache struct { + sync.RWMutex + authConfig *models.AuthConfig +} + +func (p *configCache) AuthConfig() *models.AuthConfig { + p.RLock() + defer p.RUnlock() + return p.authConfig +} + +func (p *configCache) loop(ctx context.Context, interval int) { + go func() { + t := time.NewTicker(time.Duration(interval) * time.Second) + defer t.Stop() + + for { + select { + case <-ctx.Done(): + return + case <-t.C: + if err := p.update(); err != nil { + logger.Errorf("configCache update err %s", err) + } + } + } + }() +} + +func (p *configCache) update() error { + authConfig, err := models.AuthConfigGet() + if err != nil { + return err + } + + p.Lock() + p.authConfig = authConfig + p.Unlock() + + return nil +} diff --git a/src/modules/rdb/cache/session.go b/src/modules/rdb/cache/session.go new file mode 100644 index 00000000..2020b088 --- /dev/null +++ b/src/modules/rdb/cache/session.go @@ -0,0 +1,35 @@ +package cache + +import ( + "context" + "sync" + "time" + + "github.com/toolkits/pkg/logger" +) + +type sessionCache struct { + sync.RWMutex +} + +func (p *sessionCache) loop(ctx context.Context, interval int) { + func() { + t := time.NewTicker(time.Duration(interval) * time.Second) + defer t.Stop() + + for { + select { + case <-ctx.Done(): + return + case <-t.C: + if err := p.update(); err != nil { + logger.Errorf("sessionCache update err %s", err) + } + } + } + }() +} + +func (p *sessionCache) update() error { + return nil +} diff --git a/src/modules/rdb/config/yaml.go b/src/modules/rdb/config/yaml.go index faf52cea..eb2a5328 100644 --- a/src/modules/rdb/config/yaml.go +++ b/src/modules/rdb/config/yaml.go @@ -19,8 +19,20 @@ type ConfigT struct { Sender map[string]senderSection `yaml:"sender"` RabbitMQ rabbitmqSection `yaml:"rabbitmq"` WeChat wechatSection `yaml:"wechat"` - Captcha bool `yaml:"captcha"` I18n i18n.I18nSection `yaml:"i18n"` + Auth authSection `yaml:"auth"` +} + +type authSection struct { + Captcha bool `yaml:"captcha"` + ExtraMode AuthExtraSection `yaml:"extraMode"` +} + +type AuthExtraSection struct { + Enable bool `yaml:"enable"` + WhiteList bool `yaml:"whiteList"` + FrozenDays int `yaml:"frozenDays"` + WritenOffDays int `yaml:"writenOffDays"` } type wechatSection struct { @@ -47,9 +59,18 @@ type ssoSection struct { } type httpSection struct { - Mode string `yaml:"mode"` - CookieName string `yaml:"cookieName"` - CookieDomain string `yaml:"cookieDomain"` + Mode string `yaml:"mode"` + Session SessionSection `yaml:"session"` +} + +type SessionSection struct { + CookieName string `yaml:"cookieName"` + SidLength int `yaml:"sidLength"` + HttpOnly bool `yaml:"httpOnly"` + Domain string `yaml:"domain"` + GcInterval int64 `yaml:"gcInterval"` + CookieLifetime int64 `yaml:"cookieLifetime"` + Storage string `yaml:"storage" description:"mem|db(defualt)"` } type ldapSection struct { @@ -129,6 +150,17 @@ func Parse() error { return err } + // if Config.HTTP.Session.CookieLifetime == 0 { + // Config.HTTP.Session.CookieLifetime = 24 * 3600 + // } + + if Config.HTTP.Session.GcInterval == 0 { + Config.HTTP.Session.GcInterval = 60 + } + + if Config.HTTP.Session.SidLength == 0 { + Config.HTTP.Session.SidLength = 32 + } return nil } diff --git a/src/modules/rdb/http/http_middleware.go b/src/modules/rdb/http/http_middleware.go index 5e250272..0e78520a 100644 --- a/src/modules/rdb/http/http_middleware.go +++ b/src/modules/rdb/http/http_middleware.go @@ -12,17 +12,31 @@ import ( "github.com/didi/nightingale/src/common/address" "github.com/didi/nightingale/src/models" "github.com/didi/nightingale/src/modules/rdb/config" + "github.com/didi/nightingale/src/modules/rdb/session" ) +func shouldStartSession() gin.HandlerFunc { + return func(c *gin.Context) { + sessionStart(c) + c.Next() + sessionUpdate(c) + } +} + func shouldBeLogin() gin.HandlerFunc { return func(c *gin.Context) { - c.Set("username", mustUsername(c)) + sessionStart(c) + username := mustUsername(c) + logger.Debugf("set username %s", username) + c.Set("username", username) c.Next() + sessionUpdate(c) } } func shouldBeRoot() gin.HandlerFunc { return func(c *gin.Context) { + sessionStart(c) username := mustUsername(c) user, err := models.UserGet("username=?", username) @@ -35,6 +49,7 @@ func shouldBeRoot() gin.HandlerFunc { c.Set("username", username) c.Set("user", user) c.Next() + sessionUpdate(c) } } @@ -73,7 +88,7 @@ func shouldBeService() gin.HandlerFunc { } func mustUsername(c *gin.Context) string { - username := cookieUsername(c) + username := sessionUsername(c) if username == "" { username = headerUsername(c) } @@ -85,10 +100,6 @@ func mustUsername(c *gin.Context) string { return username } -func cookieUsername(c *gin.Context) string { - return models.UsernameByUUID(readCookieUser(c)) -} - func headerUsername(c *gin.Context) string { token := c.GetHeader("X-User-Token") if token == "" { @@ -108,17 +119,52 @@ func headerUsername(c *gin.Context) string { return ut.Username } -// ------------ - -func readCookieUser(c *gin.Context) string { - uuid, err := c.Cookie(config.Config.HTTP.CookieName) +func sessionStart(c *gin.Context) error { + s, err := session.Start(c.Writer, c.Request) if err != nil { - return "" + return err + } + c.Request = c.Request.WithContext(session.NewContext(c.Request.Context(), s)) + return nil +} + +func sessionUpdate(c *gin.Context) { + if store, ok := session.FromContext(c.Request.Context()); ok { + err := store.Update(c.Writer) + if err != nil { + logger.Errorf("session update err %s", err) + } + } +} + +func sessionDestory(c *gin.Context) (sid string, err error) { + if sid, err = session.Destroy(c.Writer, c.Request); sid != "" { + models.SessionCacheDelete(sid) } - return uuid + return } -func writeCookieUser(c *gin.Context, uuid string) { - c.SetCookie(config.Config.HTTP.CookieName, uuid, 3600*24, "/", config.Config.HTTP.CookieDomain, false, true) +func sessionUsername(c *gin.Context) string { + s, ok := session.FromContext(c.Request.Context()) + if !ok { + return "" + } + return s.Get("username") +} + +func sessionLogin(c *gin.Context, username, remoteAddr string) { + s, ok := session.FromContext(c.Request.Context()) + if !ok { + logger.Warningf("session.Start() err not found sessionStore") + return + } + if err := s.Set("username", username); err != nil { + logger.Warningf("session.Set() err %s", err) + return + } + if err := s.Set("remoteAddr", remoteAddr); err != nil { + logger.Warningf("session.Set() err %s", err) + return + } } diff --git a/src/modules/rdb/http/router.go b/src/modules/rdb/http/router.go index e560eb46..cc4b0269 100644 --- a/src/modules/rdb/http/router.go +++ b/src/modules/rdb/http/router.go @@ -10,25 +10,29 @@ func Config(r *gin.Engine) { { notLogin.GET("/ping", ping) notLogin.GET("/ldap/used", ldapUsed) - notLogin.POST("/auth/login", login) - notLogin.GET("/auth/logout", logout) notLogin.GET("/ops/global", globalOpsGet) notLogin.GET("/ops/local", localOpsGet) notLogin.GET("/roles/global", globalRoleGet) notLogin.GET("/roles/local", localRoleGet) notLogin.POST("/users/invite", userInvitePost) - notLogin.GET("/auth/v2/authorize", authAuthorizeV2) - notLogin.GET("/auth/v2/callback", authCallbackV2) - notLogin.GET("/auth/v2/logout", logoutV2) - - notLogin.POST("/auth/send-login-code-by-sms", v1SendLoginCodeBySms) - notLogin.POST("/auth/send-login-code-by-email", v1SendLoginCodeByEmail) - notLogin.POST("/auth/send-rst-code-by-sms", sendRstCodeBySms) + notLogin.POST("/auth/send-login-code", sendLoginCode) + notLogin.POST("/auth/send-rst-code", sendRstCode) notLogin.POST("/auth/rst-password", rstPassword) notLogin.GET("/auth/captcha", captchaGet) notLogin.GET("/v2/nodes", nodeGets) + notLogin.GET("/pwd-rules", pwdRulesGet) + + } + + sessionStarted := r.Group("/api/rdb").Use(shouldStartSession()) + { + sessionStarted.POST("/auth/login", login) + sessionStarted.GET("/auth/logout", logout) + sessionStarted.GET("/auth/v2/authorize", authAuthorizeV2) + sessionStarted.GET("/auth/v2/callback", authCallbackV2) + sessionStarted.GET("/auth/v2/logout", logoutV2) } hbs := r.Group("/api/hbs") @@ -43,6 +47,14 @@ func Config(r *gin.Engine) { rootLogin.POST("/configs/smtp/test", smtpTest) rootLogin.PUT("/configs/smtp", smtpConfigsPut) + rootLogin.GET("/configs/auth", authConfigsGet) + rootLogin.PUT("/configs/auth", authConfigsPut) + rootLogin.POST("/auth/white-list", whiteListPost) + rootLogin.GET("/auth/white-list", whiteListsGet) + rootLogin.GET("/auth/white-list/:id", whiteListGet) + rootLogin.PUT("/auth/white-list/:id", whiteListPut) + rootLogin.DELETE("/auth/white-list/:id", whiteListDel) + rootLogin.GET("/log/login", loginLogGets) rootLogin.GET("/log/operation", operationLogGets) @@ -83,12 +95,13 @@ func Config(r *gin.Engine) { userLogin.GET("/self/profile", selfProfileGet) userLogin.PUT("/self/profile", selfProfilePut) - userLogin.PUT("/self/password", selfPasswordPut) userLogin.GET("/self/token", selfTokenGets) userLogin.POST("/self/token", selfTokenPost) userLogin.PUT("/self/token", selfTokenPut) userLogin.GET("/self/perms/global", permGlobalOps) + notLogin.PUT("/self/password", selfPasswordPut) + userLogin.GET("/users", userListGet) userLogin.GET("/users/invite", userInviteGet) @@ -167,7 +180,6 @@ func Config(r *gin.Engine) { v1.GET("/can-do-node-ops", v1CandoNodeOps) // 获取用户、团队相关信息 - v1.GET("/get-username-by-uuid", v1UsernameGetByUUID) v1.GET("/get-user-by-uuid", v1UserGetByUUID) v1.GET("/get-users-by-uuids", v1UserGetByUUIDs) v1.GET("/get-users-by-ids", v1UserGetByIds) @@ -180,12 +192,16 @@ func Config(r *gin.Engine) { v1.GET("/users", v1UserListGet) v1.POST("/login", v1Login) - v1.POST("/send-login-code-by-sms", v1SendLoginCodeBySms) - v1.POST("/send-login-code-by-email", v1SendLoginCodeByEmail) + v1.POST("/send-login-code", sendLoginCode) // 第三方系统获取某个用户的所有权限点 v1.GET("/perms/global", v1PermGlobalOps) + // session + v1.GET("/sessions/:sid", v1SessionGet) + v1.GET("/sessions/:sid/user", v1SessionGetUser) + v1.DELETE("/sessions/:sid", v1SessionDelete) + // 第三方系统同步权限表的数据 v1.GET("/table/sync/role-operation", v1RoleOperationGets) v1.GET("/table/sync/role-global-user", v1RoleGlobalUserGets) diff --git a/src/modules/rdb/http/router_auth.go b/src/modules/rdb/http/router_auth.go index 1e8233f9..fbf39977 100644 --- a/src/modules/rdb/http/router_auth.go +++ b/src/modules/rdb/http/router_auth.go @@ -14,10 +14,13 @@ import ( "github.com/gin-gonic/gin" "github.com/mojocn/base64Captcha" "github.com/toolkits/pkg/file" + "github.com/toolkits/pkg/logger" "github.com/toolkits/pkg/str" "github.com/didi/nightingale/src/common/dataobj" "github.com/didi/nightingale/src/models" + "github.com/didi/nightingale/src/modules/rdb/auth" + "github.com/didi/nightingale/src/modules/rdb/cache" "github.com/didi/nightingale/src/modules/rdb/config" "github.com/didi/nightingale/src/modules/rdb/redisc" "github.com/didi/nightingale/src/modules/rdb/ssoc" @@ -27,7 +30,6 @@ var ( loginCodeSmsTpl *template.Template loginCodeEmailTpl *template.Template errUnsupportCaptcha = errors.New("unsupported captcha") - errInvalidAnswer = errors.New("Invalid captcha answer") // TODO: set false debug = true @@ -76,49 +78,49 @@ func init() { } } +// login for UI func login(c *gin.Context) { - var f loginInput - bind(c, &f) - f.validate() + var in loginInput + bind(c, &in) + in.RemoteAddr = c.ClientIP() - if config.Config.Captcha { - c, err := models.CaptchaGet("captcha_id=?", f.CaptchaId) - dangerous(err) - if strings.ToLower(c.Answer) != strings.ToLower(f.Answer) { - dangerous(errInvalidAnswer) + err := func() error { + if err := in.Validate(); err != nil { + return err } - } - user, err := authLogin(f) - dangerous(err) + if config.Config.Auth.Captcha { + c, err := models.CaptchaGet("captcha_id=?", in.CaptchaId) + if err != nil { + return _e("Unable to get captcha") + } + if strings.ToLower(c.Answer) != strings.ToLower(in.Answer) { + return _e("Invalid captcha answer") + } + } - writeCookieUser(c, user.UUID) + user, err := authLogin(in.v1LoginInput()) + if err != nil { + logger.Debugf("login error %s", err) + return err + } - renderMessage(c, "") - - go models.LoginLogNew(user.Username, c.ClientIP(), "in") + sessionLogin(c, user.Username, in.RemoteAddr) + return nil + }() + renderMessage(c, err) } func logout(c *gin.Context) { func() { - uuid := readCookieUser(c) - if uuid == "" { - return - } - username := models.UsernameByUUID(uuid) + username := sessionUsername(c) if username == "" { return } - writeCookieUser(c, "") - go models.LoginLogNew(username, c.ClientIP(), "out") + sessionDestory(c) + models.LoginLogNew(username, c.ClientIP(), "out", nil) }() - if config.Config.SSO.Enable { - redirect := queryStr(c, "redirect", "/") - c.Redirect(302, ssoc.LogoutLocation(redirect)) - return - } - if redirect := queryStr(c, "redirect", ""); redirect != "" { c.Redirect(302, redirect) return @@ -128,27 +130,31 @@ func logout(c *gin.Context) { } type authRedirect struct { - Redirect string `json:"redirect"` - Msg string `json:"msg"` + Redirect string `json:"redirect"` + User *models.User `json:"user"` + Msg string `json:"msg"` } func authAuthorizeV2(c *gin.Context) { - redirect := queryStr(c, "redirect", "/") - ret := &authRedirect{Redirect: redirect} + resp, err := func() (*authRedirect, error) { + redirect := queryStr(c, "redirect", "/") - username := cookieUsername(c) - if username != "" { // alread login - renderData(c, ret, nil) - return - } + username := sessionUsername(c) + if username != "" { // alread login + return &authRedirect{Redirect: redirect}, nil + } - var err error - if config.Config.SSO.Enable { - ret.Redirect, err = ssoc.Authorize(redirect) - } else { - ret.Redirect = "/login" - } - renderData(c, ret, err) + if !config.Config.SSO.Enable { + return &authRedirect{Redirect: "/login"}, nil + } + + if redirect, err := ssoc.Authorize(redirect); err != nil { + return nil, err + } else { + return &authRedirect{Redirect: redirect}, nil + } + }() + renderData(c, resp, err) } func authCallbackV2(c *gin.Context) { @@ -158,19 +164,28 @@ func authCallbackV2(c *gin.Context) { ret := &authRedirect{Redirect: redirect} if code == "" && redirect != "" { + logger.Debugf("sso.callback() can't get code and redirect is not set") renderData(c, ret, nil) return } - var user *models.User var err error - ret.Redirect, user, err = ssoc.Callback(code, state) + ret.Redirect, ret.User, err = ssoc.Callback(code, state) if err != nil { + logger.Debugf("sso.callback() error %s", err) renderData(c, ret, err) return } - writeCookieUser(c, user.UUID) + if redirect := auth.ChangePasswordRedirect(ret.User, ret.Redirect); redirect != "" { + logger.Debugf("sso.callback() redirect to changePassword %s", redirect) + ret.Redirect = redirect + renderData(c, ret, nil) + return + } + + logger.Debugf("sso.callback() successfully, set username %s", ret.User.Username) + sessionLogin(c, ret.User.Username, c.ClientIP()) renderData(c, ret, nil) } @@ -178,131 +193,197 @@ func logoutV2(c *gin.Context) { redirect := queryStr(c, "redirect", "") ret := &authRedirect{Redirect: redirect} - uuid := readCookieUser(c) - if uuid == "" { - renderData(c, ret, nil) - return - } - - username := models.UsernameByUUID(uuid) + username := sessionUsername(c) if username == "" { renderData(c, ret, nil) return } - writeCookieUser(c, "") + sessionDestory(c) ret.Msg = "logout successfully" - if config.Config.SSO.Enable { - if redirect == "" { - redirect = "/" - } - ret.Redirect = ssoc.LogoutLocation(redirect) - } - renderData(c, ret, nil) - go models.LoginLogNew(username, c.ClientIP(), "out") + models.LoginLogNew(username, c.ClientIP(), "out", nil) } type loginInput struct { - Username string `json:"username"` - Password string `json:"password"` - Phone string `json:"phone"` - Email string `json:"email"` - Code string `json:"code"` - CaptchaId string `json:"captcha_id"` - Answer string `json:"answer" description:"captcha answer"` - Type string `json:"type" description:"sms-code|email-code|password|ldap"` - RemoteAddr string `json:"remote_addr" description:"use for server account(v1)"` - IsLDAP int `json:"is_ldap" description:"deprecated"` + Username string `json:"username"` + Password string `json:"password"` + CaptchaId string `json:"captcha_id"` + Answer string `json:"answer" description:"captcha answer"` + Type string `json:"type" description:"sms-code|email-code|password|ldap"` + Args []string `json:"args" description:""` + RemoteAddr string `json:"remote_addr" description:"use for server account(v1)"` + IsLDAP int `json:"is_ldap" description:"deprecated"` } -func (f *loginInput) validate() { - if f.IsLDAP == 1 { - f.Type = models.LOGIN_T_LDAP +func (p *loginInput) Validate() error { + if p.IsLDAP == 1 { + p.Type = models.LOGIN_T_LDAP } - if f.Type == "" { - f.Type = models.LOGIN_T_PWD + if p.Type == "" { + p.Type = models.LOGIN_T_PWD } - if f.Type == models.LOGIN_T_PWD { - if str.Dangerous(f.Username) { - bomb("%s invalid", f.Username) - } - if len(f.Username) > 64 { - bomb("%s too long > 64", f.Username) + if p.Type == models.LOGIN_T_PWD || p.Type == models.LOGIN_T_LDAP { + if len(p.Args) == 0 { + if str.Dangerous(p.Username) { + return _e("Username %s is invalid", p.Username) + } + if len(p.Username) > 64 { + return _e("Username %s too long > 64", p.Username) + } + p.Args = []string{p.Username, p.Password} } } + + if len(p.Args) == 0 { + return _e("Unable to get login arguments") + } + return nil +} + +func (p *loginInput) v1LoginInput() *v1LoginInput { + return &v1LoginInput{ + Type: p.Type, + Args: p.Args, + RemoteAddr: p.RemoteAddr, + } +} + +type v1LoginInput struct { + Type string `param:"data" json:"type"` + Args []string `param:"data" json:"args"` + RemoteAddr string `param:"data" json:"remote_addr"` +} + +func (p *v1LoginInput) Validate() error { + if p.Type == "" { + p.Type = models.LOGIN_T_PWD + } + + if len(p.Args) == 0 { + return _e("Unable to get login arguments") + } + + return nil } // v1Login called by sso.rdb module func v1Login(c *gin.Context) { - var f loginInput - bind(c, &f) + var in v1LoginInput + bind(c, &in) - user, err := authLogin(f) - renderData(c, *user, err) - - go models.LoginLogNew(user.Username, f.RemoteAddr, "in") + user, err := authLogin(&in) + renderData(c, user, err) } // authLogin called by /v1/rdb/login, /api/rdb/auth/login -func authLogin(in loginInput) (user *models.User, err error) { +func authLogin(in *v1LoginInput) (user *models.User, err error) { + if err = in.Validate(); err != nil { + return + } + + if err := auth.WhiteListAccess(in.RemoteAddr); err != nil { + return nil, _e("Deny Access from %s with whitelist control", in.RemoteAddr) + } + defer func() { + models.LoginLogNew(in.Args[0], in.RemoteAddr, "in", err) + }() + switch strings.ToLower(in.Type) { case models.LOGIN_T_LDAP: - return models.LdapLogin(in.Username, in.Password) + user, err = models.LdapLogin(in.Args[0], in.Args[1]) case models.LOGIN_T_PWD: - return models.PassLogin(in.Username, in.Password) + user, err = models.PassLogin(in.Args[0], in.Args[1]) case models.LOGIN_T_SMS: - return models.SmsCodeLogin(in.Phone, in.Code) + user, err = models.SmsCodeLogin(in.Args[0], in.Args[1]) case models.LOGIN_T_EMAIL: - return models.EmailCodeLogin(in.Email, in.Code) + user, err = models.EmailCodeLogin(in.Args[0], in.Args[1]) default: - return nil, fmt.Errorf("invalid login type %s", in.Type) + err = _e("Invalid login type %s", in.Type) } + + if err = auth.PostLogin(user, err); err != nil { + return nil, err + } + + return user, nil } -type v1SendLoginCodeBySmsInput struct { - Phone string `json:"phone"` +type sendCodeInput struct { + Type string `json:"type" description:"sms-code, email-code"` + Arg string `json:"arg"` } -func v1SendLoginCodeBySms(c *gin.Context) { - var f v1SendLoginCodeBySmsInput - bind(c, &f) +func (p *sendCodeInput) Validate() error { + if p.Type == "" { + return _e("Unable to get type, sms-code | email-code") + } + if p.Arg == "" { + return _e("Unable to get code arg") + } + return nil +} + +func sendLoginCode(c *gin.Context) { + var in sendCodeInput + bind(c, &in) msg, err := func() (string, error) { - if !config.Config.Redis.Enable { - return "", fmt.Errorf("sms sender is disabled") + if err := in.Validate(); err != nil { + return "", err } - phone := f.Phone - user, _ := models.UserGet("phone=?", phone) - if user == nil { - return "", fmt.Errorf("phone %s dose not exist", phone) + if !config.Config.Redis.Enable { + return "", _e("sms/email sender is disabled") + } + + if err := in.Validate(); err != nil { + return "", err } // general a random code and add cache code := fmt.Sprintf("%06d", rand.Intn(1000000)) loginCode := &models.LoginCode{ - Username: user.Username, Code: code, - LoginType: models.LOGIN_T_SMS, + LoginType: models.LOGIN_T_LOGIN, CreatedAt: time.Now().Unix(), } + var ( + user *models.User + buf bytes.Buffer + queueName string + ) + + switch in.Type { + case models.LOGIN_T_SMS: + user, _ = models.UserGet("phone=?", in.Arg) + if err := loginCodeSmsTpl.Execute(&buf, loginCode); err != nil { + return "", err + } + queueName = config.SMS_QUEUE_NAME + case models.LOGIN_T_EMAIL: + user, _ = models.UserGet("email=?", in.Arg) + if err := loginCodeEmailTpl.Execute(&buf, loginCode); err != nil { + return "", err + } + queueName = config.MAIL_QUEUE_NAME + default: + return "", _e("Invalid code type %s", in.Type) + } + + if user == nil { + return "", _e("Cannot find the user by %s", in.Arg) + } + + loginCode.Username = user.Username if err := loginCode.Save(); err != nil { return "", err } - var buf bytes.Buffer - if err := loginCodeSmsTpl.Execute(&buf, loginCode); err != nil { - return "", err - } - - if err := redisc.Write(&dataobj.Message{ - Tos: []string{phone}, - Content: buf.String(), - }, config.SMS_QUEUE_NAME); err != nil { + if err := redisc.Write(&dataobj.Message{Tos: []string{in.Arg}, Content: buf.String()}, queueName); err != nil { return "", err } @@ -316,100 +397,65 @@ func v1SendLoginCodeBySms(c *gin.Context) { renderData(c, msg, err) } -type v1SendLoginCodeByEmailInput struct { - Email string `json:"email"` -} - -func v1SendLoginCodeByEmail(c *gin.Context) { - var f v1SendLoginCodeByEmailInput - bind(c, &f) +func sendRstCode(c *gin.Context) { + var in sendCodeInput + bind(c, &in) + logger.Debugf("rst code input %#v", in) msg, err := func() (string, error) { - if !config.Config.Redis.Enable { - return "", fmt.Errorf("mail sender is disabled") + if err := in.Validate(); err != nil { + return "", err } - email := f.Email - user, _ := models.UserGet("email=?", email) - if user == nil { - return "", fmt.Errorf("email %s dose not exist", email) + if !config.Config.Redis.Enable { + return "", _e("email/sms sender is disabled") + } + + if err := in.Validate(); err != nil { + return "", err } // general a random code and add cache code := fmt.Sprintf("%06d", rand.Intn(1000000)) loginCode := &models.LoginCode{ - Username: user.Username, - Code: code, - LoginType: models.LOGIN_T_EMAIL, - CreatedAt: time.Now().Unix(), - } - - if err := loginCode.Save(); err != nil { - return "", err - } - - var buf bytes.Buffer - if err := loginCodeEmailTpl.Execute(&buf, loginCode); err != nil { - return "", err - } - - if err := redisc.Write(&dataobj.Message{ - Tos: []string{email}, - Content: buf.String(), - }, config.SMS_QUEUE_NAME); err != nil { - return "", err - } - - if debug { - return fmt.Sprintf("[debug]: %s", buf.String()), nil - } - return "successed", nil - }() - renderData(c, msg, err) -} - -type sendRstCodeBySmsInput struct { - Username string `json:"username"` - Phone string `json:"phone"` -} - -func sendRstCodeBySms(c *gin.Context) { - var f sendRstCodeBySmsInput - bind(c, &f) - - msg, err := func() (string, error) { - if !config.Config.Redis.Enable { - return "", fmt.Errorf("sms sender is disabled") - } - phone := f.Phone - user, _ := models.UserGet("username=? and phone=?", f.Username, phone) - if user == nil { - return "", fmt.Errorf("user %s phone %s dose not exist", f.Username, phone) - } - - // general a random code and add cache - code := fmt.Sprintf("%06d", rand.Intn(1000000)) - - loginCode := &models.LoginCode{ - Username: user.Username, Code: code, LoginType: models.LOGIN_T_RST, CreatedAt: time.Now().Unix(), } + var ( + user *models.User + buf bytes.Buffer + queueName string + ) + + switch in.Type { + case models.LOGIN_T_SMS: + user, _ = models.UserGet("phone=?", in.Arg) + if err := loginCodeSmsTpl.Execute(&buf, loginCode); err != nil { + return "", err + } + queueName = config.SMS_QUEUE_NAME + case models.LOGIN_T_EMAIL: + user, _ = models.UserGet("email=?", in.Arg) + if err := loginCodeEmailTpl.Execute(&buf, loginCode); err != nil { + return "", err + } + queueName = config.MAIL_QUEUE_NAME + default: + return "", _e("Invalid code type %s", in.Type) + } + + if user == nil { + return "", _e("Cannot find the user by %s", in.Arg) + } + + loginCode.Username = user.Username if err := loginCode.Save(); err != nil { return "", err } - var buf bytes.Buffer - if err := loginCodeSmsTpl.Execute(&buf, loginCode); err != nil { - return "", err - } - - if err := redisc.Write(&dataobj.Message{ - Tos: []string{phone}, - Content: buf.String(), - }, config.SMS_QUEUE_NAME); err != nil { + if err := redisc.Write(&dataobj.Message{Tos: []string{in.Arg}, Content: buf.String()}, queueName); err != nil { return "", err } @@ -418,17 +464,29 @@ func sendRstCodeBySms(c *gin.Context) { } return "successed", nil - }() renderData(c, msg, err) } type rstPasswordInput struct { - Username string `json:"username"` - Phone string `json:"phone"` + Type string `json:"type"` + Arg string `json:"arg"` Code string `json:"code"` Password string `json:"password"` - Type string `json:"type"` + DryRun bool `json:"dryRun"` +} + +func (p *rstPasswordInput) Validate() error { + if p.Type == "" { + return _e("Unable to get type, sms-code | email-code") + } + if p.Arg == "" { + return _e("Unable to get code arg") + } + if !p.DryRun && p.Password == "" { + return _e("Unable to get password") + } + return nil } func rstPassword(c *gin.Context) { @@ -436,39 +494,43 @@ func rstPassword(c *gin.Context) { bind(c, &in) err := func() error { - user, _ := models.UserGet("username=? and phone=?", in.Username, in.Phone) - if user == nil { - return fmt.Errorf("user's phone not exist") + if err := in.Validate(); err != nil { + return err } - lc, err := models.LoginCodeGet("username=? and code=? and login_type=?", - user.Username, in.Code, models.LOGIN_T_RST) + var user *models.User + + switch in.Type { + case models.LOGIN_T_SMS: + user, _ = models.UserGet("phone=?", in.Arg) + case models.LOGIN_T_EMAIL: + user, _ = models.UserGet("email=?", in.Arg) + default: + return fmt.Errorf("invalid type %s", in.Type) + } + + if user == nil { + return _e("Cannot find the user by %s", in.Arg) + } + + lc, err := models.LoginCodeGet("code=? and login_type=?", in.Code, models.LOGIN_T_RST) if err != nil { - return fmt.Errorf("invalid code") + return _e("Invalid code") } if time.Now().Unix()-lc.CreatedAt > models.LOGIN_EXPIRES_IN { - return fmt.Errorf("the code has expired") + return _e("The code has expired") } - if in.Type == "verify-code" { + if in.DryRun { return nil } - defer lc.Del() // update password - if user.Password, err = models.CryptoPass(in.Password); err != nil { + if err := auth.ChangePassword(user, in.Password); err != nil { return err } - - if err = checkPassword(in.Password); err != nil { - return err - } - - if err = user.Update("password"); err != nil { - return err - } - + lc.Del() return nil }() @@ -481,7 +543,7 @@ func rstPassword(c *gin.Context) { func captchaGet(c *gin.Context) { ret, err := func() (*models.Captcha, error) { - if !config.Config.Captcha { + if !config.Config.Auth.Captcha { return nil, errUnsupportCaptcha } @@ -516,3 +578,129 @@ func authSettings(c *gin.Context) { Sso: config.Config.SSO.Enable, }, nil) } + +func authConfigsGet(c *gin.Context) { + config, err := models.AuthConfigGet() + renderData(c, config, err) +} + +func authConfigsPut(c *gin.Context) { + var in models.AuthConfig + bind(c, &in) + + err := models.AuthConfigSet(&in) + renderData(c, "", err) +} + +type createWhiteListInput struct { + StartIp string `json:"startIp"` + EndIp string `json:"endIp"` + StartTime int64 `json:"startTime"` + EndTime int64 `json:"endTime"` +} + +func whiteListPost(c *gin.Context) { + var in createWhiteListInput + bind(c, &in) + + username := loginUser(c).Username + ts := time.Now().Unix() + + wl := models.WhiteList{ + StartIp: in.StartIp, + EndIp: in.EndIp, + StartTime: in.StartTime, + EndTime: in.EndTime, + CreatedAt: ts, + UpdatedAt: ts, + Creator: username, + Updater: username, + } + if err := wl.Validate(); err != nil { + bomb("Invalid arguments %s", err) + } + dangerous(wl.Save()) + + renderData(c, gin.H{"id": wl.Id}, nil) +} + +func whiteListsGet(c *gin.Context) { + limit := queryInt(c, "limit", 20) + query := queryStr(c, "query", "") + + total, err := models.WhiteListTotal(query) + dangerous(err) + + list, err := models.WhiteListGets(query, limit, offset(c, limit)) + dangerous(err) + + renderData(c, gin.H{ + "list": list, + "total": total, + }, nil) +} + +func whiteListGet(c *gin.Context) { + id := urlParamInt64(c, "id") + ret, err := models.WhiteListGet("id=?", id) + renderData(c, ret, err) +} + +type updateWhiteListInput struct { + StartIp string `json:"startIp"` + EndIp string `json:"endIp"` + StartTime int64 `json:"startTime"` + EndTime int64 `json:"endTime"` +} + +func whiteListPut(c *gin.Context) { + var in updateWhiteListInput + bind(c, &in) + + wl, err := models.WhiteListGet("id=?", urlParamInt64(c, "id")) + if err != nil { + bomb("Cannot found white list") + } + + wl.StartIp = in.StartIp + wl.EndIp = in.EndIp + wl.StartTime = in.StartTime + wl.EndTime = in.EndTime + wl.UpdatedAt = time.Now().Unix() + wl.Updater = loginUser(c).Username + + if err := wl.Validate(); err != nil { + bomb("Invalid arguments %s", err) + } + + renderMessage(c, wl.Update("start_ip", "end_ip", "start_time", "end_time", "updated_at", "updater")) +} + +func whiteListDel(c *gin.Context) { + wl, err := models.WhiteListGet("id=?", urlParamInt64(c, "id")) + dangerous(err) + + renderMessage(c, wl.Del()) +} + +func v1SessionGet(c *gin.Context) { + sess, err := models.SessionGetWithCache(urlParamStr(c, "sid")) + renderData(c, sess, err) +} + +func v1SessionGetUser(c *gin.Context) { + user, err := models.SessionGetUserWithCache(urlParamStr(c, "sid")) + renderData(c, user, err) +} + +func v1SessionDelete(c *gin.Context) { + sid := urlParamStr(c, "sid") + logger.Debugf("session del sid %s", sid) + renderMessage(c, models.SessionDel(sid)) +} + +// pwdRulesGet return pwd rules +func pwdRulesGet(c *gin.Context) { + cf := cache.AuthConfig() + renderData(c, cf.PwdRules(), nil) +} diff --git a/src/modules/rdb/http/router_funcs.go b/src/modules/rdb/http/router_funcs.go index 2c9afe5d..1b85d8a1 100644 --- a/src/modules/rdb/http/router_funcs.go +++ b/src/modules/rdb/http/router_funcs.go @@ -4,11 +4,10 @@ import ( "fmt" "strconv" - "github.com/gin-gonic/gin" - "github.com/toolkits/pkg/errors" - "github.com/didi/nightingale/src/models" "github.com/didi/nightingale/src/toolkits/i18n" + "github.com/gin-gonic/gin" + "github.com/toolkits/pkg/errors" ) func dangerous(v interface{}) { @@ -133,69 +132,10 @@ func renderZeroPage(c *gin.Context) { }, nil) } -// ------------ - type idsForm struct { Ids []int64 `json:"ids"` } -func checkPassword(passwd string) error { - indNum := [4]int{0, 0, 0, 0} - spCode := []byte{'!', '@', '#', '$', '%', '^', '&', '*', '_', '-', '~', '.', ',', '<', '>', '/', ';', ':', '|', '?', '+', '='} - - if len(passwd) < 6 { - return fmt.Errorf("password too short") - } - - passwdByte := []byte(passwd) - - for _, i := range passwdByte { - - if i >= 'A' && i <= 'Z' { - indNum[0] = 1 - continue - } - - if i >= 'a' && i <= 'z' { - indNum[1] = 1 - continue - } - - if i >= '0' && i <= '9' { - indNum[2] = 1 - continue - } - - has := false - for _, s := range spCode { - if i == s { - indNum[3] = 1 - has = true - break - } - } - - if !has { - return fmt.Errorf("character: %s not supported", string(i)) - } - - } - - codeCount := 0 - - for _, i := range indNum { - codeCount += i - } - - if codeCount < 4 { - return fmt.Errorf("password too simple") - } - - return nil -} - -// ------------ - func loginUsername(c *gin.Context) string { value, has := c.Get("username") if !has { @@ -280,3 +220,11 @@ func Node(id int64) *models.Node { return node } + +func _e(format string, a ...interface{}) error { + return fmt.Errorf(i18n.Sprintf(format, a...)) +} + +func _s(format string, a ...interface{}) string { + return i18n.Sprintf(format, a...) +} diff --git a/src/modules/rdb/http/router_hbs.go b/src/modules/rdb/http/router_hbs.go index 8e932225..241f37f4 100644 --- a/src/modules/rdb/http/router_hbs.go +++ b/src/modules/rdb/http/router_hbs.go @@ -23,12 +23,14 @@ func heartBeat(c *gin.Context) { Module: rev.Module, RPCPort: rev.RPCPort, HTTPPort: rev.HTTPPort, + Region: rev.Region, TS: now, } errors.Dangerous(instance.Add()) } else { instance.TS = now instance.HTTPPort = rev.HTTPPort + instance.Region = rev.Region errors.Dangerous(instance.Update()) } diff --git a/src/modules/rdb/http/router_self.go b/src/modules/rdb/http/router_self.go index a948ee10..03bc734d 100644 --- a/src/modules/rdb/http/router_self.go +++ b/src/modules/rdb/http/router_self.go @@ -2,6 +2,7 @@ package http import ( "github.com/didi/nightingale/src/models" + "github.com/didi/nightingale/src/modules/rdb/auth" "github.com/didi/nightingale/src/modules/rdb/config" "github.com/gin-gonic/gin" ) @@ -35,28 +36,32 @@ func selfProfilePut(c *gin.Context) { } type selfPasswordForm struct { - OldPass string `json:"oldpass" binding:"required"` - NewPass string `json:"newpass" binding:"required"` + Username string `json:"username" binding:"required"` + OldPass string `json:"oldpass" binding:"required"` + NewPass string `json:"newpass" binding:"required"` } func selfPasswordPut(c *gin.Context) { var f selfPasswordForm bind(c, &f) - dangerous(checkPassword(f.NewPass)) - oldpass, err := models.CryptoPass(f.OldPass) - dangerous(err) + err := func() error { + user, err := models.UserMustGet("username=?", f.Username) + if err != nil { + return err + } + oldpass, err := models.CryptoPass(f.OldPass) + if err != nil { + return err + } + if user.Password != oldpass { + return _e("Incorrect old password") + } - newpass, err := models.CryptoPass(f.NewPass) - dangerous(err) + return auth.ChangePassword(user, f.NewPass) + }() - user := loginUser(c) - if user.Password != oldpass { - bomb("old password error") - } - - user.Password = newpass - renderMessage(c, user.Update("password")) + renderMessage(c, err) } func selfTokenGets(c *gin.Context) { diff --git a/src/modules/rdb/http/router_user.go b/src/modules/rdb/http/router_user.go index e179592b..28eea7da 100644 --- a/src/modules/rdb/http/router_user.go +++ b/src/modules/rdb/http/router_user.go @@ -1,6 +1,7 @@ package http import ( + "encoding/json" "fmt" "strings" "time" @@ -9,6 +10,7 @@ import ( "github.com/toolkits/pkg/str" "github.com/didi/nightingale/src/models" + "github.com/didi/nightingale/src/modules/rdb/auth" ) // 通讯录,只要登录用户就可以看,超管要修改某个用户的信息,也是调用这个接口获取列表先 @@ -64,21 +66,25 @@ func userAddPost(c *gin.Context) { var f userProfileForm bind(c, &f) - dangerous(checkPassword(f.Password)) + dangerous(auth.CheckPassword(f.Password)) pass, err := models.CryptoPass(f.Password) dangerous(err) + now := time.Now().Unix() + b, _ := json.Marshal([]string{pass}) u := models.User{ - Username: f.Username, - Password: pass, - Dispname: f.Dispname, - Phone: f.Phone, - Email: f.Email, - Im: f.Im, - IsRoot: f.IsRoot, - LeaderId: f.LeaderId, - UUID: models.GenUUIDForUser(f.Username), + Username: f.Username, + Password: pass, + Passwords: string(b), + Dispname: f.Dispname, + Phone: f.Phone, + Email: f.Email, + Im: f.Im, + IsRoot: f.IsRoot, + LeaderId: f.LeaderId, + UpdatedAt: now, + UUID: models.GenUUIDForUser(f.Username), } if f.LeaderId != 0 { @@ -144,19 +150,17 @@ func userProfilePut(c *gin.Context) { target.IsRoot = f.IsRoot } - if f.Typ != target.Typ { - arr = append(arr, fmt.Sprintf("typ: %d -> %d", target.Typ, f.Typ)) - target.Typ = f.Typ + if f.Typ != target.Type { + arr = append(arr, fmt.Sprintf("typ: %d -> %d", target.Type, f.Typ)) + target.Type = f.Typ } if f.Status != target.Status { arr = append(arr, fmt.Sprintf("typ: %d -> %d", target.Status, f.Status)) target.Status = f.Status - } - - if f.Status != target.Status { - arr = append(arr, fmt.Sprintf("typ: %s -> %s", target.Status, f.Status)) - target.Status = f.Status + if target.Status == models.USER_S_ACTIVE { + target.LoginErrNum = 0 + } } if f.Organization != target.Organization { @@ -164,7 +168,9 @@ func userProfilePut(c *gin.Context) { target.Organization = f.Organization } - err := target.Update("dispname", "phone", "email", "im", "is_root", "leader_id", "leader_name", "typ", "status", "organization") + target.UpdatedAt = time.Now().Unix() + + err := target.Update("dispname", "phone", "email", "im", "is_root", "leader_id", "leader_name", "typ", "status", "organization", "login_err_num", "updated_at") if err == nil && len(arr) > 0 { content := strings.Join(arr, ",") go models.OperationLogNew(root.Username, "user", target.Id, fmt.Sprintf("UserModify %s %s", target.Username, content)) @@ -182,17 +188,13 @@ func userPasswordPut(c *gin.Context) { var f userPasswordForm bind(c, &f) - dangerous(checkPassword(f.Password)) + dangerous(auth.CheckPassword(f.Password)) - target := User(urlParamInt64(c, "id")) + user := User(urlParamInt64(c, "id")) + err := auth.ChangePassword(user, f.Password) - pass, err := models.CryptoPass(f.Password) - dangerous(err) - - target.Password = pass - err = target.Update("password") if err == nil { - go models.OperationLogNew(root.Username, "user", target.Id, fmt.Sprintf("UserChangePassword %s", target.Username)) + go models.OperationLogNew(root.Username, "user", user.Id, fmt.Sprintf("UserChangePassword %s", user.Username)) } renderMessage(c, err) } @@ -221,10 +223,6 @@ func userDel(c *gin.Context) { renderMessage(c, err) } -func v1UsernameGetByUUID(c *gin.Context) { - renderData(c, models.UsernameByUUID(queryStr(c, "uuid")), nil) -} - func v1UserGetByUUID(c *gin.Context) { user, err := models.UserGet("uuid=?", queryStr(c, "uuid")) dangerous(err) @@ -302,26 +300,40 @@ type userInviteForm struct { func userInvitePost(c *gin.Context) { var f userInviteForm bind(c, &f) - dangerous(checkPassword(f.Password)) - inv, err := models.InviteGet("token=?", f.Token) - dangerous(err) + err := func() error { + if err := auth.CheckPassword(f.Password); err != nil { + return err + } - if inv.Expire < time.Now().Unix() { - dangerous("invite url already expired") - } + inv, err := models.InviteGet("token=?", f.Token) + if err != nil { + return err + } - u := models.User{ - Username: f.Username, - Dispname: f.Dispname, - Phone: f.Phone, - Email: f.Email, - Im: f.Im, - UUID: models.GenUUIDForUser(f.Username), - } + if inv.Expire < time.Now().Unix() { + return _e("invite url already expired") + } - u.Password, err = models.CryptoPass(f.Password) - dangerous(err) + u := models.User{ + Username: f.Username, + Dispname: f.Dispname, + Phone: f.Phone, + Email: f.Email, + Im: f.Im, + UUID: models.GenUUIDForUser(f.Username), + } - renderMessage(c, u.Save()) + u.Password, err = models.CryptoPass(f.Password) + if err != nil { + return err + } + if err = u.Save(); err != nil { + return err + } + + return inv.Del() + }() + + renderMessage(c, err) } diff --git a/src/modules/rdb/rdb.go b/src/modules/rdb/rdb.go index e2973cf2..c5772671 100644 --- a/src/modules/rdb/rdb.go +++ b/src/modules/rdb/rdb.go @@ -14,11 +14,14 @@ import ( "github.com/didi/nightingale/src/common/loggeri" "github.com/didi/nightingale/src/models" + "github.com/didi/nightingale/src/modules/rdb/auth" + "github.com/didi/nightingale/src/modules/rdb/cache" "github.com/didi/nightingale/src/modules/rdb/config" "github.com/didi/nightingale/src/modules/rdb/cron" "github.com/didi/nightingale/src/modules/rdb/http" "github.com/didi/nightingale/src/modules/rdb/rabbitmq" "github.com/didi/nightingale/src/modules/rdb/redisc" + "github.com/didi/nightingale/src/modules/rdb/session" "github.com/didi/nightingale/src/modules/rdb/ssoc" "github.com/didi/nightingale/src/toolkits/i18n" ) @@ -72,6 +75,12 @@ func main() { // 初始化 rabbitmq 处理部分异步逻辑 rabbitmq.Init() + cache.Start() + session.Init() + + auth.Init(config.Config.Auth.ExtraMode) + auth.Start() + go cron.ConsumeMail() go cron.ConsumeSms() go cron.ConsumeVoice() @@ -102,6 +111,8 @@ func endingProc() { http.Shutdown() redisc.CloseRedis() rabbitmq.Shutdown() + session.Stop() + cache.Stop() fmt.Println("process stopped successfully") } diff --git a/src/modules/rdb/session/session.go b/src/modules/rdb/session/session.go new file mode 100644 index 00000000..6693a3b0 --- /dev/null +++ b/src/modules/rdb/session/session.go @@ -0,0 +1,288 @@ +package session + +import ( + "context" + "fmt" + "net/http" + "net/url" + "sync" + "time" + + "github.com/didi/nightingale/src/models" + "github.com/didi/nightingale/src/modules/rdb/config" + "github.com/google/uuid" + "github.com/toolkits/pkg/logger" +) + +type storage interface { + all() int + get(sid string) (*models.Session, error) + insert(*models.Session) error + del(sid string) error + update(*models.Session) error +} + +var ( + DefaultSession *Manager +) + +func Init() { + var err error + DefaultSession, err = StartSession(&config.Config.HTTP.Session) + if err != nil { + panic(err) + } +} + +func Stop() { + DefaultSession.StopGC() +} + +func Start(w http.ResponseWriter, r *http.Request) (store *SessionStore, err error) { + return DefaultSession.Start(w, r) +} + +func Destroy(w http.ResponseWriter, r *http.Request) (string, error) { + return DefaultSession.Destroy(w, r) +} + +func Get(sid string) (*SessionStore, error) { + return DefaultSession.Get(sid) +} + +func Exist(sid string) bool { + return DefaultSession.Exist(sid) +} + +func All() int { + return DefaultSession.All() +} + +func StartSession(cf *config.SessionSection, opts_ ...Option) (*Manager, error) { + opts := &options{} + + for _, opt := range opts_ { + opt.apply(opts) + } + + if opts.ctx == nil { + opts.ctx, opts.cancel = context.WithCancel(context.Background()) + } + + var storage storage + var err error + if cf.Storage == "mem" { + storage, err = newMemStorage(cf, opts) + } else { + storage, err = newDbStorage(cf, opts) + } + + if err != nil { + return nil, err + } + + return &Manager{ + storage: storage, + options: opts, + config: cf, + }, nil +} + +type Manager struct { + storage + *options + config *config.SessionSection +} + +// SessionStart generate or read the session id from http request. +// if session id exists, return SessionStore with this id. +func (p *Manager) Start(w http.ResponseWriter, r *http.Request) (store *SessionStore, err error) { + var sid string + + if sid, err = p.getSid(r); err != nil { + return + } + + if sid != "" { + if store, err := p.getSessionStore(sid, false); err == nil { + return store, nil + } + } + + // Generate a new session + sid = uuid.New().String() + + store, err = p.getSessionStore(sid, true) + if err != nil { + return nil, err + } + cookie := &http.Cookie{ + Name: p.config.CookieName, + Value: url.QueryEscape(sid), + Path: "/", + HttpOnly: p.config.HttpOnly, + Domain: p.config.Domain, + } + if p.config.CookieLifetime > 0 { + cookie.MaxAge = int(p.config.CookieLifetime) + cookie.Expires = time.Now().Add(time.Duration(p.config.CookieLifetime) * time.Second) + } + http.SetCookie(w, cookie) + r.AddCookie(cookie) + return +} + +func (p *Manager) StopGC() { + if p.cancel != nil { + p.cancel() + } +} + +func (p *Manager) Destroy(w http.ResponseWriter, r *http.Request) (string, error) { + cookie, err := r.Cookie(p.config.CookieName) + if err != nil || cookie.Value == "" { + return "", fmt.Errorf("Have not login yet") + } + + sid, _ := url.QueryUnescape(cookie.Value) + logger.Debugf("session Destory sid %s", sid) + p.del(sid) + + cookie = &http.Cookie{Name: p.config.CookieName, + Path: "/", + HttpOnly: p.config.HttpOnly, + Expires: time.Now(), + MaxAge: -1} + + http.SetCookie(w, cookie) + return sid, nil +} + +func (p *Manager) Get(sid string) (*SessionStore, error) { + return p.getSessionStore(sid, true) +} + +func (p *Manager) Exist(sid string) bool { + _, err := p.get(sid) + return err == nil +} + +// All count values in mysql session +func (p *Manager) All() int { + return p.all() +} + +func (p *Manager) getSid(r *http.Request) (sid string, err error) { + var cookie *http.Cookie + + cookie, err = r.Cookie(p.config.CookieName) + if err != nil || cookie.Value == "" { + return sid, nil + } + + return url.QueryUnescape(cookie.Value) +} + +func (p *Manager) getSessionStore(sid string, create bool) (*SessionStore, error) { + sc, err := p.get(sid) + if sc == nil && create { + ts := time.Now().Unix() + sc = &models.Session{ + Sid: sid, + CreatedAt: ts, + UpdatedAt: ts, + } + err = p.insert(sc) + } + if err != nil { + return nil, err + } + return &SessionStore{manager: p, session: sc}, nil +} + +// SessionStore mysql session store +type SessionStore struct { + sync.RWMutex + session *models.Session + manager *Manager +} + +// Set value in mysql session. +// it is temp value in map. +func (p *SessionStore) Set(k, v string) error { + p.Lock() + defer p.Unlock() + switch k { + case "username": + p.session.Username = v + case "remoteAddr": + p.session.RemoteAddr = v + default: + fmt.Errorf("unsupported session field %s", k) + } + return nil +} + +// Get value from mysql session +func (p *SessionStore) Get(k string) string { + p.RLock() + defer p.RUnlock() + switch k { + case "username": + return p.session.Username + case "remoteAddr": + return p.session.RemoteAddr + default: + return "" + } +} + +func (p *SessionStore) CreatedAt() int64 { + return p.session.CreatedAt +} + +// Delete value in mysql session +func (p *SessionStore) Delete(k string) error { + return p.Set(k, "") +} + +// Reset clear all values in mysql session +func (p *SessionStore) Reset() error { + p.Lock() + defer p.Unlock() + p.session.Username = "" + p.session.RemoteAddr = "" + return nil +} + +// Sid get session id of this mysql session store +func (p *SessionStore) Sid() string { + return p.session.Sid +} + +func (p *SessionStore) Update(w http.ResponseWriter) error { + p.session.UpdatedAt = time.Now().Unix() + return p.manager.update(p.session) +} + +const sessionKey = "context-session-key" + +type contextKeyT string + +var contextKey = contextKeyT("session") + +/* + ctx := NewContext(req.Context(), p) + req = req.WithContext(ctx) +*/ +// NewContext returns a copy of the parent context +// and associates it with an sessionStore. +func NewContext(ctx context.Context, s *SessionStore) context.Context { + return context.WithValue(ctx, contextKey, s) +} + +// FromContext returns the Auth bound to the context, if any. +func FromContext(ctx context.Context) (s *SessionStore, ok bool) { + s, ok = ctx.Value(contextKey).(*SessionStore) + return +} diff --git a/src/modules/rdb/session/session_db.go b/src/modules/rdb/session/session_db.go new file mode 100644 index 00000000..0c7b7e21 --- /dev/null +++ b/src/modules/rdb/session/session_db.go @@ -0,0 +1,73 @@ +package session + +import ( + "time" + + "github.com/didi/nightingale/src/models" + "github.com/didi/nightingale/src/modules/rdb/cache" + "github.com/didi/nightingale/src/modules/rdb/config" + "github.com/toolkits/pkg/logger" +) + +func newDbStorage(cf *config.SessionSection, opts *options) (storage, error) { + st := &dbStorage{config: cf} + + go func() { + t := time.NewTicker(time.Second * time.Duration(cf.GcInterval)) + defer t.Stop() + for { + select { + case <-opts.ctx.Done(): + return + case <-t.C: + if st := cache.AuthConfig().MaxConnIdelTime * 60; st > 0 { + err := models.SessionCleanup(time.Now().Unix() - st) + if err != nil { + logger.Errorf("session gc err %s", err) + } + } else { + ct := config.Config.HTTP.Session.CookieLifetime + if ct == 0 { + ct = 86400 + err := models.SessionCleanupByCreatedAt(time.Now().Unix() - ct) + if err != nil { + logger.Errorf("session gc err %s", err) + } + } + } + + } + } + }() + + return st, nil +} + +type dbStorage struct { + config *config.SessionSection +} + +func (p *dbStorage) all() int { + n, err := models.SessionAll() + if err != nil { + logger.Errorf("sessionAll() err %s", err) + } + return int(n) +} + +func (p *dbStorage) get(sid string) (*models.Session, error) { + return models.SessionGet(sid) +} + +func (p *dbStorage) insert(s *models.Session) error { + return models.SessionInsert(s) + +} + +func (p *dbStorage) del(sid string) error { + return models.SessionDel(sid) +} + +func (p *dbStorage) update(s *models.Session) error { + return models.SessionUpdate(s) +} diff --git a/src/modules/rdb/session/session_mem.go b/src/modules/rdb/session/session_mem.go new file mode 100644 index 00000000..8da70bf0 --- /dev/null +++ b/src/modules/rdb/session/session_mem.go @@ -0,0 +1,99 @@ +package session + +import ( + "fmt" + "sync" + "time" + + "github.com/didi/nightingale/src/models" + "github.com/didi/nightingale/src/modules/rdb/cache" + "github.com/didi/nightingale/src/modules/rdb/config" +) + +func newMemStorage(cf *config.SessionSection, opts *options) (storage, error) { + st := &mStorage{ + data: make(map[string]*models.Session), + opts: opts, + config: cf, + } + + go func() { + t := time.NewTicker(time.Second * time.Duration(cf.GcInterval)) + defer t.Stop() + for { + select { + case <-opts.ctx.Done(): + return + case <-t.C: + st.gc() + } + } + }() + + return st, nil +} + +type mStorage struct { + sync.RWMutex + data map[string]*models.Session + + opts *options + config *config.SessionSection +} + +func (p *mStorage) all() int { + p.RLock() + defer p.RUnlock() + return len(p.data) +} + +func (p *mStorage) get(sid string) (*models.Session, error) { + p.RLock() + defer p.RUnlock() + s, ok := p.data[sid] + if !ok { + return nil, fmt.Errorf("sid %s is not found", sid) + } + return s, nil +} + +func (p *mStorage) insert(s *models.Session) error { + p.Lock() + defer p.Unlock() + + p.data[s.Sid] = s + return nil +} + +func (p *mStorage) del(sid string) error { + p.Lock() + defer p.Unlock() + + delete(p.data, sid) + return nil +} + +func (p *mStorage) update(s *models.Session) error { + p.Lock() + defer p.Unlock() + + p.data[s.Sid] = s + return nil +} + +func (p *mStorage) gc() { + p.Lock() + defer p.Unlock() + + expiresAt := time.Now().Unix() - cache.AuthConfig().MaxConnIdelTime*60 + keys := []string{} + for k, v := range p.data { + if v.UpdatedAt < expiresAt { + keys = append(keys, k) + } + } + + for _, k := range keys { + delete(p.data, k) + } +} diff --git a/src/modules/rdb/session/session_options.go b/src/modules/rdb/session/session_options.go new file mode 100644 index 00000000..6cb92b7d --- /dev/null +++ b/src/modules/rdb/session/session_options.go @@ -0,0 +1,42 @@ +package session + +import ( + "context" +) + +type options struct { + ctx context.Context + cancel context.CancelFunc + mem bool +} + +type Option interface { + apply(*options) +} + +type funcOption struct { + f func(*options) +} + +func (p *funcOption) apply(opt *options) { + p.f(opt) +} + +func newFuncOption(f func(*options)) *funcOption { + return &funcOption{ + f: f, + } +} + +func WithCtx(ctx context.Context) Option { + return newFuncOption(func(o *options) { + o.ctx = ctx + o.cancel = nil + }) +} + +func WithMem() Option { + return newFuncOption(func(o *options) { + o.mem = true + }) +} diff --git a/src/modules/rdb/session/session_test.go b/src/modules/rdb/session/session_test.go new file mode 100644 index 00000000..ab876163 --- /dev/null +++ b/src/modules/rdb/session/session_test.go @@ -0,0 +1 @@ +package session diff --git a/src/modules/rdb/ssoc/sso.go b/src/modules/rdb/ssoc/sso.go index 44fe6da4..39d7d734 100644 --- a/src/modules/rdb/ssoc/sso.go +++ b/src/modules/rdb/ssoc/sso.go @@ -96,7 +96,6 @@ func Authorize(redirect string) (string, error) { return "", err } - // log.Printf("add state %s", state) return cli.config.AuthCodeURL(state.State), nil } @@ -116,13 +115,11 @@ func Callback(code, state string) (string, *models.User, error) { } s.Del() - // log.Printf("remove state %s", state) u, err := exchangeUser(code) if err != nil { return "", nil, errUser } - // log.Printf("exchange user %v", u) user, err := models.UserGet("username=?", u.Username) if err != nil { diff --git a/src/modules/transfer/backend/datasource.go b/src/modules/transfer/backend/datasource.go index 899aabac..09993ef0 100644 --- a/src/modules/transfer/backend/datasource.go +++ b/src/modules/transfer/backend/datasource.go @@ -30,7 +30,7 @@ type DataSource interface { QueryMetrics(recv dataobj.EndpointsRecv) *dataobj.MetricResp QueryTagPairs(recv dataobj.EndpointMetricRecv) []dataobj.IndexTagkvResp QueryIndexByClude(recv []dataobj.CludeRecv) []dataobj.XcludeResp - QueryIndexByFullTags(recv []dataobj.IndexByFullTagsRecv) []dataobj.IndexByFullTagsResp + QueryIndexByFullTags(recv []dataobj.IndexByFullTagsRecv) ([]dataobj.IndexByFullTagsResp, int) // tsdb instance GetInstance(metric, endpoint string, tags map[string]string) []string diff --git a/src/modules/transfer/backend/influxdb/query.go b/src/modules/transfer/backend/influxdb/query.go index 7c343dc7..127edb62 100644 --- a/src/modules/transfer/backend/influxdb/query.go +++ b/src/modules/transfer/backend/influxdb/query.go @@ -407,7 +407,7 @@ func (influxdb *InfluxdbDataSource) QueryIndexByClude(recvs []dataobj.CludeRecv) } // show series from metric where ... -func (influxdb *InfluxdbDataSource) QueryIndexByFullTags(recvs []dataobj.IndexByFullTagsRecv) []dataobj.IndexByFullTagsResp { +func (influxdb *InfluxdbDataSource) QueryIndexByFullTags(recvs []dataobj.IndexByFullTagsRecv) ([]dataobj.IndexByFullTagsResp, int) { logger.Debugf("query IndexByFullTags , recv: %+v", recvs) c, err := NewInfluxdbClient(influxdb.Section) @@ -415,7 +415,7 @@ func (influxdb *InfluxdbDataSource) QueryIndexByFullTags(recvs []dataobj.IndexBy if err != nil { logger.Errorf("init influxdb client fail: %v", err) - return nil + return nil, 0 } resp := make([]dataobj.IndexByFullTagsResp, 0) @@ -483,7 +483,7 @@ func (influxdb *InfluxdbDataSource) QueryIndexByFullTags(recvs []dataobj.IndexBy resp = append(resp, fullTagResp) } - return resp + return resp, len(resp) } func convertValues(series models.Row) []*dataobj.RRDData { diff --git a/src/modules/transfer/backend/m3db/.gitignore b/src/modules/transfer/backend/m3db/.gitignore new file mode 100644 index 00000000..65e3ba2e --- /dev/null +++ b/src/modules/transfer/backend/m3db/.gitignore @@ -0,0 +1 @@ +test/ diff --git a/src/modules/transfer/backend/m3db/m3db.go b/src/modules/transfer/backend/m3db/m3db.go index 9734d52f..1fab6345 100644 --- a/src/modules/transfer/backend/m3db/m3db.go +++ b/src/modules/transfer/backend/m3db/m3db.go @@ -256,19 +256,24 @@ func (p *Client) queryIndexByClude(session client.Session, input dataobj.CludeRe // QueryIndexByFullTags: && (|| endpoints...) (metric) (&& Tagkv...) // return all the tags that matches -func (p *Client) QueryIndexByFullTags(inputs []dataobj.IndexByFullTagsRecv) []dataobj.IndexByFullTagsResp { +func (p *Client) QueryIndexByFullTags(inputs []dataobj.IndexByFullTagsRecv) ([]dataobj.IndexByFullTagsResp, int) { session, err := p.session() if err != nil { logger.Errorf("unable to get m3db session: %s", err) - return nil + return nil, 0 } - ret := make([]dataobj.IndexByFullTagsResp, len(inputs)) + list := make([]dataobj.IndexByFullTagsResp, len(inputs)) + count := 0 + + var resp dataobj.IndexByFullTagsResp for i, input := range inputs { - ret[i] = p.queryIndexByFullTags(session, input) + resp = p.queryIndexByFullTags(session, input) + list[i] = resp + count += resp.Count } - return ret + return list, count } func (p *Client) queryIndexByFullTags(session client.Session, input dataobj.IndexByFullTagsRecv) (ret dataobj.IndexByFullTagsResp) { @@ -293,6 +298,7 @@ func (p *Client) queryIndexByFullTags(session client.Session, input dataobj.Inde ret.Endpoints = input.Endpoints ret.Nids = input.Nids + ret.Count = iter.Remaining() tags := map[string]struct{}{} for iter.Next() { _, _, tagIter := iter.Current() diff --git a/src/modules/transfer/backend/m3db/test/01-push-data.sh b/src/modules/transfer/backend/m3db/test/01-push-data.sh deleted file mode 100755 index 619dfeda..00000000 --- a/src/modules/transfer/backend/m3db/test/01-push-data.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash - -# type MetricValue struct { -# Nid string `json:"nid"` -# Metric string `json:"metric"` -# Endpoint string `json:"endpoint"` -# Timestamp int64 `json:"timestamp"` -# Step int64 `json:"step"` -# ValueUntyped interface{} `json:"value"` -# Value float64 `json:"-"` -# CounterType string `json:"counterType"` -# Tags string `json:"tags"` -# TagsMap map[string]string `json:"tagsMap"` //保留2种格式,方便后端组件使用 -# Extra string `json:"extra"` -# } - - -curl -X POST \ - http://localhost:8008/api/transfer/push \ --d '[{ - "metric": "test", - "endpoint": "m3db-dev01-yubo.py", - "timestamp": '$(date "+%s")', - "step": 60, - "value": 1.111, - "counterType": "GAUGE", - "tags": "", - "tagsMap": { - "city":"bj", - "region":"c1", - "test": "end" - }, - "extra": "" -}]' - diff --git a/src/modules/transfer/backend/m3db/test/02-query-data.sh b/src/modules/transfer/backend/m3db/test/02-query-data.sh deleted file mode 100755 index 53b0ea35..00000000 --- a/src/modules/transfer/backend/m3db/test/02-query-data.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash - -# type QueryData struct { -# Start int64 `json:"start"` -# End int64 `json:"end"` -# ConsolFunc string `json:"consolFunc"` -# Endpoints []string `json:"endpoints"` -# Nids []string `json:"nids"` -# Counters []string `json:"counters"` -# Step int `json:"step"` -# DsType string `json:"dstype"` -# } - - -curl -X POST \ - http://localhost:8008/api/transfer/data \ --d '[{ - "start": '$(date -d "1 hour ago" "+%s")', - "end": '$(date "+%s")', - "consolFunc": "", - "endpoints": ["m3db-dev01-yubo.py"], - "counters": [], - "step": 60, - "dstype": "GAUGE" -}]' | jq . - diff --git a/src/modules/transfer/backend/m3db/test/03-query-data-for-ui.sh b/src/modules/transfer/backend/m3db/test/03-query-data-for-ui.sh deleted file mode 100755 index de3bbf2e..00000000 --- a/src/modules/transfer/backend/m3db/test/03-query-data-for-ui.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash - -# type QueryDataForUI struct { -# Start int64 `json:"start"` -# End int64 `json:"end"` -# Metric string `json:"metric"` -# Endpoints []string `json:"endpoints"` -# Nids []string `json:"nids"` -# Tags []string `json:"tags"` -# Step int `json:"step"` -# DsType string `json:"dstype"` -# GroupKey []string `json:"groupKey"` //聚合维度 -# AggrFunc string `json:"aggrFunc"` //聚合计算 -# ConsolFunc string `json:"consolFunc"` -# Comparisons []int64 `json:"comparisons"` //环比多少时间 -# } - - - - -curl -X POST \ - http://localhost:8008/api/transfer/data/ui \ --d '{ - "start": '$(date -d "1 hour ago" "+%s")', - "end": '$(date "+%s")', - "metric": "test", - "endpoints": ["m3db-dev01-yubo.py"], - "nids": [], - "tags": [], - "step": 60, - "dstype": "", - "groupKey": [], - "aggrFunc": "", - "consolFunc": "", - "comparisons": [] -}' | jq . - - diff --git a/src/modules/transfer/backend/m3db/test/04-query-metrics.sh b/src/modules/transfer/backend/m3db/test/04-query-metrics.sh deleted file mode 100755 index f3e34c5e..00000000 --- a/src/modules/transfer/backend/m3db/test/04-query-metrics.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -# type EndpointsRecv struct { -# Endpoints []string `json:"endpoints"` -# } - -curl -X POST \ - http://localhost:8008/api/index/metrics \ --d '{ - "endpoints": ["10.178.24.120", "10.178.25.123"] -}' - - diff --git a/src/modules/transfer/backend/m3db/test/05-query-tag-pairs.sh b/src/modules/transfer/backend/m3db/test/05-query-tag-pairs.sh deleted file mode 100755 index 1c77912f..00000000 --- a/src/modules/transfer/backend/m3db/test/05-query-tag-pairs.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -# type EndpointMetricRecv struct { -# Endpoints []string `json:"endpoints"` -# Metrics []string `json:"metrics"` -# } - -curl -X POST \ - http://localhost:8008/api/index/tagkv \ --d '{ - "endpoints": [], - "metrics": ["test"] -}' | jq . - - diff --git a/src/modules/transfer/backend/m3db/test/06-query-index-by-clude.sh b/src/modules/transfer/backend/m3db/test/06-query-index-by-clude.sh deleted file mode 100755 index c588633f..00000000 --- a/src/modules/transfer/backend/m3db/test/06-query-index-by-clude.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - -# data:[{Nid: Endpoint:10.86.76.13 Metric:disk.bytes.used.percent Tags:[mount=/tmp] Step:0 Dstype:}] - -# type CludeRecv struct { -# Endpoints []string `json:"endpoints"` -# Metric string `json:"metric"` -# Include []*TagPair `json:"include"` -# Exclude []*TagPair `json:"exclude"` -# } - - -curl -X POST \ - http://localhost:8008/api/index/counter/clude \ --d '[{ - "endpoints": ["10.86.76.13"], - "metric": "disk.bytes.used.percent", - "exclude": [{"tagk":"mount", "tagv": ["/boot"]}], - "include": [{"tagk":"mount", "tagv": ["/", "/home"]}] -}]' | jq . diff --git a/src/modules/transfer/backend/m3db/test/07-query-index-by-full-tags.sh b/src/modules/transfer/backend/m3db/test/07-query-index-by-full-tags.sh deleted file mode 100755 index a9487cc1..00000000 --- a/src/modules/transfer/backend/m3db/test/07-query-index-by-full-tags.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -# type IndexByFullTagsRecv struct { -# Endpoints []string `json:"endpoints"` -# Metric string `json:"metric"` -# Tagkv []TagPair `json:"tagkv"` -# } - -curl -X POST \ - http://localhost:8008/api/index/counter/fullmatch \ --d '[{ - "endpoints": ["m3db-dev01-yubo.py"], - "metric": "test2", - "tagkv": [] -}]' | jq . - - - diff --git a/src/modules/transfer/backend/m3db/test/08-get-instances.sh b/src/modules/transfer/backend/m3db/test/08-get-instances.sh deleted file mode 100755 index 75488237..00000000 --- a/src/modules/transfer/backend/m3db/test/08-get-instances.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -curl -X POST \ - http://localhost:8008/api/transfer/which-tsdb -d '{}' - diff --git a/src/modules/transfer/backend/tsdb/query.go b/src/modules/transfer/backend/tsdb/query.go index ec948a3f..9e32d3de 100644 --- a/src/modules/transfer/backend/tsdb/query.go +++ b/src/modules/transfer/backend/tsdb/query.go @@ -443,21 +443,22 @@ type IndexByFullTagsResp struct { Err string `json:"err"` } -func (tsdb *TsdbDataSource) QueryIndexByFullTags(recv []dataobj.IndexByFullTagsRecv) []dataobj.IndexByFullTagsResp { +// deprecated +func (tsdb *TsdbDataSource) QueryIndexByFullTags(recv []dataobj.IndexByFullTagsRecv) ([]dataobj.IndexByFullTagsResp, int) { var result IndexByFullTagsResp err := PostIndex("/api/index/counter/fullmatch", int64(tsdb.Section.CallTimeout), recv, &result) if err != nil { logger.Errorf("post index failed, %+v", err) - return nil + return nil, 0 } if result.Err != "" || len(result.Data) == 0 { logger.Errorf("index fullTags failed, %+v", result.Err) - return nil + return nil, 0 } - return result.Data + return result.Data, len(result.Data) } func PostIndex(url string, calltimeout int64, recv interface{}, resp interface{}) error { diff --git a/src/modules/transfer/http/query_router.go b/src/modules/transfer/http/query_router.go index 31f6be95..e2a8d6d8 100644 --- a/src/modules/transfer/http/query_router.go +++ b/src/modules/transfer/http/query_router.go @@ -148,8 +148,8 @@ func GetIndexByFullTags(c *gin.Context) { return } - resp := dataSource.QueryIndexByFullTags(recvs) - render.Data(c, &listResp{List: resp, Count: len(resp)}, nil) + resp, count := dataSource.QueryIndexByFullTags(recvs) + render.Data(c, &listResp{List: resp, Count: count}, nil) } type listResp struct {