From 28bf2fe3f7c0b013685921c9d4bfdb254ac42e81 Mon Sep 17 00:00:00 2001 From: Ankit Nayan Date: Mon, 22 Nov 2021 16:15:58 +0530 Subject: [PATCH] feat: enables prometheus rules and alerts (#292) * feat: enables prometheus rules and alerts which can be sent to alertmanager * chore: adding configs for alertmanager, alert, and prom * chore: alerts WIP * chore: alerts WIP * chore: alerts WIP * chore: setRules API will update rules * chore: initialization of prometheus related stuff moved to separate function * chore: alerts WIP * chore: alerts WIP * fix: r.promConfig was nil * feat: routing alertmanager apis to alertmanager service at nginx * chore: not writing to localDB if string parsing gives error * feat: list alerts API * chore: error in creating multiple groups * feat: CRUD APIs for rules working * chore: changed prometheus version * chore: updated AlertingRuleResponse struct's Id json value * chore: updated prometheus's version * chore: will load rules from database on bootup * feat: crud APIs for notification channels WIP * fix: changed ALERTMANAGER_API_PREFIX * chore: enabling scrape and notify discover manager * chore: fixing path for signoz.db * chore: used transactions for rules APIs * chore: editchannel API updated and other apis refactored * chore: fixed merge conflicts * chore: changing createChannel api from yaml to json reader * chore: changing editChannel api from yaml to json reader * chore: porting loadChannels to json format * chore: editRule returning rule not found * chore: pre-release * chore: fixed db path for persistence * release: v0.5.0 --- .../docker/clickhouse-setup/alertmanager.yml | 35 + deploy/docker/clickhouse-setup/alerts.yml | 11 + .../clickhouse-setup/docker-compose.yaml | 15 +- deploy/docker/clickhouse-setup/prometheus.yml | 3 +- deploy/docker/common/nginx-config.conf | 3 + .../app/clickhouseReader/reader.go | 973 +++++++++++++++++- pkg/query-service/app/dashboards/model.go | 47 +- pkg/query-service/app/druidReader/reader.go | 48 +- pkg/query-service/app/http_handler.go | 181 +++- pkg/query-service/app/interface.go | 13 +- pkg/query-service/app/parser.go | 4 + pkg/query-service/app/server.go | 12 +- pkg/query-service/config/alerts.yml | 11 + pkg/query-service/config/prometheus.yml | 5 +- pkg/query-service/constants/constants.go | 2 + pkg/query-service/go.mod | 9 +- pkg/query-service/go.sum | 83 ++ pkg/query-service/model/response.go | 53 + 18 files changed, 1456 insertions(+), 52 deletions(-) create mode 100644 deploy/docker/clickhouse-setup/alertmanager.yml create mode 100644 deploy/docker/clickhouse-setup/alerts.yml create mode 100644 pkg/query-service/config/alerts.yml diff --git a/deploy/docker/clickhouse-setup/alertmanager.yml b/deploy/docker/clickhouse-setup/alertmanager.yml new file mode 100644 index 0000000000..d69357f9dd --- /dev/null +++ b/deploy/docker/clickhouse-setup/alertmanager.yml @@ -0,0 +1,35 @@ +global: + resolve_timeout: 1m + slack_api_url: 'https://hooks.slack.com/services/xxx' + +route: + receiver: 'slack-notifications' + +receivers: +- name: 'slack-notifications' + slack_configs: + - channel: '#alerts' + send_resolved: true + icon_url: https://avatars3.githubusercontent.com/u/3380462 + title: |- + [{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .CommonLabels.alertname }} for {{ .CommonLabels.job }} + {{- if gt (len .CommonLabels) (len .GroupLabels) -}} + {{" "}}( + {{- with .CommonLabels.Remove .GroupLabels.Names }} + {{- range $index, $label := .SortedPairs -}} + {{ if $index }}, {{ end }} + {{- $label.Name }}="{{ $label.Value -}}" + {{- end }} + {{- end -}} + ) + {{- end }} + text: >- + {{ range .Alerts -}} + *Alert:* {{ .Annotations.title }}{{ if .Labels.severity }} - `{{ .Labels.severity }}`{{ end }} + + *Description:* {{ .Annotations.description }} + + *Details:* + {{ range .Labels.SortedPairs }} • *{{ .Name }}:* `{{ .Value }}` + {{ end }} + {{ end }} \ No newline at end of file diff --git a/deploy/docker/clickhouse-setup/alerts.yml b/deploy/docker/clickhouse-setup/alerts.yml new file mode 100644 index 0000000000..810a20750c --- /dev/null +++ b/deploy/docker/clickhouse-setup/alerts.yml @@ -0,0 +1,11 @@ +groups: +- name: ExampleCPULoadGroup + rules: + - alert: HighCpuLoad + expr: system_cpu_load_average_1m > 0.1 + for: 0m + labels: + severity: warning + annotations: + summary: High CPU load + description: "CPU load is > 0.1\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" diff --git a/deploy/docker/clickhouse-setup/docker-compose.yaml b/deploy/docker/clickhouse-setup/docker-compose.yaml index 4aaf9def0d..822644842e 100644 --- a/deploy/docker/clickhouse-setup/docker-compose.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose.yaml @@ -21,8 +21,19 @@ services: timeout: 5s retries: 3 + alertmanager: + image: signoz/alertmanager:0.5.0 + volumes: + - ./alertmanager.yml:/prometheus/alertmanager.yml + - ./data/alertmanager:/data + command: + - '--config.file=/prometheus/alertmanager.yml' + - '--storage.path=/data' + ports: + - 9093:9093 + query-service: - image: signoz/query-service:0.4.5 + image: signoz/query-service:0.5.0 container_name: query-service command: ["-config=/root/config/prometheus.yml"] ports: @@ -43,7 +54,7 @@ services: condition: service_healthy frontend: - image: signoz/frontend:0.4.5 + image: signoz/frontend:0.5.0 container_name: frontend depends_on: diff --git a/deploy/docker/clickhouse-setup/prometheus.yml b/deploy/docker/clickhouse-setup/prometheus.yml index 7d04428a42..16e65ff18c 100644 --- a/deploy/docker/clickhouse-setup/prometheus.yml +++ b/deploy/docker/clickhouse-setup/prometheus.yml @@ -9,12 +9,13 @@ alerting: alertmanagers: - static_configs: - targets: - # - alertmanager:9093 + - alertmanager:9093 # Load rules once and periodically evaluate them according to the global 'evaluation_interval'. rule_files: # - "first_rules.yml" # - "second_rules.yml" + - 'alerts.yml' # A scrape configuration containing exactly one endpoint to scrape: # Here it's Prometheus itself. diff --git a/deploy/docker/common/nginx-config.conf b/deploy/docker/common/nginx-config.conf index 881cdc77cb..dd5eac0c83 100644 --- a/deploy/docker/common/nginx-config.conf +++ b/deploy/docker/common/nginx-config.conf @@ -16,6 +16,9 @@ server { index index.html index.htm; try_files $uri $uri/ /index.html; } + location /api/alertmanager{ + proxy_pass http://alertmanager:9093/api/v2; + } location /api { proxy_pass http://query-service:8080/api; diff --git a/pkg/query-service/app/clickhouseReader/reader.go b/pkg/query-service/app/clickhouseReader/reader.go index cd1ddf8c74..d526ff8e22 100644 --- a/pkg/query-service/app/clickhouseReader/reader.go +++ b/pkg/query-service/app/clickhouseReader/reader.go @@ -1,24 +1,46 @@ package clickhouseReader import ( + "bytes" "context" - "errors" + "crypto/md5" + "encoding/json" "flag" "fmt" + "io/ioutil" + "net" + "net/http" + "net/url" "os" + "sort" "strconv" "strings" + "sync" "time" + sd_config "github.com/prometheus/prometheus/discovery/config" + "github.com/prometheus/prometheus/scrape" + + "github.com/pkg/errors" + _ "github.com/ClickHouse/clickhouse-go" "github.com/go-kit/log" + "github.com/go-kit/log/level" "github.com/jmoiron/sqlx" + "github.com/oklog/oklog/pkg/group" + "github.com/prometheus/client_golang/prometheus" promModel "github.com/prometheus/common/model" "github.com/prometheus/common/promlog" "github.com/prometheus/prometheus/config" + "github.com/prometheus/prometheus/discovery" + "github.com/prometheus/prometheus/notifier" "github.com/prometheus/prometheus/promql" + "github.com/prometheus/prometheus/rules" + "github.com/prometheus/prometheus/storage" "github.com/prometheus/prometheus/storage/remote" "github.com/prometheus/prometheus/util/stats" + "github.com/prometheus/prometheus/util/strutil" + "github.com/prometheus/tsdb" "go.signoz.io/query-service/constants" "go.signoz.io/query-service/model" @@ -47,15 +69,18 @@ var ( // SpanWriter for reading spans from ClickHouse type ClickHouseReader struct { db *sqlx.DB + localDB *sqlx.DB operationsTable string indexTable string spansTable string queryEngine *promql.Engine remoteStorage *remote.Storage + ruleManager *rules.Manager + promConfig *config.Config } // NewTraceReader returns a TraceReader for the database -func NewReader() *ClickHouseReader { +func NewReader(localDB *sqlx.DB) *ClickHouseReader { datasource := os.Getenv("ClickHouseUrl") options := NewOptions(datasource, primaryNamespace, archiveNamespace) @@ -66,6 +91,16 @@ func NewReader() *ClickHouseReader { os.Exit(1) } + return &ClickHouseReader{ + db: db, + localDB: localDB, + operationsTable: options.primary.OperationsTable, + indexTable: options.primary.IndexTable, + spansTable: options.primary.SpansTable, + } +} + +func (r *ClickHouseReader) Start() { logLevel := promlog.AllowedLevel{} logLevel.Set("debug") // allowedFormat := promlog.AllowedFormat{} @@ -78,6 +113,78 @@ func NewReader() *ClickHouseReader { logger := promlog.New(logLevel) + startTime := func() (int64, error) { + return int64(promModel.Latest), nil + + } + + remoteStorage := remote.NewStorage(log.With(logger, "component", "remote"), startTime, time.Duration(1*time.Minute)) + + // conf, err := config.LoadFile(*filename) + // if err != nil { + // zap.S().Error("couldn't load configuration (--config.file=%q): %v", filename, err) + // } + + // err = remoteStorage.ApplyConfig(conf) + // if err != nil { + // zap.S().Error("Error in remoteStorage.ApplyConfig: ", err) + // } + cfg := struct { + configFile string + + localStoragePath string + notifier notifier.Options + notifierTimeout promModel.Duration + forGracePeriod promModel.Duration + outageTolerance promModel.Duration + resendDelay promModel.Duration + tsdb tsdb.Options + lookbackDelta promModel.Duration + webTimeout promModel.Duration + queryTimeout promModel.Duration + queryConcurrency int + queryMaxSamples int + RemoteFlushDeadline promModel.Duration + + prometheusURL string + + logLevel promlog.AllowedLevel + }{ + notifier: notifier.Options{ + Registerer: prometheus.DefaultRegisterer, + }, + } + + flag.StringVar(&cfg.configFile, "config", "./config/prometheus.yml", "(prometheus config to read metrics)") + flag.Parse() + + // fanoutStorage := remoteStorage + fanoutStorage := storage.NewFanout(logger, remoteStorage) + localStorage := remoteStorage + + cfg.notifier.QueueCapacity = 10000 + cfg.notifierTimeout = promModel.Duration(time.Duration.Seconds(10)) + notifier := notifier.NewManager(&cfg.notifier, log.With(logger, "component", "notifier")) + // notifier.ApplyConfig(conf) + + ExternalURL, err := computeExternalURL("", "0.0.0.0:3000") + if err != nil { + fmt.Fprintln(os.Stderr, errors.Wrapf(err, "parse external URL %q", ExternalURL.String())) + os.Exit(2) + } + + cfg.outageTolerance = promModel.Duration(time.Duration.Hours(1)) + cfg.forGracePeriod = promModel.Duration(time.Duration.Minutes(10)) + cfg.resendDelay = promModel.Duration(time.Duration.Minutes(1)) + + ctxScrape, cancelScrape := context.WithCancel(context.Background()) + discoveryManagerScrape := discovery.NewManager(ctxScrape, log.With(logger, "component", "discovery manager scrape"), discovery.Name("scrape")) + + ctxNotify, cancelNotify := context.WithCancel(context.Background()) + discoveryManagerNotify := discovery.NewManager(ctxNotify, log.With(logger, "component", "discovery manager notify"), discovery.Name("notify")) + + scrapeManager := scrape.NewManager(log.With(logger, "component", "scrape manager"), fanoutStorage) + opts := promql.EngineOpts{ Logger: log.With(logger, "component", "query engine"), Reg: nil, @@ -88,32 +195,314 @@ func NewReader() *ClickHouseReader { queryEngine := promql.NewEngine(opts) - startTime := func() (int64, error) { - return int64(promModel.Latest), nil + ruleManager := rules.NewManager(&rules.ManagerOptions{ + Appendable: fanoutStorage, + TSDB: localStorage, + QueryFunc: rules.EngineQueryFunc(queryEngine, fanoutStorage), + NotifyFunc: sendAlerts(notifier, ExternalURL.String()), + Context: context.Background(), + ExternalURL: ExternalURL, + Registerer: prometheus.DefaultRegisterer, + Logger: log.With(logger, "component", "rule manager"), + OutageTolerance: time.Duration(cfg.outageTolerance), + ForGracePeriod: time.Duration(cfg.forGracePeriod), + ResendDelay: time.Duration(cfg.resendDelay), + }) + + reloaders := []func(cfg *config.Config) error{ + remoteStorage.ApplyConfig, + // The Scrape and notifier managers need to reload before the Discovery manager as + // they need to read the most updated config when receiving the new targets list. + notifier.ApplyConfig, + scrapeManager.ApplyConfig, + func(cfg *config.Config) error { + c := make(map[string]sd_config.ServiceDiscoveryConfig) + for _, v := range cfg.ScrapeConfigs { + c[v.JobName] = v.ServiceDiscoveryConfig + } + return discoveryManagerScrape.ApplyConfig(c) + }, + func(cfg *config.Config) error { + c := make(map[string]sd_config.ServiceDiscoveryConfig) + for _, v := range cfg.AlertingConfig.AlertmanagerConfigs { + // AlertmanagerConfigs doesn't hold an unique identifier so we use the config hash as the identifier. + b, err := json.Marshal(v) + if err != nil { + return err + } + c[fmt.Sprintf("%x", md5.Sum(b))] = v.ServiceDiscoveryConfig + } + return discoveryManagerNotify.ApplyConfig(c) + }, + // func(cfg *config.Config) error { + // // Get all rule files matching the configuration oaths. + // var files []string + // for _, pat := range cfg.RuleFiles { + // fs, err := filepath.Glob(pat) + // if err != nil { + // // The only error can be a bad pattern. + // return fmt.Errorf("error retrieving rule files for %s: %s", pat, err) + // } + // files = append(files, fs...) + // } + // return ruleManager.Update(time.Duration(cfg.GlobalConfig.EvaluationInterval), files) + // }, } - remoteStorage := remote.NewStorage(log.With(logger, "component", "remote"), startTime, time.Duration(1*time.Minute)) + // sync.Once is used to make sure we can close the channel at different execution stages(SIGTERM or when the config is loaded). + type closeOnce struct { + C chan struct{} + once sync.Once + Close func() + } + // Wait until the server is ready to handle reloading. + reloadReady := &closeOnce{ + C: make(chan struct{}), + } + reloadReady.Close = func() { + reloadReady.once.Do(func() { + close(reloadReady.C) + }) + } - filename := flag.String("config", "./config/prometheus.yml", "(prometheus config to read metrics)") - flag.Parse() - conf, err := config.LoadFile(*filename) + var g group.Group + { + // Scrape discovery manager. + g.Add( + func() error { + err := discoveryManagerScrape.Run() + level.Info(logger).Log("msg", "Scrape discovery manager stopped") + return err + }, + func(err error) { + level.Info(logger).Log("msg", "Stopping scrape discovery manager...") + cancelScrape() + }, + ) + } + { + // Notify discovery manager. + g.Add( + func() error { + err := discoveryManagerNotify.Run() + level.Info(logger).Log("msg", "Notify discovery manager stopped") + return err + }, + func(err error) { + level.Info(logger).Log("msg", "Stopping notify discovery manager...") + cancelNotify() + }, + ) + } + { + // Scrape manager. + g.Add( + func() error { + // When the scrape manager receives a new targets list + // it needs to read a valid config for each job. + // It depends on the config being in sync with the discovery manager so + // we wait until the config is fully loaded. + <-reloadReady.C + + err := scrapeManager.Run(discoveryManagerScrape.SyncCh()) + level.Info(logger).Log("msg", "Scrape manager stopped") + return err + }, + func(err error) { + // Scrape manager needs to be stopped before closing the local TSDB + // so that it doesn't try to write samples to a closed storage. + level.Info(logger).Log("msg", "Stopping scrape manager...") + scrapeManager.Stop() + }, + ) + } + { + // Initial configuration loading. + cancel := make(chan struct{}) + g.Add( + func() error { + // select { + // case <-dbOpen: + // break + // // In case a shutdown is initiated before the dbOpen is released + // case <-cancel: + // reloadReady.Close() + // return nil + // } + r.promConfig, err = reloadConfig(cfg.configFile, logger, reloaders...) + if err != nil { + return fmt.Errorf("error loading config from %q: %s", cfg.configFile, err) + } + + reloadReady.Close() + + rules, apiErrorObj := r.GetRulesFromDB() + + if apiErrorObj != nil { + zap.S().Errorf("Not able to read rules from DB") + } + for _, rule := range *rules { + apiErrorObj = r.LoadRule(rule) + if apiErrorObj != nil { + zap.S().Errorf("Not able to load rule with id=%d loaded from DB", rule.Id, rule.Data) + } + } + + channels, apiErrorObj := r.GetChannels() + + if apiErrorObj != nil { + zap.S().Errorf("Not able to read channels from DB") + } + for _, channel := range *channels { + apiErrorObj = r.LoadChannel(&channel) + if apiErrorObj != nil { + zap.S().Errorf("Not able to load channel with id=%d loaded from DB", channel.Id, channel.Data) + } + } + + <-cancel + + return nil + }, + func(err error) { + close(cancel) + }, + ) + } + { + // Rule manager. + // TODO(krasi) refactor ruleManager.Run() to be blocking to avoid using an extra blocking channel. + cancel := make(chan struct{}) + g.Add( + func() error { + <-reloadReady.C + ruleManager.Run() + <-cancel + return nil + }, + func(err error) { + ruleManager.Stop() + close(cancel) + }, + ) + } + { + // Notifier. + + // Calling notifier.Stop() before ruleManager.Stop() will cause a panic if the ruleManager isn't running, + // so keep this interrupt after the ruleManager.Stop(). + g.Add( + func() error { + // When the notifier manager receives a new targets list + // it needs to read a valid config for each job. + // It depends on the config being in sync with the discovery manager + // so we wait until the config is fully loaded. + <-reloadReady.C + + notifier.Run(discoveryManagerNotify.SyncCh()) + level.Info(logger).Log("msg", "Notifier manager stopped") + return nil + }, + func(err error) { + notifier.Stop() + }, + ) + } + r.queryEngine = queryEngine + r.remoteStorage = remoteStorage + r.ruleManager = ruleManager + + if err := g.Run(); err != nil { + level.Error(logger).Log("err", err) + os.Exit(1) + } + +} + +func reloadConfig(filename string, logger log.Logger, rls ...func(*config.Config) error) (promConfig *config.Config, err error) { + level.Info(logger).Log("msg", "Loading configuration file", "filename", filename) + + conf, err := config.LoadFile(filename) if err != nil { - zap.S().Error("couldn't load configuration (--config.file=%q): %v", filename, err) + return nil, fmt.Errorf("couldn't load configuration (--config.file=%q): %v", filename, err) } - err = remoteStorage.ApplyConfig(conf) + failed := false + for _, rl := range rls { + if err := rl(conf); err != nil { + level.Error(logger).Log("msg", "Failed to apply configuration", "err", err) + failed = true + } + } + if failed { + return nil, fmt.Errorf("one or more errors occurred while applying the new configuration (--config.file=%q)", filename) + } + level.Info(logger).Log("msg", "Completed loading of configuration file", "filename", filename) + return conf, nil +} + +func startsOrEndsWithQuote(s string) bool { + return strings.HasPrefix(s, "\"") || strings.HasPrefix(s, "'") || + strings.HasSuffix(s, "\"") || strings.HasSuffix(s, "'") +} + +// computeExternalURL computes a sanitized external URL from a raw input. It infers unset +// URL parts from the OS and the given listen address. +func computeExternalURL(u, listenAddr string) (*url.URL, error) { + if u == "" { + hostname, err := os.Hostname() + if err != nil { + return nil, err + } + _, port, err := net.SplitHostPort(listenAddr) + if err != nil { + return nil, err + } + u = fmt.Sprintf("http://%s:%s/", hostname, port) + } + + if startsOrEndsWithQuote(u) { + return nil, fmt.Errorf("URL must not begin or end with quotes") + } + + eu, err := url.Parse(u) if err != nil { - zap.S().Error("Error in remoteStorage.ApplyConfig: ", err) + return nil, err } - return &ClickHouseReader{ - db: db, - operationsTable: options.primary.OperationsTable, - indexTable: options.primary.IndexTable, - spansTable: options.primary.SpansTable, - queryEngine: queryEngine, - remoteStorage: remoteStorage, + ppref := strings.TrimRight(eu.Path, "/") + if ppref != "" && !strings.HasPrefix(ppref, "/") { + ppref = "/" + ppref + } + eu.Path = ppref + + return eu, nil +} + +// sendAlerts implements the rules.NotifyFunc for a Notifier. +func sendAlerts(n *notifier.Manager, externalURL string) rules.NotifyFunc { + return func(ctx context.Context, expr string, alerts ...*rules.Alert) { + var res []*notifier.Alert + + for _, alert := range alerts { + a := ¬ifier.Alert{ + StartsAt: alert.FiredAt, + Labels: alert.Labels, + Annotations: alert.Annotations, + GeneratorURL: externalURL + strutil.TableLinkForExpression(expr), + } + if !alert.ResolvedAt.IsZero() { + a.EndsAt = alert.ResolvedAt + } else { + a.EndsAt = alert.ValidUntil + } + res = append(res, a) + } + + if len(alerts) > 0 { + n.Send(res...) + } } } @@ -135,6 +524,554 @@ func connect(cfg *namespaceConfig) (*sqlx.DB, error) { return cfg.Connector(cfg) } +type byAlertStateAndNameSorter struct { + alerts []*AlertingRuleWithGroup +} + +func (s byAlertStateAndNameSorter) Len() int { + return len(s.alerts) +} + +func (s byAlertStateAndNameSorter) Less(i, j int) bool { + return s.alerts[i].State() > s.alerts[j].State() || + (s.alerts[i].State() == s.alerts[j].State() && + s.alerts[i].Name() < s.alerts[j].Name()) +} + +func (s byAlertStateAndNameSorter) Swap(i, j int) { + s.alerts[i], s.alerts[j] = s.alerts[j], s.alerts[i] +} + +type AlertingRuleWithGroup struct { + rules.AlertingRule + Id int +} + +func (r *ClickHouseReader) GetRulesFromDB() (*[]model.RuleResponseItem, *model.ApiError) { + + rules := []model.RuleResponseItem{} + + query := fmt.Sprintf("SELECT id, updated_at, data FROM rules") + + err := r.localDB.Select(&rules, query) + + zap.S().Info(query) + + if err != nil { + zap.S().Debug("Error in processing sql query: ", err) + return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + + return &rules, nil +} + +func (r *ClickHouseReader) GetRule(id string) (*model.RuleResponseItem, *model.ApiError) { + + idInt, _ := strconv.Atoi(id) + + rule := &model.RuleResponseItem{} + + query := fmt.Sprintf("SELECT id, updated_at, data FROM rules WHERE id=%d", idInt) + + err := r.localDB.Get(rule, query) + + zap.S().Info(query) + + if err != nil { + zap.S().Debug("Error in processing sql query: ", err) + return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + + return rule, nil +} + +func (r *ClickHouseReader) ListRulesFromProm() (*model.AlertDiscovery, *model.ApiError) { + + groups := r.ruleManager.RuleGroups() + + alertingRulesWithGroupObjects := []*AlertingRuleWithGroup{} + + for _, group := range groups { + groupNameParts := strings.Split(group.Name(), "-groupname") + if len(groupNameParts) < 2 { + continue + } + id, _ := strconv.Atoi(groupNameParts[0]) + for _, rule := range group.Rules() { + if alertingRule, ok := rule.(*rules.AlertingRule); ok { + alertingRulesWithGroupObject := AlertingRuleWithGroup{ + *alertingRule, + id, + } + alertingRulesWithGroupObjects = append(alertingRulesWithGroupObjects, &alertingRulesWithGroupObject) + } + } + } + + // alertingRules := r.ruleManager.AlertingRules() + + alertsSorter := byAlertStateAndNameSorter{alerts: alertingRulesWithGroupObjects} + sort.Sort(alertsSorter) + alerts := []*model.AlertingRuleResponse{} + + for _, alertingRule := range alertsSorter.alerts { + + alertingRuleResponseObject := &model.AlertingRuleResponse{ + Labels: alertingRule.Labels(), + // Annotations: alertingRule.Annotations(), + Name: alertingRule.Name(), + Id: alertingRule.Id, + } + if len(alertingRule.ActiveAlerts()) == 0 { + alertingRuleResponseObject.State = rules.StateInactive.String() + } else { + alertingRuleResponseObject.State = (*(alertingRule.ActiveAlerts()[0])).State.String() + } + + alerts = append( + alerts, + alertingRuleResponseObject, + ) + } + + res := &model.AlertDiscovery{Alerts: alerts} + + return res, nil +} + +func (r *ClickHouseReader) LoadRule(rule model.RuleResponseItem) *model.ApiError { + + groupName := fmt.Sprintf("%d-groupname", rule.Id) + + err := r.ruleManager.AddGroup(time.Duration(r.promConfig.GlobalConfig.EvaluationInterval), rule.Data, groupName) + + if err != nil { + return &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + + return nil +} + +func (r *ClickHouseReader) LoadChannel(channel *model.ChannelItem) *model.ApiError { + + receiver := &model.Receiver{} + if err := json.Unmarshal([]byte(channel.Data), receiver); err != nil { // Parse []byte to go struct pointer + return &model.ApiError{Typ: model.ErrorBadData, Err: err} + } + + response, err := http.Post(constants.ALERTMANAGER_API_PREFIX+"v1/receivers", "application/json", bytes.NewBuffer([]byte(channel.Data))) + + if err != nil { + zap.S().Errorf("Error in getting response of API call to alertmanager/v1/receivers\n", err) + return &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + if response.StatusCode > 299 { + responseData, _ := ioutil.ReadAll(response.Body) + + err := fmt.Errorf("Error in getting 2xx response in API call to alertmanager/v1/receivers\n", response.Status, string(responseData)) + zap.S().Error(err) + + return &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + + return nil +} + +func (r *ClickHouseReader) GetChannel(id string) (*model.ChannelItem, *model.ApiError) { + + idInt, _ := strconv.Atoi(id) + channel := model.ChannelItem{} + + query := fmt.Sprintf("SELECT id, created_at, updated_at, name, type, data data FROM notification_channels WHERE id=%d", idInt) + + err := r.localDB.Get(&channel, query) + + if err != nil { + zap.S().Debug("Error in processing sql query: ", err) + return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + + return &channel, nil + +} + +func (r *ClickHouseReader) DeleteChannel(id string) *model.ApiError { + + idInt, _ := strconv.Atoi(id) + + channelToDelete, apiErrorObj := r.GetChannel(id) + + if apiErrorObj != nil { + return apiErrorObj + } + + tx, err := r.localDB.Begin() + if err != nil { + return &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + + { + stmt, err := tx.Prepare(`DELETE FROM notification_channels WHERE id=$1;`) + if err != nil { + zap.S().Errorf("Error in preparing statement for INSERT to notification_channels\n", err) + tx.Rollback() + return &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + defer stmt.Close() + + if _, err := stmt.Exec(idInt); err != nil { + zap.S().Errorf("Error in Executing prepared statement for INSERT to notification_channels\n", err) + tx.Rollback() // return an error too, we may want to wrap them + return &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + } + + values := map[string]string{"name": channelToDelete.Name} + jsonValue, _ := json.Marshal(values) + + req, err := http.NewRequest(http.MethodDelete, constants.ALERTMANAGER_API_PREFIX+"v1/receivers", bytes.NewBuffer(jsonValue)) + + if err != nil { + zap.S().Errorf("Error in creating new delete request to alertmanager/v1/receivers\n", err) + tx.Rollback() + return &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + + req.Header.Add("Content-Type", "application/json") + + client := &http.Client{} + response, err := client.Do(req) + + if err != nil { + zap.S().Errorf("Error in delete API call to alertmanager/v1/receivers\n", err) + tx.Rollback() + return &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + if response.StatusCode > 299 { + err := fmt.Errorf("Error in getting 2xx response in API call to delete alertmanager/v1/receivers\n", response.Status) + zap.S().Error(err) + tx.Rollback() + return &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + + err = tx.Commit() + if err != nil { + zap.S().Errorf("Error in commiting transaction for DELETE command to notification_channels\n", err) + return &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + + return nil + +} + +func (r *ClickHouseReader) GetChannels() (*[]model.ChannelItem, *model.ApiError) { + + channels := []model.ChannelItem{} + + query := fmt.Sprintf("SELECT id, created_at, updated_at, name, type, data data FROM notification_channels") + + err := r.localDB.Select(&channels, query) + + // zap.S().Info(query) + + if err != nil { + zap.S().Debug("Error in processing sql query: ", err) + return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + + return &channels, nil + +} + +func getChannelType(receiver *model.Receiver) string { + + if receiver.EmailConfigs != nil { + return "email" + } + if receiver.OpsGenieConfigs != nil { + return "opsgenie" + } + if receiver.PagerdutyConfigs != nil { + return "pagerduty" + } + if receiver.PushoverConfigs != nil { + return "pushover" + } + if receiver.SNSConfigs != nil { + return "sns" + } + if receiver.SlackConfigs != nil { + return "slack" + } + if receiver.VictorOpsConfigs != nil { + return "victorops" + } + if receiver.WebhookConfigs != nil { + return "webhook" + } + if receiver.WechatConfigs != nil { + return "wechat" + } + + return "" +} + +func (r *ClickHouseReader) EditChannel(receiver *model.Receiver, id string) (*model.Receiver, *model.ApiError) { + + idInt, _ := strconv.Atoi(id) + + channel, apiErrObj := r.GetChannel(id) + + if apiErrObj != nil { + return nil, apiErrObj + } + if channel.Name != receiver.Name { + return nil, &model.ApiError{Typ: model.ErrorBadData, Err: fmt.Errorf("channel name cannot be changed")} + } + + tx, err := r.localDB.Begin() + if err != nil { + return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + + channel_type := getChannelType(receiver) + receiverString, _ := json.Marshal(receiver) + + { + stmt, err := tx.Prepare(`UPDATE notification_channels SET updated_at=$1, type=$2, data=$3 WHERE id=$4;`) + + if err != nil { + zap.S().Errorf("Error in preparing statement for UPDATE to notification_channels\n", err) + tx.Rollback() + return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + defer stmt.Close() + + if _, err := stmt.Exec(time.Now(), channel_type, string(receiverString), idInt); err != nil { + zap.S().Errorf("Error in Executing prepared statement for UPDATE to notification_channels\n", err) + tx.Rollback() // return an error too, we may want to wrap them + return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + } + + req, err := http.NewRequest(http.MethodPut, constants.ALERTMANAGER_API_PREFIX+"v1/receivers", bytes.NewBuffer(receiverString)) + + if err != nil { + zap.S().Errorf("Error in creating new update request to alertmanager/v1/receivers\n", err) + tx.Rollback() + return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + + req.Header.Add("Content-Type", "application/json") + + client := &http.Client{} + response, err := client.Do(req) + + if err != nil { + zap.S().Errorf("Error in update API call to alertmanager/v1/receivers\n", err) + tx.Rollback() + return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + + if response.StatusCode > 299 { + err := fmt.Errorf("Error in getting 2xx response in API call to alertmanager/v1/receivers\n", response.Status) + zap.S().Error(err) + tx.Rollback() + return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + + err = tx.Commit() + if err != nil { + zap.S().Errorf("Error in commiting transaction for INSERT to notification_channels\n", err) + return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + + return receiver, nil + +} + +func (r *ClickHouseReader) CreateChannel(receiver *model.Receiver) (*model.Receiver, *model.ApiError) { + + tx, err := r.localDB.Begin() + if err != nil { + return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + + channel_type := getChannelType(receiver) + receiverString, _ := json.Marshal(receiver) + + { + stmt, err := tx.Prepare(`INSERT INTO notification_channels (created_at, updated_at, name, type, data) VALUES($1,$2,$3,$4,$5);`) + if err != nil { + zap.S().Errorf("Error in preparing statement for INSERT to notification_channels\n", err) + tx.Rollback() + return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + defer stmt.Close() + + if _, err := stmt.Exec(time.Now(), time.Now(), receiver.Name, channel_type, string(receiverString)); err != nil { + zap.S().Errorf("Error in Executing prepared statement for INSERT to notification_channels\n", err) + tx.Rollback() // return an error too, we may want to wrap them + return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + } + + response, err := http.Post(constants.ALERTMANAGER_API_PREFIX+"v1/receivers", "application/json", bytes.NewBuffer(receiverString)) + + if err != nil { + zap.S().Errorf("Error in getting response of API call to alertmanager/v1/receivers\n", err) + tx.Rollback() + return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + if response.StatusCode > 299 { + err := fmt.Errorf("Error in getting 2xx response in API call to alertmanager/v1/receivers\n", response.Status) + zap.S().Error(err) + tx.Rollback() + return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + + err = tx.Commit() + if err != nil { + zap.S().Errorf("Error in commiting transaction for INSERT to notification_channels\n", err) + return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + + return receiver, nil + +} + +func (r *ClickHouseReader) CreateRule(rule string) *model.ApiError { + + tx, err := r.localDB.Begin() + if err != nil { + return &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + + var lastInsertId int64 + + { + stmt, err := tx.Prepare(`INSERT into rules (updated_at, data) VALUES($1,$2);`) + if err != nil { + zap.S().Errorf("Error in preparing statement for INSERT to rules\n", err) + tx.Rollback() + return &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + defer stmt.Close() + + result, err := stmt.Exec(time.Now(), rule) + if err != nil { + zap.S().Errorf("Error in Executing prepared statement for INSERT to rules\n", err) + tx.Rollback() // return an error too, we may want to wrap them + return &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + lastInsertId, _ = result.LastInsertId() + + groupName := fmt.Sprintf("%d-groupname", lastInsertId) + + err = r.ruleManager.AddGroup(time.Duration(r.promConfig.GlobalConfig.EvaluationInterval), rule, groupName) + + if err != nil { + tx.Rollback() + return &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + } + err = tx.Commit() + if err != nil { + zap.S().Errorf("Error in commiting transaction for INSERT to rules\n", err) + return &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + return nil +} + +func (r *ClickHouseReader) EditRule(rule string, id string) *model.ApiError { + + idInt, _ := strconv.Atoi(id) + + tx, err := r.localDB.Begin() + if err != nil { + return &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + + { + stmt, err := tx.Prepare(`UPDATE rules SET updated_at=$1, data=$2 WHERE id=$3;`) + if err != nil { + zap.S().Errorf("Error in preparing statement for UPDATE to rules\n", err) + tx.Rollback() + return &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + defer stmt.Close() + + if _, err := stmt.Exec(time.Now(), rule, idInt); err != nil { + zap.S().Errorf("Error in Executing prepared statement for UPDATE to rules\n", err) + tx.Rollback() // return an error too, we may want to wrap them + return &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + + groupName := fmt.Sprintf("%d-groupname", idInt) + + err = r.ruleManager.EditGroup(time.Duration(r.promConfig.GlobalConfig.EvaluationInterval), rule, groupName) + + if err != nil { + tx.Rollback() + return &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + } + + err = tx.Commit() + if err != nil { + zap.S().Errorf("Error in commiting transaction for UPDATE to rules\n", err) + return &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + + return nil +} + +func (r *ClickHouseReader) DeleteRule(id string) *model.ApiError { + + idInt, _ := strconv.Atoi(id) + + tx, err := r.localDB.Begin() + if err != nil { + return &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + + { + stmt, err := tx.Prepare(`DELETE FROM rules WHERE id=$1;`) + + if err != nil { + return &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + defer stmt.Close() + + if _, err := stmt.Exec(idInt); err != nil { + zap.S().Errorf("Error in Executing prepared statement for DELETE to rules\n", err) + tx.Rollback() // return an error too, we may want to wrap them + return &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + + groupName := fmt.Sprintf("%d-groupname", idInt) + + rule := "" // dummy rule to pass to function + // err = r.ruleManager.UpdateGroupWithAction(time.Duration(r.promConfig.GlobalConfig.EvaluationInterval), rule, groupName, "delete") + err = r.ruleManager.DeleteGroup(time.Duration(r.promConfig.GlobalConfig.EvaluationInterval), rule, groupName) + + if err != nil { + tx.Rollback() + zap.S().Errorf("Error in deleting rule from rulemanager...\n", err) + return &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + + } + + err = tx.Commit() + if err != nil { + zap.S().Errorf("Error in commiting transaction for deleting rules\n", err) + return &model.ApiError{Typ: model.ErrorInternal, Err: err} + } + + return nil +} + func (r *ClickHouseReader) GetInstantQueryMetricsResult(ctx context.Context, queryParams *model.InstantQueryMetricsParams) (*promql.Result, *stats.QueryStats, *model.ApiError) { qry, err := r.queryEngine.NewInstantQuery(r.remoteStorage, queryParams.Query, queryParams.Time) if err != nil { diff --git a/pkg/query-service/app/dashboards/model.go b/pkg/query-service/app/dashboards/model.go index 8baf48d61e..f5c3106334 100644 --- a/pkg/query-service/app/dashboards/model.go +++ b/pkg/query-service/app/dashboards/model.go @@ -13,28 +13,16 @@ import ( "go.uber.org/zap" ) -// const ( -// ErrorNone ErrorType = "" -// ErrorTimeout ErrorType = "timeout" -// ErrorCanceled ErrorType = "canceled" -// ErrorExec ErrorType = "execution" -// ErrorBadData ErrorType = "bad_data" -// ErrorInternal ErrorType = "internal" -// ErrorUnavailable ErrorType = "unavailable" -// ErrorNotFound ErrorType = "not_found" -// ErrorNotImplemented ErrorType = "not_implemented" -// ) - // This time the global variable is unexported. var db *sqlx.DB // InitDB sets up setting up the connection pool global variable. -func InitDB(dataSourceName string) error { +func InitDB(dataSourceName string) (*sqlx.DB, error) { var err error db, err = sqlx.Open("sqlite3", dataSourceName) if err != nil { - return err + return nil, err } table_schema := `CREATE TABLE IF NOT EXISTS dashboards ( @@ -47,10 +35,37 @@ func InitDB(dataSourceName string) error { _, err = db.Exec(table_schema) if err != nil { - return fmt.Errorf("Error in creating dashboard table: ", err.Error()) + return nil, fmt.Errorf("Error in creating dashboard table: ", err.Error()) } - return nil + table_schema = `CREATE TABLE IF NOT EXISTS rules ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + updated_at datetime NOT NULL, + deleted INTEGER DEFAULT 0, + data TEXT NOT NULL + );` + + _, err = db.Exec(table_schema) + if err != nil { + return nil, fmt.Errorf("Error in creating rules table: ", err.Error()) + } + + table_schema = `CREATE TABLE IF NOT EXISTS notification_channels ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + created_at datetime NOT NULL, + updated_at datetime NOT NULL, + name TEXT NOT NULL UNIQUE, + type TEXT NOT NULL, + deleted INTEGER DEFAULT 0, + data TEXT NOT NULL + );` + + _, err = db.Exec(table_schema) + if err != nil { + return nil, fmt.Errorf("Error in creating notification_channles table: ", err.Error()) + } + + return db, nil } type Dashboard struct { diff --git a/pkg/query-service/app/druidReader/reader.go b/pkg/query-service/app/druidReader/reader.go index 4c4ecb2d6f..dc018fc430 100644 --- a/pkg/query-service/app/druidReader/reader.go +++ b/pkg/query-service/app/druidReader/reader.go @@ -5,6 +5,7 @@ import ( "fmt" "os" + "github.com/jmoiron/sqlx" "github.com/prometheus/prometheus/promql" "github.com/prometheus/prometheus/util/stats" "go.signoz.io/query-service/druidQuery" @@ -15,9 +16,10 @@ import ( type DruidReader struct { Client *godruid.Client SqlClient *druidQuery.SqlClient + LocalDB *sqlx.DB } -func NewReader() *DruidReader { +func NewReader(localDB *sqlx.DB) *DruidReader { initialize() druidClientUrl := os.Getenv("DruidClientUrl") @@ -34,6 +36,7 @@ func NewReader() *DruidReader { return &DruidReader{ Client: &client, SqlClient: &sqlClient, + LocalDB: localDB, } } @@ -52,6 +55,49 @@ func (druid *DruidReader) GetInstantQueryMetricsResult(ctx context.Context, quer return nil, nil, &model.ApiError{model.ErrorNotImplemented, fmt.Errorf("Druid does not support metrics")} } +func (druid *DruidReader) DeleteChannel(id string) *model.ApiError { + return &model.ApiError{model.ErrorNotImplemented, fmt.Errorf("Druid does not support notification channel for alerts")} +} + +func (druid *DruidReader) GetChannel(id string) (*model.ChannelItem, *model.ApiError) { + return nil, &model.ApiError{model.ErrorNotImplemented, fmt.Errorf("Druid does not support notification channel for alerts")} +} +func (druid *DruidReader) GetChannels() (*[]model.ChannelItem, *model.ApiError) { + return nil, &model.ApiError{model.ErrorNotImplemented, fmt.Errorf("Druid does not support notification channel for alerts")} +} +func (druid *DruidReader) CreateChannel(receiver *model.Receiver) (*model.Receiver, *model.ApiError) { + + return nil, &model.ApiError{model.ErrorNotImplemented, fmt.Errorf("Druid does not support notification channel for alerts")} + +} +func (druid *DruidReader) EditChannel(receiver *model.Receiver, id string) (*model.Receiver, *model.ApiError) { + + return nil, &model.ApiError{model.ErrorNotImplemented, fmt.Errorf("Druid does not support notification channel for alerts")} + +} + +func (druid *DruidReader) ListRulesFromProm() (*model.AlertDiscovery, *model.ApiError) { + + res := model.AlertDiscovery{} + return &res, &model.ApiError{model.ErrorNotImplemented, fmt.Errorf("Druid does not support getting rules for alerting")} +} +func (druid *DruidReader) GetRule(id string) (*model.RuleResponseItem, *model.ApiError) { + + return nil, &model.ApiError{model.ErrorNotImplemented, fmt.Errorf("Druid does not support getting rules for alerting")} +} +func (druid *DruidReader) CreateRule(alert string) *model.ApiError { + + return &model.ApiError{model.ErrorNotImplemented, fmt.Errorf("Druid does not support setting rules for alerting")} +} +func (druid *DruidReader) EditRule(alert string, id string) *model.ApiError { + + return &model.ApiError{model.ErrorNotImplemented, fmt.Errorf("Druid does not support editing rules for alerting")} +} +func (druid *DruidReader) DeleteRule(id string) *model.ApiError { + + return &model.ApiError{model.ErrorNotImplemented, fmt.Errorf("Druid does not support deleting rules for alerting")} +} + func (druid *DruidReader) GetServiceOverview(ctx context.Context, query *model.GetServiceOverviewParams) (*[]model.ServiceOverviewItem, error) { return druidQuery.GetServiceOverview(druid.SqlClient, query) } diff --git a/pkg/query-service/app/http_handler.go b/pkg/query-service/app/http_handler.go index 1578afb0a2..aaeab745a3 100644 --- a/pkg/query-service/app/http_handler.go +++ b/pkg/query-service/app/http_handler.go @@ -4,10 +4,10 @@ import ( "context" "encoding/json" "fmt" + "io/ioutil" "net/http" "github.com/gorilla/mux" - "github.com/jmoiron/sqlx" jsoniter "github.com/json-iterator/go" _ "github.com/mattn/go-sqlite3" "github.com/posthog/posthog-go" @@ -38,7 +38,6 @@ type APIHandler struct { reader *Reader pc *posthog.Client distinctId string - db *sqlx.DB ready func(http.HandlerFunc) http.HandlerFunc } @@ -52,11 +51,6 @@ func NewAPIHandler(reader *Reader, pc *posthog.Client, distinctId string) (*APIH } aH.ready = aH.testReady - err := dashboards.InitDB("/var/lib/signoz/signoz.db") - if err != nil { - return nil, err - } - errReadingDashboards := dashboards.LoadDashboardFiles() if errReadingDashboards != nil { return nil, errReadingDashboards @@ -172,6 +166,16 @@ func (aH *APIHandler) respond(w http.ResponseWriter, data interface{}) { func (aH *APIHandler) RegisterRoutes(router *mux.Router) { router.HandleFunc("/api/v1/query_range", aH.queryRangeMetrics).Methods(http.MethodGet) router.HandleFunc("/api/v1/query", aH.queryMetrics).Methods(http.MethodGet) + router.HandleFunc("/api/v1/channels", aH.listChannels).Methods(http.MethodGet) + router.HandleFunc("/api/v1/channels/{id}", aH.getChannel).Methods(http.MethodGet) + router.HandleFunc("/api/v1/channels/{id}", aH.editChannel).Methods(http.MethodPut) + router.HandleFunc("/api/v1/channels/{id}", aH.deleteChannel).Methods(http.MethodDelete) + router.HandleFunc("/api/v1/channels", aH.createChannel).Methods(http.MethodPost) + router.HandleFunc("/api/v1/rules", aH.listRulesFromProm).Methods(http.MethodGet) + router.HandleFunc("/api/v1/rules/{id}", aH.getRule).Methods(http.MethodGet) + router.HandleFunc("/api/v1/rules", aH.createRule).Methods(http.MethodPost) + router.HandleFunc("/api/v1/rules/{id}", aH.editRule).Methods(http.MethodPut) + router.HandleFunc("/api/v1/rules/{id}", aH.deleteRule).Methods(http.MethodDelete) router.HandleFunc("/api/v1/dashboards", aH.getDashboards).Methods(http.MethodGet) router.HandleFunc("/api/v1/dashboards", aH.createDashboards).Methods(http.MethodPost) @@ -215,6 +219,25 @@ func Intersection(a, b []int) (c []int) { return } +func (aH *APIHandler) getRule(w http.ResponseWriter, r *http.Request) { + id := mux.Vars(r)["id"] + alertList, apiErrorObj := (*aH.reader).GetRule(id) + if apiErrorObj != nil { + aH.respondError(w, apiErrorObj, nil) + return + } + aH.respond(w, alertList) +} + +func (aH *APIHandler) listRulesFromProm(w http.ResponseWriter, r *http.Request) { + alertList, apiErrorObj := (*aH.reader).ListRulesFromProm() + if apiErrorObj != nil { + aH.respondError(w, apiErrorObj, nil) + return + } + aH.respond(w, alertList) +} + func (aH *APIHandler) getDashboards(w http.ResponseWriter, r *http.Request) { allDashboards, err := dashboards.GetDashboards() @@ -351,6 +374,150 @@ func (aH *APIHandler) createDashboards(w http.ResponseWriter, r *http.Request) { } +func (aH *APIHandler) deleteRule(w http.ResponseWriter, r *http.Request) { + id := mux.Vars(r)["id"] + + apiErrorObj := (*aH.reader).DeleteRule(id) + + if apiErrorObj != nil { + aH.respondError(w, apiErrorObj, nil) + return + } + + aH.respond(w, "rule successfully deleted") + +} +func (aH *APIHandler) editRule(w http.ResponseWriter, r *http.Request) { + id := mux.Vars(r)["id"] + + var postData map[string]string + err := json.NewDecoder(r.Body).Decode(&postData) + if err != nil { + aH.respondError(w, &model.ApiError{Typ: model.ErrorBadData, Err: err}, "Error reading request body") + return + } + + apiErrorObj := (*aH.reader).EditRule(postData["data"], id) + + if apiErrorObj != nil { + aH.respondError(w, apiErrorObj, nil) + return + } + + aH.respond(w, "rule successfully edited") + +} + +func (aH *APIHandler) getChannel(w http.ResponseWriter, r *http.Request) { + id := mux.Vars(r)["id"] + channel, apiErrorObj := (*aH.reader).GetChannel(id) + if apiErrorObj != nil { + aH.respondError(w, apiErrorObj, nil) + return + } + aH.respond(w, channel) +} + +func (aH *APIHandler) deleteChannel(w http.ResponseWriter, r *http.Request) { + id := mux.Vars(r)["id"] + apiErrorObj := (*aH.reader).DeleteChannel(id) + if apiErrorObj != nil { + aH.respondError(w, apiErrorObj, nil) + return + } + aH.respond(w, "notification channel successfully deleted") +} + +func (aH *APIHandler) listChannels(w http.ResponseWriter, r *http.Request) { + channels, apiErrorObj := (*aH.reader).GetChannels() + if apiErrorObj != nil { + aH.respondError(w, apiErrorObj, nil) + return + } + aH.respond(w, channels) +} + +func (aH *APIHandler) editChannel(w http.ResponseWriter, r *http.Request) { + + id := mux.Vars(r)["id"] + + defer r.Body.Close() + body, err := ioutil.ReadAll(r.Body) + if err != nil { + zap.S().Errorf("Error in getting req body of editChannel API\n", err) + aH.respondError(w, &model.ApiError{Typ: model.ErrorBadData, Err: err}, nil) + return + } + + receiver := &model.Receiver{} + if err := json.Unmarshal(body, receiver); err != nil { // Parse []byte to go struct pointer + zap.S().Errorf("Error in parsing req body of editChannel API\n", err) + aH.respondError(w, &model.ApiError{Typ: model.ErrorBadData, Err: err}, nil) + return + } + + _, apiErrorObj := (*aH.reader).EditChannel(receiver, id) + + if apiErrorObj != nil { + aH.respondError(w, apiErrorObj, nil) + return + } + + aH.respond(w, nil) + +} + +func (aH *APIHandler) createChannel(w http.ResponseWriter, r *http.Request) { + + defer r.Body.Close() + body, err := ioutil.ReadAll(r.Body) + if err != nil { + zap.S().Errorf("Error in getting req body of createChannel API\n", err) + aH.respondError(w, &model.ApiError{Typ: model.ErrorBadData, Err: err}, nil) + return + } + + receiver := &model.Receiver{} + if err := json.Unmarshal(body, receiver); err != nil { // Parse []byte to go struct pointer + zap.S().Errorf("Error in parsing req body of createChannel API\n", err) + aH.respondError(w, &model.ApiError{Typ: model.ErrorBadData, Err: err}, nil) + return + } + + _, apiErrorObj := (*aH.reader).CreateChannel(receiver) + + if apiErrorObj != nil { + aH.respondError(w, apiErrorObj, nil) + return + } + + aH.respond(w, nil) + +} + +func (aH *APIHandler) createRule(w http.ResponseWriter, r *http.Request) { + + decoder := json.NewDecoder(r.Body) + + var postData map[string]string + err := decoder.Decode(&postData) + + if err != nil { + aH.respondError(w, &model.ApiError{Typ: model.ErrorBadData, Err: err}, nil) + return + } + + apiErrorObj := (*aH.reader).CreateRule(postData["data"]) + + if apiErrorObj != nil { + aH.respondError(w, apiErrorObj, nil) + return + } + + aH.respond(w, "rule successfully added") + +} + func (aH *APIHandler) queryRangeMetrics(w http.ResponseWriter, r *http.Request) { query, apiErrorObj := parseQueryRangeRequest(r) diff --git a/pkg/query-service/app/interface.go b/pkg/query-service/app/interface.go index d5db44750b..75483b1b43 100644 --- a/pkg/query-service/app/interface.go +++ b/pkg/query-service/app/interface.go @@ -9,7 +9,18 @@ import ( ) type Reader interface { - // Getter Interfaces + GetChannel(id string) (*model.ChannelItem, *model.ApiError) + GetChannels() (*[]model.ChannelItem, *model.ApiError) + DeleteChannel(id string) *model.ApiError + CreateChannel(receiver *model.Receiver) (*model.Receiver, *model.ApiError) + EditChannel(receiver *model.Receiver, id string) (*model.Receiver, *model.ApiError) + + GetRule(id string) (*model.RuleResponseItem, *model.ApiError) + ListRulesFromProm() (*model.AlertDiscovery, *model.ApiError) + CreateRule(alert string) *model.ApiError + EditRule(alert string, id string) *model.ApiError + DeleteRule(id string) *model.ApiError + GetInstantQueryMetricsResult(ctx context.Context, query *model.InstantQueryMetricsParams) (*promql.Result, *stats.QueryStats, *model.ApiError) GetQueryRangeResult(ctx context.Context, query *model.QueryRangeParams) (*promql.Result, *stats.QueryStats, *model.ApiError) GetServiceOverview(ctx context.Context, query *model.GetServiceOverviewParams) (*[]model.ServiceOverviewItem, error) diff --git a/pkg/query-service/app/parser.go b/pkg/query-service/app/parser.go index 6f7cbaeca6..9db12840f6 100644 --- a/pkg/query-service/app/parser.go +++ b/pkg/query-service/app/parser.go @@ -567,6 +567,10 @@ func parseTimestamp(param string, r *http.Request) (*string, error) { return &timeStr, nil } +func parseSetRulesRequest(r *http.Request) (string, *model.ApiError) { + + return "", nil +} func parseDuration(r *http.Request) (*model.TTLParams, error) { diff --git a/pkg/query-service/app/server.go b/pkg/query-service/app/server.go index b876c0e69f..8e4088d8d0 100644 --- a/pkg/query-service/app/server.go +++ b/pkg/query-service/app/server.go @@ -14,6 +14,7 @@ import ( "github.com/rs/cors" "github.com/soheilhy/cmux" "go.signoz.io/query-service/app/clickhouseReader" + "go.signoz.io/query-service/app/dashboards" "go.signoz.io/query-service/app/druidReader" "go.signoz.io/query-service/healthcheck" "go.signoz.io/query-service/utils" @@ -93,15 +94,22 @@ func createHTTPServer() (*http.Server, error) { posthogClient = posthog.New("H-htDCae7CR3RV57gUzmol6IAKtm5IMCvbcm_fwnL-w") distinctId = uuid.New().String() + localDB, err := dashboards.InitDB("/var/lib/signoz/signoz.db") + if err != nil { + return nil, err + } + var reader Reader storage := os.Getenv("STORAGE") if storage == "druid" { zap.S().Info("Using Apache Druid as datastore ...") - reader = druidReader.NewReader() + reader = druidReader.NewReader(localDB) } else if storage == "clickhouse" { zap.S().Info("Using ClickHouse as datastore ...") - reader = clickhouseReader.NewReader() + clickhouseReader := clickhouseReader.NewReader(localDB) + go clickhouseReader.Start() + reader = clickhouseReader } else { return nil, fmt.Errorf("Storage type: %s is not supported in query service", storage) } diff --git a/pkg/query-service/config/alerts.yml b/pkg/query-service/config/alerts.yml new file mode 100644 index 0000000000..99e1b95fa0 --- /dev/null +++ b/pkg/query-service/config/alerts.yml @@ -0,0 +1,11 @@ +groups: +- name: ExampleCPULoadGroup + rules: + - alert: HighCpuLoad-dev + expr: system_cpu_load_average_1m > 0.01 + for: 0m + labels: + severity: warning + annotations: + summary: High CPU load + description: "CPU load is > 0.01\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" \ No newline at end of file diff --git a/pkg/query-service/config/prometheus.yml b/pkg/query-service/config/prometheus.yml index 3764528078..c4834f9849 100644 --- a/pkg/query-service/config/prometheus.yml +++ b/pkg/query-service/config/prometheus.yml @@ -9,12 +9,13 @@ alerting: alertmanagers: - static_configs: - targets: - # - alertmanager:9093 + - 127.0.0.1:9093 # Load rules once and periodically evaluate them according to the global 'evaluation_interval'. rule_files: # - "first_rules.yml" # - "second_rules.yml" + - 'alerts.yml' # A scrape configuration containing exactly one endpoint to scrape: # Here it's Prometheus itself. @@ -22,4 +23,4 @@ scrape_configs: remote_read: - - url: tcp://localhost:9000/?database=signoz_metrics + - url: tcp://3.135.248.251:9001/?database=signoz_metrics diff --git a/pkg/query-service/constants/constants.go b/pkg/query-service/constants/constants.go index 82cdd9aaa9..1a77b4053b 100644 --- a/pkg/query-service/constants/constants.go +++ b/pkg/query-service/constants/constants.go @@ -11,3 +11,5 @@ var DruidDatasource = os.Getenv("DruidDatasource") const TraceTTL = "traces" const MetricsTTL = "metrics" + +const ALERTMANAGER_API_PREFIX = "http://alertmanager:9093/api/" diff --git a/pkg/query-service/go.mod b/pkg/query-service/go.mod index dec63c928e..f069568f19 100644 --- a/pkg/query-service/go.mod +++ b/pkg/query-service/go.mod @@ -32,16 +32,20 @@ require ( github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/modern-go/reflect2 v1.0.1 // indirect + github.com/oklog/oklog v0.3.2 + github.com/oklog/run v1.1.0 // indirect github.com/oklog/ulid v1.3.1 // indirect github.com/onsi/gomega v1.14.0 // indirect github.com/opentracing/opentracing-go v1.1.0 // indirect github.com/pascaldekloe/goe v0.1.0 // indirect github.com/pierrec/lz4 v2.4.1+incompatible // indirect - github.com/pkg/errors v0.9.1 // indirect + github.com/pkg/errors v0.9.1 github.com/posthog/posthog-go v0.0.0-20200525173953-e46dc8e6b89b + github.com/prometheus/client_golang v0.9.0-pre1.0.20181001174001-0a8115f42e03 github.com/prometheus/common v0.0.0-20180518154759-7600349dcfe1 github.com/prometheus/procfs v0.0.8 // indirect github.com/prometheus/prometheus v2.5.0+incompatible + github.com/prometheus/tsdb v0.0.0-20181003080831-0ce41118ed20 github.com/rs/cors v1.7.0 github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da // indirect github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect @@ -54,7 +58,8 @@ require ( google.golang.org/api v0.51.0 // indirect google.golang.org/grpc/examples v0.0.0-20210803221256-6ba56c814be7 // indirect gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect ) -replace github.com/prometheus/prometheus => github.com/SigNoz/prometheus v1.9.5 +replace github.com/prometheus/prometheus => github.com/SigNoz/prometheus v1.9.68 diff --git a/pkg/query-service/go.sum b/pkg/query-service/go.sum index 48d976f3c5..34097b7357 100644 --- a/pkg/query-service/go.sum +++ b/pkg/query-service/go.sum @@ -27,18 +27,23 @@ cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNF cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0 h1:PQcPefKFdaIzjQFbiyOgAqyx8q5djaE7x9Sqe712DPA= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0 h1:/May9ojXjRkPBNVrq+oWLqmWCkr4OU5uRY29bu0mRyQ= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1 h1:ukjixP1wl0LpnZ6LWtZJ0mX5tBmjp1f8Sqer8Z2OMUU= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0 h1:STgFzyU5/8miMl0//zKh2aQeTyeaUH3WN9bSUiJ09bA= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-sdk-for-go v5.0.0-beta.0.20161028183111-bd73d950fa44+incompatible h1:+5hx+ZckahrubYyxbjTwnq9w5xpnq1CwSL4N54I8/qc= github.com/Azure/azure-sdk-for-go v5.0.0-beta.0.20161028183111-bd73d950fa44+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= @@ -46,6 +51,7 @@ github.com/Azure/go-autorest v10.8.1+incompatible h1:u0jVQf+a6k6x8A+sT60l6EY9XZu github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ClickHouse/clickhouse-go v1.4.5 h1:FfhyEnv6/BaWldyjgT2k4gDDmeNwJ9C4NbY/MXxJlXk= github.com/ClickHouse/clickhouse-go v1.4.5/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI= @@ -55,8 +61,27 @@ github.com/SigNoz/prometheus v1.9.4 h1:HqMPTM/QXgsjzuG5whSS+9l0mVePuya/uZmrAt2H9 github.com/SigNoz/prometheus v1.9.4/go.mod h1:39/E+7N2hh5Q6NunhoRz9EHCRAyRGj63YK1hE4SiHdk= github.com/SigNoz/prometheus v1.9.5 h1:ITuK/71BFY3Hxv50/Upsvo6KJUdOxT7o6lCXmVxyLF0= github.com/SigNoz/prometheus v1.9.5/go.mod h1:BHEawFYBYkVr9BjPXsz9Ye6QVVQ+2a99m6r/S8hO/lA= +github.com/SigNoz/prometheus v1.9.6 h1:G1QYQkwCzTZG961a16r2z/GPK4Esx6PMHa1GF4sVeuo= +github.com/SigNoz/prometheus v1.9.6/go.mod h1:BHEawFYBYkVr9BjPXsz9Ye6QVVQ+2a99m6r/S8hO/lA= +github.com/SigNoz/prometheus v1.9.62 h1:YaemiWBVbaALzgjDHz4qtd6RlJuP2TabLdJZOORWJ/U= +github.com/SigNoz/prometheus v1.9.62/go.mod h1:BHEawFYBYkVr9BjPXsz9Ye6QVVQ+2a99m6r/S8hO/lA= +github.com/SigNoz/prometheus v1.9.63 h1:K/uQMltbLYZEgUGSYxVMK3HEUzPe3VmT2oH91Omnkmo= +github.com/SigNoz/prometheus v1.9.63/go.mod h1:BHEawFYBYkVr9BjPXsz9Ye6QVVQ+2a99m6r/S8hO/lA= +github.com/SigNoz/prometheus v1.9.64 h1:oPB53pbWncTUOAbQxQDq/f8Zz6Ffe6iZSicbsjXKxPQ= +github.com/SigNoz/prometheus v1.9.64/go.mod h1:BHEawFYBYkVr9BjPXsz9Ye6QVVQ+2a99m6r/S8hO/lA= +github.com/SigNoz/prometheus v1.9.65 h1:mbbFs055F3bapnvbreyMLtvMeNRWGH0zTzgBlGVhYJw= +github.com/SigNoz/prometheus v1.9.65/go.mod h1:BHEawFYBYkVr9BjPXsz9Ye6QVVQ+2a99m6r/S8hO/lA= +github.com/SigNoz/prometheus v1.9.66 h1:gmS/wdj+sRQa+PJCRzZQROUqbgC6GWtelSwWJ/iMZhA= +github.com/SigNoz/prometheus v1.9.66/go.mod h1:BHEawFYBYkVr9BjPXsz9Ye6QVVQ+2a99m6r/S8hO/lA= +github.com/SigNoz/prometheus v1.9.67 h1:JYQhs/Y0lwLoiExYzSEa0wQYYe0C96Y4ZPiWaC9MaMU= +github.com/SigNoz/prometheus v1.9.67/go.mod h1:BHEawFYBYkVr9BjPXsz9Ye6QVVQ+2a99m6r/S8hO/lA= +github.com/SigNoz/prometheus v1.9.68 h1:YeouJ2bLUksLtOPVutKFWLVgLDI0CRldosihPZtFCJY= +github.com/SigNoz/prometheus v1.9.68/go.mod h1:BHEawFYBYkVr9BjPXsz9Ye6QVVQ+2a99m6r/S8hO/lA= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= @@ -68,22 +93,32 @@ 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/bkaradzic/go-lz4 v1.0.0 h1:RXc4wYsyz985CkXXeX04y4VnZFGG8Rd43pRaHsOXAKk= github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4= +github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v0.0.0-20161118035902-4a94f899c20b/go.mod h1:fX/lfQBkSCDXZSUgv6jVIu/EVA3/JNseAX5asI4c4T4= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 h1:F1EaeKL/ta07PY/k9Os/UFtwERei2/XzGemhpGnBKNg= github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 h1:cqQfy1jclcSy/FwLjemeg3SR1yaINm74aQyupQ0Bl8M= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed h1:OZmjad4L3H8ncOIR8rnb5MREYqG8ixi5+WbeUsquF0c= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cockroachdb/cmux v0.0.0-20170110192607-30d10be49292 h1:dzj1/xcivGjNPwwifh/dWTczkwcuqsXXFHY1X/TZMtw= github.com/cockroachdb/cmux v0.0.0-20170110192607-30d10be49292/go.mod h1:qRiX68mZX1lGBkTWyp3CLcenw9I94W2dLeRvMzcn9N4= +github.com/cockroachdb/cockroach v0.0.0-20170608034007-84bc9597164f h1:0FHGBrsIyDci8tF7zujQkHdMTJdCTSIV9esrni2fKQI= github.com/cockroachdb/cockroach v0.0.0-20170608034007-84bc9597164f/go.mod h1:xeT/CQ0qZHangbYbWShlCGAx31aV4AjGswDUjhKS6HQ= +github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -91,6 +126,7 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/dgrijalva/jwt-go v3.0.1-0.20161101193935-9ed569b5d1ac+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 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/dgryski/go-bits v0.0.0-20160601073636-2ad8d707cc05 h1:XXlpYVyW5p4t3IvIv1jegXhqjgFMHnDwZ4AKvmwOL7I= github.com/dgryski/go-bits v0.0.0-20160601073636-2ad8d707cc05/go.mod h1:/9UYwwvZuEgp+mQ4960SHWCU1FS+FgdFX+m5ExFByNs= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -98,7 +134,9 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0 h1:dulLQAYQFYtG5MTplgNGHWuV2D+OBD+Z8lmDBmbLg+s= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= @@ -109,9 +147,12 @@ github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0= 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 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-ini/ini v1.21.1 h1:+QXUYsI7Tfxc64oD6R5BxU/Aq+UwGkyjH4W/hMNG7bg= github.com/go-ini/ini v1.21.1/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.4.1-0.20170517165212-6964666de57c/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk= @@ -127,6 +168,7 @@ github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB github.com/go-stack/stack v1.5.4/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gogo/protobuf v0.0.0-20171123125729-971cbfd2e72b/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= @@ -146,6 +188,7 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= 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= @@ -188,9 +231,11 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ= github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20180605153948-8b03ce837f34/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -205,7 +250,9 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9 h1:2tft2559dNwKl2znYB58oVTql0grRB+Ml3LWIBbc4WM= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -259,9 +306,12 @@ github.com/hashicorp/memberlist v0.1.0 h1:qSsCiC0WYD39lbSitKNt40e30uorm2Ss/d4JGU github.com/hashicorp/memberlist v0.1.0/go.mod h1:ncdBp14cuox2iFOq3kDiquKU6fqsTBc3W6JvZwjxxsE= github.com/hashicorp/serf v0.8.1-0.20161007004122-1d4fa605f6ff h1:epPiU3hEuHbpThFTQSGbdBBJemXM7aNQIU1thmpucTU= github.com/hashicorp/serf v0.8.1-0.20161007004122-1d4fa605f6ff/go.mod h1:h/Ru6tmZazX7WO/GDmwdpS975F019L4t5ng5IgwbNrE= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weKRL81bEnm8A0HT1/CAelMQDBuQIfLw8n+d6xI= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/influxdata/influxdb v1.2.3-0.20170331210902-15e594fc09f1 h1:GUipvZTEOj/jmmk0RcCFL0JKMPuTiwivshNNc2cx5dk= github.com/influxdata/influxdb v1.2.3-0.20170331210902-15e594fc09f1/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= @@ -273,16 +323,22 @@ github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBv github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.1.1-0.20150905172533-109e267447e9 h1:9BJzO5IK9VOH5HV48q1+pV+V5wiUA30N+gQAsA6HGtY= github.com/julienschmidt/httprouter v1.1.1-0.20150905172533-109e267447e9/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.1.0 h1:ZqfnKyx9KGpRcW04j5nnPDgRgoXUeLh2YFBeFzphcA0= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 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 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 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= @@ -314,7 +370,12 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oklog/oklog v0.2.3-0.20170918173356-f857583a70c3 h1:2RCgCV8V4tpTv/J6PSPXGYsuYwaKVKlN3vv45d2JFHI= github.com/oklog/oklog v0.2.3-0.20170918173356-f857583a70c3/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= +github.com/oklog/oklog v0.3.2 h1:wVfs8F+in6nTBMkA7CbRw+zZMIB7nNM825cM1wuzoTk= +github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= +github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= +github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/oklog/ulid v0.3.1-0.20170117200651-66bb6560562f/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= @@ -326,6 +387,7 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.14.0 h1:ep6kpPVwmr/nTbklSx2nrLNSIO62DoYAhnPNIMhK8gI= github.com/onsi/gomega v1.14.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= +github.com/opentracing-contrib/go-stdlib v0.0.0-20170113013457-1de4cc2120e7 h1:8KbikWulLUcMM96hBxjgoo6gTmCkG6HYSDohv/WygYU= github.com/opentracing-contrib/go-stdlib v0.0.0-20170113013457-1de4cc2120e7/go.mod h1:PLldrQSroqzH70Xl+1DQcGnefIbqsKR7UDaiux3zV+w= github.com/opentracing/opentracing-go v1.0.1/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= @@ -334,6 +396,7 @@ github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0Mw github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/peterbourgon/diskv v2.0.2-0.20180312054125-0646ccaebea1+incompatible h1:FhnA4iH8T/yYW+AolPONZjGE897wxj3MAzfEbrZkSYw= github.com/peterbourgon/diskv v2.0.2-0.20180312054125-0646ccaebea1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.4.1+incompatible h1:mFe7ttWaflA46Mhqh+jUfjp2qTbPYxLB2/OyBppH9dg= @@ -357,17 +420,22 @@ github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLk github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/tsdb v0.0.0-20181003080831-0ce41118ed20 h1:Jh/eKJuru9z9u3rUGdQ8gYc3aZmCGkjXT3gmy0Ex8W8= github.com/prometheus/tsdb v0.0.0-20181003080831-0ce41118ed20/go.mod h1:lFf/o1J2a31WmWQbxYXfY1azJK5Xp5D8hwKMnVMBTGU= +github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/samuel/go-zookeeper v0.0.0-20161028232340-1d7be4effb13/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da h1:p3Vo3i64TCLY7gIfzeQaUJ+kppEO5WQG3cL8iE8tGHU= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0= github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371 h1:SWV2fHctRpRrp49VXJ6UZja7gU9QLHwRpIPBN89SKEo= github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/shurcooL/vfsgen v0.0.0-20180711163814-62bca832be04 h1:y0cMJ0qjii33BnD6tMGcF/+gHYsoKQ6tbwQpy233OII= github.com/shurcooL/vfsgen v0.0.0-20180711163814-62bca832be04/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= @@ -383,6 +451,7 @@ github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2 github.com/spf13/pflag v1.0.0/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= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -396,6 +465,7 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5 h1:dPmz1Snjq0kmkz159iL7S6WzdahUTHnHB5M56WFVifs= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= @@ -405,6 +475,7 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/proto/otlp v0.7.0 h1:rwOQPCuKAKmwGKq2aVNnYIibI6wnV7EvzgfTCzcdGg8= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -429,8 +500,10 @@ golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u0 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 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -446,6 +519,7 @@ golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPI golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/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 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs= 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= @@ -571,6 +645,7 @@ golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -684,6 +759,7 @@ google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCID google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/cloud v0.0.0-20160622021550-0a83eba2cadb h1:IAkbDsVCpt6+HIt4uWPISM5JflWvw6fMk+A1S8aZLxc= google.golang.org/cloud v0.0.0-20160622021550-0a83eba2cadb/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -758,6 +834,7 @@ google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQ google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.39.0 h1:Klz8I9kdtkIN6EpHHUOMLCYhTn/2WAe5a0s1hcBkdTI= google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 h1:M1YKkFIboKNieVO5DLUEVzQfGwJD30Nv2jfUgzb5UcE= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/grpc/examples v0.0.0-20210803221256-6ba56c814be7 h1:k3XsiLoPLXhNlZJy1sHKvkgGEfpMk8bsdJVHKKhTdrc= google.golang.org/grpc/examples v0.0.0-20210803221256-6ba56c814be7/go.mod h1:bF8wuZSAZTcbF7ZPKrDI/qY52toTP/yxLpRRY4Eu9Js= @@ -775,11 +852,14 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/alecthomas/kingpin.v2 v2.2.5 h1:qskSCq465uEvC3oGocwvZNsO3RF3SpLVLumOAhL0bXo= gopkg.in/alecthomas/kingpin.v2 v2.2.5/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +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.3.0/go.mod h1:Fyux9zXlo4rWoMSIzpn9fDAYjalPqJ/K1qJ27s+7ltE= gopkg.in/fsnotify/fsnotify.v1 v1.4.7 h1:XNNYLJHt73EyYiCZi6+xjupS9CpvmiDgjPTAjrBlQbo= @@ -812,6 +892,9 @@ k8s.io/client-go v8.0.0+incompatible h1:tTI4hRmb1DRMl4fG6Vclfdi6nTM82oIrTT7Hfitm k8s.io/client-go v8.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= k8s.io/kube-openapi v0.0.0-20180629012420-d83b052f768a h1:tHgpQvrWaYfrnC8G4N0Oszw5HHCsZxKilDi2R7HuCSM= k8s.io/kube-openapi v0.0.0-20180629012420-d83b052f768a/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= +rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/pkg/query-service/model/response.go b/pkg/query-service/model/response.go index 113e5ec5ea..db6b19c6fb 100644 --- a/pkg/query-service/model/response.go +++ b/pkg/query-service/model/response.go @@ -6,6 +6,7 @@ import ( "strconv" "time" + "github.com/prometheus/prometheus/pkg/labels" "github.com/prometheus/prometheus/promql" "github.com/prometheus/prometheus/util/stats" ) @@ -34,6 +35,58 @@ type QueryData struct { Stats *stats.QueryStats `json:"stats,omitempty"` } +type RuleResponseItem struct { + Id int `json:"id" db:"id"` + UpdatedAt time.Time `json:"updated_at" db:"updated_at"` + Data string `json:"data" db:"data"` +} + +type ChannelItem struct { + Id int `json:"id" db:"id"` + CreatedAt time.Time `json:"created_at" db:"created_at"` + UpdatedAt time.Time `json:"updated_at" db:"updated_at"` + Name string `json:"name" db:"name"` + Type string `json:"type" db:"type"` + Data string `json:"data" db:"data"` +} + +// Receiver configuration provides configuration on how to contact a receiver. +type Receiver struct { + // A unique identifier for this receiver. + Name string `yaml:"name" json:"name"` + + EmailConfigs interface{} `yaml:"email_configs,omitempty" json:"email_configs,omitempty"` + PagerdutyConfigs interface{} `yaml:"pagerduty_configs,omitempty" json:"pagerduty_configs,omitempty"` + SlackConfigs interface{} `yaml:"slack_configs,omitempty" json:"slack_configs,omitempty"` + WebhookConfigs interface{} `yaml:"webhook_configs,omitempty" json:"webhook_configs,omitempty"` + OpsGenieConfigs interface{} `yaml:"opsgenie_configs,omitempty" json:"opsgenie_configs,omitempty"` + WechatConfigs interface{} `yaml:"wechat_configs,omitempty" json:"wechat_configs,omitempty"` + PushoverConfigs interface{} `yaml:"pushover_configs,omitempty" json:"pushover_configs,omitempty"` + VictorOpsConfigs interface{} `yaml:"victorops_configs,omitempty" json:"victorops_configs,omitempty"` + SNSConfigs interface{} `yaml:"sns_configs,omitempty" json:"sns_configs,omitempty"` +} + +type ReceiverResponse struct { + Status string `json:"status"` + Data Receiver `json:"data"` +} + +// AlertDiscovery has info for all active alerts. +type AlertDiscovery struct { + Alerts []*AlertingRuleResponse `json:"rules"` +} + +// Alert has info for an alert. +type AlertingRuleResponse struct { + Labels labels.Labels `json:"labels"` + Annotations labels.Labels `json:"annotations"` + State string `json:"state"` + Name string `json:"name"` + Id int `json:"id"` + // ActiveAt *time.Time `json:"activeAt,omitempty"` + // Value float64 `json:"value"` +} + type ServiceItem struct { ServiceName string `json:"serviceName" db:"serviceName"` Percentile99 float32 `json:"p99" db:"p99"`