From e630d9aa639d517182ec528258668375b1f6e40d Mon Sep 17 00:00:00 2001 From: Raghav Rastogi Date: Sat, 2 Sep 2023 22:47:33 +0530 Subject: [PATCH 1/6] chnage package structure --- .gitignore | 4 ++- alerts/vmalerts.go | 23 +++++++++++++ {commands => cmd}/alerts.go | 10 +++--- {commands => cmd}/groups.go | 6 ++-- main.go | 7 ++-- models/vmalerts.go | 27 ---------------- models/vmgroups.go | 64 ------------------------------------- rules/vmgroups.go | 52 ++++++++++++++++++++++++++++++ 8 files changed, 90 insertions(+), 103 deletions(-) create mode 100644 alerts/vmalerts.go rename {commands => cmd}/alerts.go (91%) rename {commands => cmd}/groups.go (96%) delete mode 100644 models/vmalerts.go delete mode 100644 models/vmgroups.go create mode 100644 rules/vmgroups.go diff --git a/.gitignore b/.gitignore index 8cd0df3..7677301 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ .vscode -.idea \ No newline at end of file +.idea +vendor/ +nimbus \ No newline at end of file diff --git a/alerts/vmalerts.go b/alerts/vmalerts.go new file mode 100644 index 0000000..eb94591 --- /dev/null +++ b/alerts/vmalerts.go @@ -0,0 +1,23 @@ +package alerts + +import "time" + +type VMAlerts struct { + Status string `json:"status"` + Data struct { + Alerts []struct { + State string `json:"state"` + Name string `json:"name"` + Value string `json:"value"` + Labels map[string]string `json:"labels"` + Annotations map[string]string `json:"annotations"` + ActiveAt time.Time `json:"activeAt"` + Id string `json:"id"` + RuleId string `json:"rule_id"` + GroupId string `json:"group_id"` + Expression string `json:"expression"` + Source string `json:"source"` + Restored bool `json:"restored"` + } `json:"alerts"` + } `json:"data"` +} diff --git a/commands/alerts.go b/cmd/alerts.go similarity index 91% rename from commands/alerts.go rename to cmd/alerts.go index 01c8f5a..3d48e01 100644 --- a/commands/alerts.go +++ b/cmd/alerts.go @@ -1,4 +1,4 @@ -package commands +package cmd import ( "context" @@ -7,8 +7,8 @@ import ( "net/http" "github.com/fatih/color" + "github.com/rag594/nimbus/alerts" "github.com/rag594/nimbus/httpClient" - "github.com/rag594/nimbus/models" "github.com/rodaine/table" "github.com/urfave/cli/v2" ) @@ -52,7 +52,7 @@ func NewAlertCommand(host string, client *httpClient.Client) *AlertCommand { }, Action: func(cCtx *cli.Context) error { res, err := client.Get(context.Background(), uri, nil) - vmAlerts := &models.VMAlerts{} + vmAlerts := &alerts.VMAlerts{} if err != nil { fmt.Println("Error in requesting vmalerts data", err) } @@ -72,13 +72,13 @@ func NewAlertCommand(host string, client *httpClient.Client) *AlertCommand { tbl.WithHeaderFormatter(headerFmt).WithFirstColumnFormatter(columnFmt) for _, vmAlert := range vmAlerts.Data.Alerts { if name != "" && vmAlert.Name == name { - tbl.AddRow(vmAlert.Labels["alertname"], vmAlert.Annotations.Summary, vmAlert.Annotations.Description, vmAlert.State) + tbl.AddRow(vmAlert.Labels["alertname"], vmAlert.Annotations["Summary"], vmAlert.Annotations["Description"], vmAlert.State) } if label != "" && value != "" { val, ok := vmAlert.Labels[label] if ok && val == value { - tbl.AddRow(vmAlert.Labels["alertname"], vmAlert.Annotations.Summary, vmAlert.Annotations.Description, vmAlert.State) + tbl.AddRow(vmAlert.Labels["alertname"], vmAlert.Annotations["Summary"], vmAlert.Annotations["Description"], vmAlert.State) } } diff --git a/commands/groups.go b/cmd/groups.go similarity index 96% rename from commands/groups.go rename to cmd/groups.go index 12fadbb..54fe4f6 100644 --- a/commands/groups.go +++ b/cmd/groups.go @@ -1,4 +1,4 @@ -package commands +package cmd import ( "context" @@ -7,7 +7,7 @@ import ( "net/http" "github.com/rag594/nimbus/httpClient" - "github.com/rag594/nimbus/models" + "github.com/rag594/nimbus/rules" "github.com/fatih/color" "github.com/rodaine/table" @@ -54,7 +54,7 @@ func NewGroupCommand(host string, client *httpClient.Client) *GroupCommand { }, Action: func(cCtx *cli.Context) error { res, err := client.Get(context.Background(), uri, nil) - vmGroups := &models.VMGroups{} + vmGroups := &rules.VMGroups{} if err != nil { fmt.Println("Error in requesting groups data", err) } diff --git a/main.go b/main.go index fe2b78c..0db3d47 100644 --- a/main.go +++ b/main.go @@ -2,10 +2,11 @@ package main import ( "log" - commands "github.com/rag594/nimbus/commands" - "github.com/rag594/nimbus/httpClient" "os" + commands "github.com/rag594/nimbus/cmd" + "github.com/rag594/nimbus/httpClient" + "github.com/urfave/cli/v2" ) @@ -20,7 +21,7 @@ func main() { groupCommand := commands.NewGroupCommand(vmHost, client).Command cliCommands := []*cli.Command{alertCommand, groupCommand} app := &cli.App{ - Name: "CLI for VM Alerts", + Name: "CLI for VM Alerts", Commands: cliCommands, } diff --git a/models/vmalerts.go b/models/vmalerts.go deleted file mode 100644 index 3eb4e52..0000000 --- a/models/vmalerts.go +++ /dev/null @@ -1,27 +0,0 @@ -package models - -import "time" - -type VMAlerts struct { - Status string `json:"status"` - Data struct { - Alerts []struct { - State string `json:"state"` - Name string `json:"name"` - Value string `json:"value"` - Labels map[string]string `json:"labels"` - Annotations struct { - Description string `json:"description"` - Mode string `json:"mode"` - Summary string `json:"summary"` - } `json:"annotations"` - ActiveAt time.Time `json:"activeAt"` - Id string `json:"id"` - RuleId string `json:"rule_id"` - GroupId string `json:"group_id"` - Expression string `json:"expression"` - Source string `json:"source"` - Restored bool `json:"restored"` - } `json:"alerts"` - } `json:"data"` -} diff --git a/models/vmgroups.go b/models/vmgroups.go deleted file mode 100644 index e47c07b..0000000 --- a/models/vmgroups.go +++ /dev/null @@ -1,64 +0,0 @@ -package models - -import "time" - -type VMGroups struct { - Status string `json:"status"` - Data struct { - Groups []struct { - Name string `json:"name"` - Rules []struct { - State string `json:"state"` - Name string `json:"name"` - Query string `json:"query"` - Duration int `json:"duration"` - Labels map[string]string `json:"labels"` - Annotations struct { - Summary string `json:"summary"` - } `json:"annotations"` - LastError string `json:"lastError"` - EvaluationTime float64 `json:"evaluationTime"` - LastEvaluation time.Time `json:"lastEvaluation"` - Health string `json:"health"` - Type string `json:"type"` - DatasourceType string `json:"datasourceType"` - LastSamples int `json:"lastSamples"` - LastSeriesFetched int `json:"lastSeriesFetched"` - Id string `json:"id"` - GroupId string `json:"group_id"` - Debug bool `json:"debug"` - MaxUpdatesEntries int `json:"max_updates_entries"` - Alerts []struct { - State string `json:"state"` - Name string `json:"name"` - Value string `json:"value"` - Labels struct { - Alertgroup string `json:"alertgroup"` - Alertname string `json:"alertname"` - Hpa string `json:"hpa"` - Namespace string `json:"namespace"` - Severity string `json:"severity"` - Squadcast string `json:"squadcast"` - Team string `json:"team"` - } `json:"labels"` - Annotations struct { - Summary string `json:"summary"` - } `json:"annotations"` - ActiveAt time.Time `json:"activeAt"` - Id string `json:"id"` - RuleId string `json:"rule_id"` - GroupId string `json:"group_id"` - Expression string `json:"expression"` - Source string `json:"source"` - Restored bool `json:"restored"` - } `json:"alerts,omitempty"` - } `json:"rules"` - Interval int `json:"interval"` - LastEvaluation time.Time `json:"lastEvaluation"` - Type string `json:"type"` - Id string `json:"id"` - File string `json:"file"` - Concurrency int `json:"concurrency"` - } `json:"groups"` - } `json:"data"` -} diff --git a/rules/vmgroups.go b/rules/vmgroups.go new file mode 100644 index 0000000..7dcb503 --- /dev/null +++ b/rules/vmgroups.go @@ -0,0 +1,52 @@ +package rules + +import "time" + +type VMGroups struct { + Status string `json:"status"` + Data struct { + Groups []struct { + Name string `json:"name"` + Rules []struct { + State string `json:"state"` + Name string `json:"name"` + Query string `json:"query"` + Duration int `json:"duration"` + Labels map[string]string `json:"labels"` + Annotations map[string]string `json:"annotations"` + LastError string `json:"lastError"` + EvaluationTime float64 `json:"evaluationTime"` + LastEvaluation time.Time `json:"lastEvaluation"` + Health string `json:"health"` + Type string `json:"type"` + DatasourceType string `json:"datasourceType"` + LastSamples int `json:"lastSamples"` + LastSeriesFetched int `json:"lastSeriesFetched"` + Id string `json:"id"` + GroupId string `json:"group_id"` + Debug bool `json:"debug"` + MaxUpdatesEntries int `json:"max_updates_entries"` + Alerts []struct { + State string `json:"state"` + Name string `json:"name"` + Value string `json:"value"` + Labels map[string]string `json:"labels"` + Annotations map[string]string `json:"annotations"` + ActiveAt time.Time `json:"activeAt"` + Id string `json:"id"` + RuleId string `json:"rule_id"` + GroupId string `json:"group_id"` + Expression string `json:"expression"` + Source string `json:"source"` + Restored bool `json:"restored"` + } `json:"alerts,omitempty"` + } `json:"rules"` + Interval int `json:"interval"` + LastEvaluation time.Time `json:"lastEvaluation"` + Type string `json:"type"` + Id string `json:"id"` + File string `json:"file"` + Concurrency int `json:"concurrency"` + } `json:"groups"` + } `json:"data"` +} From 9d2eb1bf88249b088c6bfa76f54964108a0a6cd5 Mon Sep 17 00:00:00 2001 From: Raghav Rastogi Date: Sun, 3 Sep 2023 11:37:24 +0530 Subject: [PATCH 2/6] move alerts, rules to different packages --- alerts/alerts_table.go | 50 ++++++++++++++++++++ alerts/cmd/alerts.go | 61 ++++++++++++++++++++++++ alerts/vmalerts.go | 40 ++++++++-------- cmd/alerts.go | 94 ------------------------------------- cmd/groups.go | 100 ---------------------------------------- httpClient/client.go | 2 +- httpClient/vm_client.go | 64 +++++++++++++++++++++++++ main.go | 8 ++-- rules/cmd/groups.go | 67 +++++++++++++++++++++++++++ rules/rules_table.go | 54 ++++++++++++++++++++++ rules/vmgroups.go | 93 ++++++++++++++++++------------------- 11 files changed, 369 insertions(+), 264 deletions(-) create mode 100644 alerts/alerts_table.go create mode 100644 alerts/cmd/alerts.go delete mode 100644 cmd/alerts.go delete mode 100644 cmd/groups.go create mode 100644 httpClient/vm_client.go create mode 100644 rules/cmd/groups.go create mode 100644 rules/rules_table.go diff --git a/alerts/alerts_table.go b/alerts/alerts_table.go new file mode 100644 index 0000000..5aa279c --- /dev/null +++ b/alerts/alerts_table.go @@ -0,0 +1,50 @@ +package alerts + +import ( + "github.com/fatih/color" + "github.com/rodaine/table" +) + +type alertsTable struct { + table table.Table + alerts *VMAlertsResponse +} + +func NewAlertsTable(alerts *VMAlertsResponse, columns ...interface{}) alertsTable { + headerFmt := color.New(color.FgGreen, color.Underline).SprintfFunc() + columnFmt := color.New(color.FgYellow).SprintfFunc() + + tbl := table.New(columns...) + tbl.WithHeaderFormatter(headerFmt).WithFirstColumnFormatter(columnFmt) + + return alertsTable{table: tbl, alerts: alerts} +} + +func (t *alertsTable) ListAlertsByName(name string) table.Table { + if name == "" { + return nil + } + + for _, vmAlert := range t.alerts.Data.Alerts { + if vmAlert.Name == name { + t.table.AddRow(vmAlert.Labels["alertname"], vmAlert.Annotations["Summary"], vmAlert.Annotations["Description"], vmAlert.State, vmAlert.ActiveAt) + } + } + + return t.table +} + +func (t *alertsTable) ListAlertsByLabels(label, value string) table.Table { + if label == "" && value == "" { + return nil + } + + for _, vmAlert := range t.alerts.Data.Alerts { + val, ok := vmAlert.Labels[label] + if ok && val == value { + t.table.AddRow(vmAlert.Labels["alertname"], vmAlert.Annotations["Summary"], vmAlert.Annotations["Description"], vmAlert.State) + } + } + + return t.table +} diff --git a/alerts/cmd/alerts.go b/alerts/cmd/alerts.go new file mode 100644 index 0000000..92b3928 --- /dev/null +++ b/alerts/cmd/alerts.go @@ -0,0 +1,61 @@ +package cmd + +import ( + "github.com/rag594/nimbus/alerts" + "github.com/rag594/nimbus/httpClient" + "github.com/urfave/cli/v2" +) + +type AlertCommand struct { + Command *cli.Command +} + +func NewAlertCommand(vmClient *httpClient.VMClient) *AlertCommand { + var ( + label string + name string + value string + ) + command := &cli.Command{ + Name: "alerts", + Usage: "filter your alerts by name or labels", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "label", + Usage: "filter alerts by labels, provide key and then value. Example --label --value ", + Destination: &label, + Category: "Labels", + }, + &cli.StringFlag{ + Name: "value", + Usage: "filter alerts by value, value should be provided with label. Example --label --value ", + Destination: &value, + Category: "Labels", + }, + &cli.StringFlag{ + Name: "name", + Usage: "filter alerts by name", + Destination: &name, + }, + }, + Action: func(cCtx *cli.Context) error { + vmAlerts, err := vmClient.GetVMAlerts() + if err != nil { + return err + } + + alertsTable := alerts.NewAlertsTable(vmAlerts, "AlertName", "Summary", "Description", "State", "ActiveAt") + if table := alertsTable.ListAlertsByLabels(label, value); table != nil { + table.Print() + } + + if table := alertsTable.ListAlertsByName(name); table != nil { + table.Print() + } + + return nil + }, + } + + return &AlertCommand{Command: command} +} diff --git a/alerts/vmalerts.go b/alerts/vmalerts.go index eb94591..ca3b942 100644 --- a/alerts/vmalerts.go +++ b/alerts/vmalerts.go @@ -2,22 +2,26 @@ package alerts import "time" -type VMAlerts struct { - Status string `json:"status"` - Data struct { - Alerts []struct { - State string `json:"state"` - Name string `json:"name"` - Value string `json:"value"` - Labels map[string]string `json:"labels"` - Annotations map[string]string `json:"annotations"` - ActiveAt time.Time `json:"activeAt"` - Id string `json:"id"` - RuleId string `json:"rule_id"` - GroupId string `json:"group_id"` - Expression string `json:"expression"` - Source string `json:"source"` - Restored bool `json:"restored"` - } `json:"alerts"` - } `json:"data"` +type VMAlertsResponse struct { + Status string `json:"status"` + Data AlertsData `json:"data"` +} + +type AlertsData struct { + Alerts []Alert `json:"alerts"` +} + +type Alert struct { + State string `json:"state"` + Name string `json:"name"` + Value string `json:"value"` + Labels map[string]string `json:"labels"` + Annotations map[string]string `json:"annotations"` + ActiveAt time.Time `json:"activeAt"` + Id string `json:"id"` + RuleId string `json:"rule_id"` + GroupId string `json:"group_id"` + Expression string `json:"expression"` + Source string `json:"source"` + Restored bool `json:"restored"` } diff --git a/cmd/alerts.go b/cmd/alerts.go deleted file mode 100644 index 3d48e01..0000000 --- a/cmd/alerts.go +++ /dev/null @@ -1,94 +0,0 @@ -package cmd - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - - "github.com/fatih/color" - "github.com/rag594/nimbus/alerts" - "github.com/rag594/nimbus/httpClient" - "github.com/rodaine/table" - "github.com/urfave/cli/v2" -) - -const ( - alertPath = "/api/v1/alerts" -) - -type AlertCommand struct { - Command *cli.Command -} - -func NewAlertCommand(host string, client *httpClient.Client) *AlertCommand { - uri := fmt.Sprintf("%s%s", host, alertPath) - var ( - label string - name string - value string - ) - command := &cli.Command{ - Name: "alerts", - Usage: "filter your alerts by name or labels", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "label", - Usage: "filter alerts by labels, provide key and then value. Example --label --value ", - Destination: &label, - Category: "Labels", - }, - &cli.StringFlag{ - Name: "value", - Usage: "filter alerts by value, value should be provided with label. Example --label --value ", - Destination: &value, - Category: "Labels", - }, - &cli.StringFlag{ - Name: "name", - Usage: "filter alerts by name", - Destination: &name, - }, - }, - Action: func(cCtx *cli.Context) error { - res, err := client.Get(context.Background(), uri, nil) - vmAlerts := &alerts.VMAlerts{} - if err != nil { - fmt.Println("Error in requesting vmalerts data", err) - } - defer res.Body.Close() - - if res.StatusCode == http.StatusOK { - - if err := json.NewDecoder(res.Body).Decode(vmAlerts); err != nil { - fmt.Println("Err in decoding ", err) - } - } - - headerFmt := color.New(color.FgGreen, color.Underline).SprintfFunc() - columnFmt := color.New(color.FgYellow).SprintfFunc() - - tbl := table.New("AlertName", "Summary", "Description", "State") - tbl.WithHeaderFormatter(headerFmt).WithFirstColumnFormatter(columnFmt) - for _, vmAlert := range vmAlerts.Data.Alerts { - if name != "" && vmAlert.Name == name { - tbl.AddRow(vmAlert.Labels["alertname"], vmAlert.Annotations["Summary"], vmAlert.Annotations["Description"], vmAlert.State) - } - - if label != "" && value != "" { - val, ok := vmAlert.Labels[label] - if ok && val == value { - tbl.AddRow(vmAlert.Labels["alertname"], vmAlert.Annotations["Summary"], vmAlert.Annotations["Description"], vmAlert.State) - } - } - - } - - tbl.Print() - - return nil - }, - } - - return &AlertCommand{Command: command} -} diff --git a/cmd/groups.go b/cmd/groups.go deleted file mode 100644 index 54fe4f6..0000000 --- a/cmd/groups.go +++ /dev/null @@ -1,100 +0,0 @@ -package cmd - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - - "github.com/rag594/nimbus/httpClient" - "github.com/rag594/nimbus/rules" - - "github.com/fatih/color" - "github.com/rodaine/table" - "github.com/urfave/cli/v2" -) - -const ( - rulesPath = "/api/v1/rules" -) - -type GroupCommand struct { - Command *cli.Command -} - -func NewGroupCommand(host string, client *httpClient.Client) *GroupCommand { - uri := fmt.Sprintf("%s%s", host, rulesPath) - var ( - label string - groupName string - value string - ) - command := &cli.Command{ - Name: "group", - Usage: "lists down the rules", - UsageText: "use name flag to get info alertRules applied and get their state", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "label", - Usage: "filter rules by labels, provide key and then value. Example --label --value ", - Destination: &label, - Category: "Labels", - }, - &cli.StringFlag{ - Name: "value", - Usage: "filter rules by value, value should be provided with label. Example --label --value ", - Destination: &value, - Category: "Labels", - }, - &cli.StringFlag{ - Name: "name", - Usage: "list rules name wise", - Destination: &groupName, - }, - }, - Action: func(cCtx *cli.Context) error { - res, err := client.Get(context.Background(), uri, nil) - vmGroups := &rules.VMGroups{} - if err != nil { - fmt.Println("Error in requesting groups data", err) - } - defer res.Body.Close() - - if res.StatusCode == http.StatusOK { - - if err := json.NewDecoder(res.Body).Decode(vmGroups); err != nil { - fmt.Println("Err in decoding ", err) - } - } - - headerFmt := color.New(color.FgGreen, color.Underline).SprintfFunc() - columnFmt := color.New(color.FgYellow).SprintfFunc() - - tbl := table.New("Rule", "State") - tbl.WithHeaderFormatter(headerFmt).WithFirstColumnFormatter(columnFmt) - for _, vmGroup := range vmGroups.Data.Groups { - if groupName != "" && vmGroup.Name == groupName { - for _, rule := range vmGroup.Rules { - tbl.AddRow(rule.Name, rule.State) - } - } - - if label != "" && value != "" { - for _, rule := range vmGroup.Rules { - val, ok := rule.Labels[label] - if ok && val == value { - tbl.AddRow(rule.Name, rule.State) - } - } - } - - } - - tbl.Print() - - return nil - }, - } - - return &GroupCommand{Command: command} -} diff --git a/httpClient/client.go b/httpClient/client.go index 0042f1a..a505d2d 100644 --- a/httpClient/client.go +++ b/httpClient/client.go @@ -6,7 +6,7 @@ import ( "time" ) -//Client defines Http Client +// Client defines Http Client type Client struct { httpClient *http.Client } diff --git a/httpClient/vm_client.go b/httpClient/vm_client.go new file mode 100644 index 0000000..ead6263 --- /dev/null +++ b/httpClient/vm_client.go @@ -0,0 +1,64 @@ +package httpClient + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "net/http" + + "github.com/rag594/nimbus/alerts" + "github.com/rag594/nimbus/rules" +) + +const ( + alertsURI = "/api/v1/alerts" + rulesURI = "/api/v1/rules" +) + +type VMClient struct { + client *Client + host string +} + +func NewVmClient(host string, client *Client) *VMClient { + return &VMClient{client: client, host: host} +} + +func (v *VMClient) GetVMAlerts() (*alerts.VMAlertsResponse, error) { + res, err := v.client.Get(context.Background(), alertsURI, nil) + if err != nil { + fmt.Println("Error in requesting vmalerts data", err) + } + defer res.Body.Close() + + if res.StatusCode == http.StatusOK { + vmAlerts := &alerts.VMAlertsResponse{} + if err := json.NewDecoder(res.Body).Decode(vmAlerts); err != nil { + fmt.Println("Err in decoding ", err) + return nil, err + } + return vmAlerts, nil + } + + return nil, errors.New("failure in getting alerts repsonse from vm") +} + +func (v *VMClient) GetVMGroups() (*rules.VMGroupResponse, error) { + res, err := v.client.Get(context.Background(), rulesURI, nil) + if err != nil { + fmt.Println("Error in requesting vmalerts data", err) + } + defer res.Body.Close() + + if res.StatusCode == http.StatusOK { + vmRules := &rules.VMGroupResponse{} + if err := json.NewDecoder(res.Body).Decode(vmRules); err != nil { + fmt.Println("Err in decoding ", err) + return nil, err + } + return vmRules, nil + } + + return nil, errors.New("failure in getting rules repsonse from vm") +} diff --git a/main.go b/main.go index 0db3d47..cf576ad 100644 --- a/main.go +++ b/main.go @@ -4,8 +4,9 @@ import ( "log" "os" - commands "github.com/rag594/nimbus/cmd" + alertsCmd "github.com/rag594/nimbus/alerts/cmd" "github.com/rag594/nimbus/httpClient" + rulesCmd "github.com/rag594/nimbus/rules/cmd" "github.com/urfave/cli/v2" ) @@ -17,8 +18,9 @@ func main() { os.Exit(0) } client := httpClient.Init() - alertCommand := commands.NewAlertCommand(vmHost, client).Command - groupCommand := commands.NewGroupCommand(vmHost, client).Command + vmClient := httpClient.NewVmClient(vmHost, client) + alertCommand := alertsCmd.NewAlertCommand(vmClient).Command + groupCommand := rulesCmd.NewGroupCommand(vmClient).Command cliCommands := []*cli.Command{alertCommand, groupCommand} app := &cli.App{ Name: "CLI for VM Alerts", diff --git a/rules/cmd/groups.go b/rules/cmd/groups.go new file mode 100644 index 0000000..7e4d208 --- /dev/null +++ b/rules/cmd/groups.go @@ -0,0 +1,67 @@ +package cmd + +import ( + "github.com/rag594/nimbus/httpClient" + "github.com/rag594/nimbus/rules" + + "github.com/urfave/cli/v2" +) + +const ( + rulesPath = "/api/v1/rules" +) + +type GroupCommand struct { + Command *cli.Command +} + +func NewGroupCommand(vmClient *httpClient.VMClient) *GroupCommand { + var ( + label string + groupName string + value string + ) + command := &cli.Command{ + Name: "group", + Usage: "lists down the rules", + UsageText: "use name flag to get info alertRules applied and get their state", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "label", + Usage: "filter rules by labels, provide key and then value. Example --label --value ", + Destination: &label, + Category: "Labels", + }, + &cli.StringFlag{ + Name: "value", + Usage: "filter rules by value, value should be provided with label. Example --label --value ", + Destination: &value, + Category: "Labels", + }, + &cli.StringFlag{ + Name: "name", + Usage: "list rules name wise", + Destination: &groupName, + }, + }, + Action: func(cCtx *cli.Context) error { + vmRules, err := vmClient.GetVMGroups() + if err != nil { + return err + } + vmRulesTable := rules.NewRulesTable(vmRules, "Rule", "State") + + if table := vmRulesTable.ListRulesByGroupName(groupName); table != nil { + table.Print() + } + + if table := vmRulesTable.ListRulesByLabels(label, value); table != nil { + table.Print() + } + + return nil + }, + } + + return &GroupCommand{Command: command} +} diff --git a/rules/rules_table.go b/rules/rules_table.go new file mode 100644 index 0000000..e98d86b --- /dev/null +++ b/rules/rules_table.go @@ -0,0 +1,54 @@ +package rules + +import ( + "github.com/fatih/color" + "github.com/rodaine/table" +) + +type rulesTable struct { + table table.Table + rules *VMGroupResponse +} + +func NewRulesTable(rules *VMGroupResponse, columns ...interface{}) rulesTable { + headerFmt := color.New(color.FgGreen, color.Underline).SprintfFunc() + columnFmt := color.New(color.FgYellow).SprintfFunc() + + tbl := table.New(columns...) + tbl.WithHeaderFormatter(headerFmt).WithFirstColumnFormatter(columnFmt) + + return rulesTable{table: tbl, rules: rules} +} + +func (t *rulesTable) ListRulesByGroupName(name string) table.Table { + if name == "" { + return nil + } + + for _, vmGroup := range t.rules.Data.Groups { + if vmGroup.Name == name { + for _, rule := range vmGroup.Rules { + t.table.AddRow(rule.Name, rule.State) + } + } + } + + return t.table +} + +func (t *rulesTable) ListRulesByLabels(label, value string) table.Table { + if label == "" && value == "" { + return nil + } + + for _, vmGroup := range t.rules.Data.Groups { + for _, rule := range vmGroup.Rules { + val, ok := rule.Labels[label] + if ok && val == value { + t.table.AddRow(rule.Name, rule.State) + } + } + } + + return t.table +} diff --git a/rules/vmgroups.go b/rules/vmgroups.go index 7dcb503..681ec0b 100644 --- a/rules/vmgroups.go +++ b/rules/vmgroups.go @@ -1,52 +1,49 @@ package rules -import "time" +import ( + "time" -type VMGroups struct { - Status string `json:"status"` - Data struct { - Groups []struct { - Name string `json:"name"` - Rules []struct { - State string `json:"state"` - Name string `json:"name"` - Query string `json:"query"` - Duration int `json:"duration"` - Labels map[string]string `json:"labels"` - Annotations map[string]string `json:"annotations"` - LastError string `json:"lastError"` - EvaluationTime float64 `json:"evaluationTime"` - LastEvaluation time.Time `json:"lastEvaluation"` - Health string `json:"health"` - Type string `json:"type"` - DatasourceType string `json:"datasourceType"` - LastSamples int `json:"lastSamples"` - LastSeriesFetched int `json:"lastSeriesFetched"` - Id string `json:"id"` - GroupId string `json:"group_id"` - Debug bool `json:"debug"` - MaxUpdatesEntries int `json:"max_updates_entries"` - Alerts []struct { - State string `json:"state"` - Name string `json:"name"` - Value string `json:"value"` - Labels map[string]string `json:"labels"` - Annotations map[string]string `json:"annotations"` - ActiveAt time.Time `json:"activeAt"` - Id string `json:"id"` - RuleId string `json:"rule_id"` - GroupId string `json:"group_id"` - Expression string `json:"expression"` - Source string `json:"source"` - Restored bool `json:"restored"` - } `json:"alerts,omitempty"` - } `json:"rules"` - Interval int `json:"interval"` - LastEvaluation time.Time `json:"lastEvaluation"` - Type string `json:"type"` - Id string `json:"id"` - File string `json:"file"` - Concurrency int `json:"concurrency"` - } `json:"groups"` - } `json:"data"` + "github.com/rag594/nimbus/alerts" +) + +type VMGroupResponse struct { + Status string `json:"status"` + Data GroupData `json:"data"` +} + +type GroupData struct { + Groups []Group `json:"groups"` +} + +type Group struct { + Name string `json:"name"` + Rules []Rule `json:"rules"` + Interval int `json:"interval"` + LastEvaluation time.Time `json:"lastEvaluation"` + Type string `json:"type"` + Id string `json:"id"` + File string `json:"file"` + Concurrency int `json:"concurrency"` +} + +type Rule struct { + State string `json:"state"` + Name string `json:"name"` + Query string `json:"query"` + Duration int `json:"duration"` + Labels map[string]string `json:"labels"` + Annotations map[string]string `json:"annotations"` + LastError string `json:"lastError"` + EvaluationTime float64 `json:"evaluationTime"` + LastEvaluation time.Time `json:"lastEvaluation"` + Health string `json:"health"` + Type string `json:"type"` + DatasourceType string `json:"datasourceType"` + LastSamples int `json:"lastSamples"` + LastSeriesFetched int `json:"lastSeriesFetched"` + Id string `json:"id"` + GroupId string `json:"group_id"` + Debug bool `json:"debug"` + MaxUpdatesEntries int `json:"max_updates_entries"` + Alerts []alerts.Alert `json:"alerts,omitempty"` } From 8639fda58b54759c404b5daefc9e6f199a91c9c3 Mon Sep 17 00:00:00 2001 From: Raghav Rastogi Date: Sun, 3 Sep 2023 12:19:50 +0530 Subject: [PATCH 3/6] fix uri --- alerts/alerts_table.go | 4 ++-- httpClient/vm_client.go | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/alerts/alerts_table.go b/alerts/alerts_table.go index 5aa279c..8ebb92f 100644 --- a/alerts/alerts_table.go +++ b/alerts/alerts_table.go @@ -27,7 +27,7 @@ func (t *alertsTable) ListAlertsByName(name string) table.Table { for _, vmAlert := range t.alerts.Data.Alerts { if vmAlert.Name == name { - t.table.AddRow(vmAlert.Labels["alertname"], vmAlert.Annotations["Summary"], vmAlert.Annotations["Description"], vmAlert.State, vmAlert.ActiveAt) + t.table.AddRow(vmAlert.Name, vmAlert.Annotations["Summary"], vmAlert.Annotations["Description"], vmAlert.State, vmAlert.ActiveAt) } } @@ -42,7 +42,7 @@ func (t *alertsTable) ListAlertsByLabels(label, value string) table.Table { for _, vmAlert := range t.alerts.Data.Alerts { val, ok := vmAlert.Labels[label] if ok && val == value { - t.table.AddRow(vmAlert.Labels["alertname"], vmAlert.Annotations["Summary"], vmAlert.Annotations["Description"], vmAlert.State) + t.table.AddRow(vmAlert.Name, vmAlert.Annotations["Summary"], vmAlert.Annotations["Description"], vmAlert.State) } } diff --git a/httpClient/vm_client.go b/httpClient/vm_client.go index ead6263..4b1e177 100644 --- a/httpClient/vm_client.go +++ b/httpClient/vm_client.go @@ -26,7 +26,8 @@ func NewVmClient(host string, client *Client) *VMClient { } func (v *VMClient) GetVMAlerts() (*alerts.VMAlertsResponse, error) { - res, err := v.client.Get(context.Background(), alertsURI, nil) + uri := fmt.Sprintf("%s%s", v.host, alertsURI) + res, err := v.client.Get(context.Background(), uri, nil) if err != nil { fmt.Println("Error in requesting vmalerts data", err) } @@ -45,7 +46,8 @@ func (v *VMClient) GetVMAlerts() (*alerts.VMAlertsResponse, error) { } func (v *VMClient) GetVMGroups() (*rules.VMGroupResponse, error) { - res, err := v.client.Get(context.Background(), rulesURI, nil) + uri := fmt.Sprintf("%s%s", v.host, rulesURI) + res, err := v.client.Get(context.Background(), uri, nil) if err != nil { fmt.Println("Error in requesting vmalerts data", err) } From 983029e6fc077a2732841616092311406bc9dd04 Mon Sep 17 00:00:00 2001 From: Raghav Rastogi Date: Sun, 3 Sep 2023 16:59:30 +0530 Subject: [PATCH 4/6] add health for rules --- rules/cmd/groups.go | 2 +- rules/rules_table.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rules/cmd/groups.go b/rules/cmd/groups.go index 7e4d208..6951076 100644 --- a/rules/cmd/groups.go +++ b/rules/cmd/groups.go @@ -49,7 +49,7 @@ func NewGroupCommand(vmClient *httpClient.VMClient) *GroupCommand { if err != nil { return err } - vmRulesTable := rules.NewRulesTable(vmRules, "Rule", "State") + vmRulesTable := rules.NewRulesTable(vmRules, "Rule", "State", "Health") if table := vmRulesTable.ListRulesByGroupName(groupName); table != nil { table.Print() diff --git a/rules/rules_table.go b/rules/rules_table.go index e98d86b..8190bf4 100644 --- a/rules/rules_table.go +++ b/rules/rules_table.go @@ -28,7 +28,7 @@ func (t *rulesTable) ListRulesByGroupName(name string) table.Table { for _, vmGroup := range t.rules.Data.Groups { if vmGroup.Name == name { for _, rule := range vmGroup.Rules { - t.table.AddRow(rule.Name, rule.State) + t.table.AddRow(rule.Name, rule.State, rule.Health) } } } @@ -45,7 +45,7 @@ func (t *rulesTable) ListRulesByLabels(label, value string) table.Table { for _, rule := range vmGroup.Rules { val, ok := rule.Labels[label] if ok && val == value { - t.table.AddRow(rule.Name, rule.State) + t.table.AddRow(rule.Name, rule.State, rule.Health) } } } From 862a8bd782ca16d6a9dba90074e1c2d8d8559d49 Mon Sep 17 00:00:00 2001 From: Raghav Rastogi Date: Sun, 3 Sep 2023 17:15:22 +0530 Subject: [PATCH 5/6] format annoations and labels --- alerts/alerts_table.go | 4 ++-- alerts/cmd/alerts.go | 2 +- alerts/vmalerts.go | 22 +++++++++++++++++++++- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/alerts/alerts_table.go b/alerts/alerts_table.go index 8ebb92f..0b34ef0 100644 --- a/alerts/alerts_table.go +++ b/alerts/alerts_table.go @@ -27,7 +27,7 @@ func (t *alertsTable) ListAlertsByName(name string) table.Table { for _, vmAlert := range t.alerts.Data.Alerts { if vmAlert.Name == name { - t.table.AddRow(vmAlert.Name, vmAlert.Annotations["Summary"], vmAlert.Annotations["Description"], vmAlert.State, vmAlert.ActiveAt) + t.table.AddRow(vmAlert.Name, vmAlert.FormatLabels(), vmAlert.FormatAnnotations(), vmAlert.State, vmAlert.ActiveAt) } } @@ -42,7 +42,7 @@ func (t *alertsTable) ListAlertsByLabels(label, value string) table.Table { for _, vmAlert := range t.alerts.Data.Alerts { val, ok := vmAlert.Labels[label] if ok && val == value { - t.table.AddRow(vmAlert.Name, vmAlert.Annotations["Summary"], vmAlert.Annotations["Description"], vmAlert.State) + t.table.AddRow(vmAlert.Name, vmAlert.FormatLabels(), vmAlert.FormatAnnotations(), vmAlert.State) } } diff --git a/alerts/cmd/alerts.go b/alerts/cmd/alerts.go index 92b3928..0be001a 100644 --- a/alerts/cmd/alerts.go +++ b/alerts/cmd/alerts.go @@ -44,7 +44,7 @@ func NewAlertCommand(vmClient *httpClient.VMClient) *AlertCommand { return err } - alertsTable := alerts.NewAlertsTable(vmAlerts, "AlertName", "Summary", "Description", "State", "ActiveAt") + alertsTable := alerts.NewAlertsTable(vmAlerts, "AlertName", "Labels", "Annotaions", "State", "ActiveAt") if table := alertsTable.ListAlertsByLabels(label, value); table != nil { table.Print() } diff --git a/alerts/vmalerts.go b/alerts/vmalerts.go index ca3b942..0dee958 100644 --- a/alerts/vmalerts.go +++ b/alerts/vmalerts.go @@ -1,6 +1,10 @@ package alerts -import "time" +import ( + "fmt" + "strings" + "time" +) type VMAlertsResponse struct { Status string `json:"status"` @@ -25,3 +29,19 @@ type Alert struct { Source string `json:"source"` Restored bool `json:"restored"` } + +func (a Alert) FormatLabels() string { + var labelsBuilder strings.Builder + for key, value := range a.Labels { + labelsBuilder.WriteString(fmt.Sprintf("%s_%s\n", key, value)) + } + return labelsBuilder.String() +} + +func (a Alert) FormatAnnotations() string { + var annotationsBuilder strings.Builder + for key, value := range a.Annotations { + annotationsBuilder.WriteString(fmt.Sprintf("%s_%s\n", key, value)) + } + return annotationsBuilder.String() +} From 6438e9faad0412d5877b2b4981cf913dbc769e15 Mon Sep 17 00:00:00 2001 From: Raghav Rastogi Date: Sun, 3 Sep 2023 17:29:08 +0530 Subject: [PATCH 6/6] format activateAt --- alerts/alerts_table.go | 4 ++-- alerts/vmalerts.go | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/alerts/alerts_table.go b/alerts/alerts_table.go index 0b34ef0..e2e08cf 100644 --- a/alerts/alerts_table.go +++ b/alerts/alerts_table.go @@ -27,7 +27,7 @@ func (t *alertsTable) ListAlertsByName(name string) table.Table { for _, vmAlert := range t.alerts.Data.Alerts { if vmAlert.Name == name { - t.table.AddRow(vmAlert.Name, vmAlert.FormatLabels(), vmAlert.FormatAnnotations(), vmAlert.State, vmAlert.ActiveAt) + t.table.AddRow(vmAlert.Name, vmAlert.FormatLabels(), vmAlert.FormatAnnotations(), vmAlert.State, vmAlert.GetActivateAt()) } } @@ -42,7 +42,7 @@ func (t *alertsTable) ListAlertsByLabels(label, value string) table.Table { for _, vmAlert := range t.alerts.Data.Alerts { val, ok := vmAlert.Labels[label] if ok && val == value { - t.table.AddRow(vmAlert.Name, vmAlert.FormatLabels(), vmAlert.FormatAnnotations(), vmAlert.State) + t.table.AddRow(vmAlert.Name, vmAlert.FormatLabels(), vmAlert.FormatAnnotations(), vmAlert.State, vmAlert.GetActivateAt()) } } diff --git a/alerts/vmalerts.go b/alerts/vmalerts.go index 0dee958..b520864 100644 --- a/alerts/vmalerts.go +++ b/alerts/vmalerts.go @@ -45,3 +45,7 @@ func (a Alert) FormatAnnotations() string { } return annotationsBuilder.String() } + +func (a Alert) GetActivateAt() string { + return a.ActiveAt.Local().Format("2006-01-02 15:04:05") +}