-
Notifications
You must be signed in to change notification settings - Fork 2.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Switch amtool to kingpin #976
Changes from 6 commits
6f7d047
d98a05e
c4706f0
3431d21
e8190b9
09a2e74
4efe840
3848aae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,17 +6,15 @@ import ( | |
"fmt" | ||
"net/http" | ||
"net/url" | ||
"path" | ||
"strings" | ||
"time" | ||
|
||
"github.com/alecthomas/kingpin" | ||
"github.com/prometheus/alertmanager/cli/format" | ||
"github.com/prometheus/alertmanager/dispatch" | ||
"github.com/prometheus/alertmanager/pkg/parse" | ||
"github.com/prometheus/alertmanager/types" | ||
"github.com/prometheus/common/model" | ||
"github.com/spf13/cobra" | ||
"github.com/spf13/viper" | ||
) | ||
|
||
type alertmanagerAlertResponse struct { | ||
|
@@ -37,61 +35,45 @@ type alertBlock struct { | |
Alerts []*dispatch.APIAlert `json:"alerts"` | ||
} | ||
|
||
// alertCmd represents the alert command | ||
var alertCmd = &cobra.Command{ | ||
Use: "alert", | ||
Short: "View and search through current alerts", | ||
Long: `View and search through current alerts. | ||
|
||
Amtool has a simplified prometheus query syntax, but contains robust support for | ||
bash variable expansions. The non-option section of arguments constructs a list | ||
of "Matcher Groups" that will be used to filter your query. The following | ||
examples will attempt to show this behaviour in action: | ||
var ( | ||
alertCmd = app.Command("alert", "View and search through current alerts") | ||
alertQueryCmd = alertCmd.Command("query", "View and search through current alerts").Default() | ||
expired = alertQueryCmd.Flag("expired", "Show expired alerts as well as active").Bool() | ||
showSilenced = alertQueryCmd.Flag("silenced", "Show silenced alerts").Short('s').Bool() | ||
alertQuery = alertQueryCmd.Arg("matcher-groups", "Query filter").Strings() | ||
) | ||
|
||
amtool alert query alertname=foo node=bar | ||
func init() { | ||
alertQueryCmd.Action(queryAlerts) | ||
longHelpText["alert"] = `View and search through current alerts. | ||
|
||
This query will match all alerts with the alertname=foo and node=bar label | ||
value pairs set. | ||
Amtool has a simplified prometheus query syntax, but contains robust support for | ||
bash variable expansions. The non-option section of arguments constructs a list | ||
of "Matcher Groups" that will be used to filter your query. The following | ||
examples will attempt to show this behaviour in action: | ||
|
||
amtool alert query foo node=bar | ||
amtool alert query alertname=foo node=bar | ||
|
||
If alertname is ommited and the first argument does not contain a '=' or a | ||
'=~' then it will be assumed to be the value of the alertname pair. | ||
This query will match all alerts with the alertname=foo and node=bar label | ||
value pairs set. | ||
|
||
amtool alert query 'alertname=~foo.*' | ||
amtool alert query foo node=bar | ||
|
||
As well as direct equality, regex matching is also supported. The '=~' syntax | ||
(similar to prometheus) is used to represent a regex match. Regex matching | ||
can be used in combination with a direct match. | ||
`, | ||
Run: CommandWrapper(queryAlerts), | ||
} | ||
If alertname is ommited and the first argument does not contain a '=' or a | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
'=~' then it will be assumed to be the value of the alertname pair. | ||
|
||
var alertQueryCmd = &cobra.Command{ | ||
Use: "query", | ||
Short: "View and search through current alerts", | ||
Long: alertCmd.Long, | ||
RunE: queryAlerts, | ||
} | ||
amtool alert query 'alertname=~foo.*' | ||
|
||
func init() { | ||
RootCmd.AddCommand(alertCmd) | ||
alertCmd.AddCommand(alertQueryCmd) | ||
alertQueryCmd.Flags().Bool("expired", false, "Show expired alerts as well as active") | ||
alertQueryCmd.Flags().BoolP("silenced", "s", false, "Show silenced alerts") | ||
viper.BindPFlag("expired", alertQueryCmd.Flags().Lookup("expired")) | ||
viper.BindPFlag("silenced", alertQueryCmd.Flags().Lookup("silenced")) | ||
As well as direct equality, regex matching is also supported. The '=~' syntax | ||
(similar to prometheus) is used to represent a regex match. Regex matching | ||
can be used in combination with a direct match.` | ||
longHelpText["alert query"] = longHelpText["alert"] | ||
} | ||
|
||
func fetchAlerts(filter string) ([]*dispatch.APIAlert, error) { | ||
alertResponse := alertmanagerAlertResponse{} | ||
|
||
u, err := GetAlertmanagerURL() | ||
if err != nil { | ||
return []*dispatch.APIAlert{}, err | ||
} | ||
|
||
u.Path = path.Join(u.Path, "/api/v1/alerts/groups") | ||
u := GetAlertmanagerURL("/api/v1/alerts/groups") | ||
u.RawQuery = "filter=" + url.QueryEscape(filter) | ||
|
||
res, err := http.Get(u.String()) | ||
|
@@ -123,23 +105,20 @@ func flattenAlertOverview(overview []*alertGroup) []*dispatch.APIAlert { | |
return alerts | ||
} | ||
|
||
func queryAlerts(cmd *cobra.Command, args []string) error { | ||
expired := viper.GetBool("expired") | ||
showSilenced := viper.GetBool("silenced") | ||
|
||
func queryAlerts(element *kingpin.ParseElement, ctx *kingpin.ParseContext) error { | ||
var filterString = "" | ||
if len(args) == 1 { | ||
if len(*alertQuery) == 1 { | ||
// If we only have one argument then it's possible that the user wants me to assume alertname=<arg> | ||
// Attempt to use the parser to pare the argument | ||
// If the parser fails then we likely don't have a (=|=~|!=|!~) so lets prepend `alertname=` to the front | ||
_, err := parse.Matcher(args[0]) | ||
_, err := parse.Matcher((*alertQuery)[0]) | ||
if err != nil { | ||
filterString = fmt.Sprintf("{alertname=%s}", args[0]) | ||
filterString = fmt.Sprintf("{alertname=%s}", (*alertQuery)[0]) | ||
} else { | ||
filterString = fmt.Sprintf("{%s}", strings.Join(args, ",")) | ||
filterString = fmt.Sprintf("{%s}", strings.Join(*alertQuery, ",")) | ||
} | ||
} else if len(args) > 1 { | ||
filterString = fmt.Sprintf("{%s}", strings.Join(args, ",")) | ||
} else if len(*alertQuery) > 1 { | ||
filterString = fmt.Sprintf("{%s}", strings.Join(*alertQuery, ",")) | ||
} | ||
|
||
fetchedAlerts, err := fetchAlerts(filterString) | ||
|
@@ -150,13 +129,13 @@ func queryAlerts(cmd *cobra.Command, args []string) error { | |
displayAlerts := []*dispatch.APIAlert{} | ||
for _, alert := range fetchedAlerts { | ||
// If we are only returning current alerts and this one has already expired skip it | ||
if !expired { | ||
if !*expired { | ||
if !alert.EndsAt.IsZero() && alert.EndsAt.Before(time.Now()) { | ||
continue | ||
} | ||
} | ||
|
||
if !showSilenced { | ||
if !*showSilenced { | ||
// If any silence mutes this alert don't show it | ||
if alert.Status.State == types.AlertStateSuppressed && len(alert.Status.SilencedBy) > 0 { | ||
continue | ||
|
@@ -166,7 +145,7 @@ func queryAlerts(cmd *cobra.Command, args []string) error { | |
displayAlerts = append(displayAlerts, alert) | ||
} | ||
|
||
formatter, found := format.Formatters[viper.GetString("output")] | ||
formatter, found := format.Formatters[*output] | ||
if !found { | ||
return errors.New("unknown output formatter") | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,13 +5,11 @@ import ( | |
"errors" | ||
"fmt" | ||
"net/http" | ||
"path" | ||
"time" | ||
|
||
"github.com/alecthomas/kingpin" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same import issue as above |
||
"github.com/prometheus/alertmanager/cli/format" | ||
"github.com/prometheus/alertmanager/config" | ||
"github.com/spf13/cobra" | ||
"github.com/spf13/viper" | ||
) | ||
|
||
// Config is the response type of alertmanager config endpoint | ||
|
@@ -43,31 +41,21 @@ type alertmanagerStatusResponse struct { | |
Error string `json:"error,omitempty"` | ||
} | ||
|
||
// alertCmd represents the alert command | ||
var configCmd = &cobra.Command{ | ||
Use: "config", | ||
Short: "View the running config", | ||
Long: `View current config | ||
// configCmd represents the config command | ||
var configCmd = app.Command("config", "View the running config").Action(queryConfig) | ||
|
||
func init() { | ||
longHelpText["config"] = `View current config | ||
The amount of output is controlled by the output selection flag: | ||
- Simple: Print just the running config | ||
- Extended: Print the running config as well as uptime and all version info | ||
- Json: Print entire config object as json`, | ||
RunE: queryConfig, | ||
} | ||
|
||
func init() { | ||
RootCmd.AddCommand(configCmd) | ||
- Json: Print entire config object as json` | ||
} | ||
|
||
func fetchConfig() (Config, error) { | ||
configResponse := alertmanagerStatusResponse{} | ||
u, err := GetAlertmanagerURL() | ||
if err != nil { | ||
return Config{}, err | ||
} | ||
|
||
u.Path = path.Join(u.Path, "/api/v1/status") | ||
u := GetAlertmanagerURL("/api/v1/status") | ||
res, err := http.Get(u.String()) | ||
if err != nil { | ||
return Config{}, err | ||
|
@@ -87,13 +75,13 @@ func fetchConfig() (Config, error) { | |
return configResponse.Data, nil | ||
} | ||
|
||
func queryConfig(cmd *cobra.Command, args []string) error { | ||
func queryConfig(element *kingpin.ParseElement, ctx *kingpin.ParseContext) error { | ||
config, err := fetchConfig() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
formatter, found := format.Formatters[viper.GetString("output")] | ||
formatter, found := format.Formatters[*output] | ||
if !found { | ||
return errors.New("unknown output formatter") | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,14 +4,22 @@ import ( | |
"io" | ||
"time" | ||
|
||
"github.com/alecthomas/kingpin" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. import |
||
"github.com/prometheus/alertmanager/config" | ||
"github.com/prometheus/alertmanager/dispatch" | ||
"github.com/prometheus/alertmanager/types" | ||
"github.com/spf13/viper" | ||
) | ||
|
||
const DefaultDateFormat = "2006-01-02 15:04:05 MST" | ||
|
||
var ( | ||
dateFormat *string | ||
) | ||
|
||
func InitFormatFlags(app *kingpin.Application) { | ||
dateFormat = app.Flag("date.format", "Format of date output").Default(DefaultDateFormat).String() | ||
} | ||
|
||
// Config representation | ||
// Need to get this moved to the prometheus/common/model repo having is duplicated here is smelly | ||
type Config struct { | ||
|
@@ -46,6 +54,5 @@ type Formatter interface { | |
var Formatters = map[string]Formatter{} | ||
|
||
func FormatDate(input time.Time) string { | ||
dateformat := viper.GetString("date.format") | ||
return input.Format(dateformat) | ||
return input.Format(*dateFormat) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should use
gopkg.in/alecthomas/kingpin.v2
, like in prometheus (and as recommended in the repo)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, no :) We need to use the v3-branch, since the pull request I made to kingpin to add support for config files is only present on that branch.