Add timeout support to amtool commands (#1471)

Signed-off-by: stuart nelson <stuartnelson3@gmail.com>
This commit is contained in:
stuart nelson 2018-07-17 09:50:48 +02:00 committed by GitHub
parent 50e271678d
commit bd6100793f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 49 additions and 30 deletions

View File

@ -17,10 +17,11 @@ import (
"context"
"fmt"
"time"
"github.com/prometheus/alertmanager/client"
"github.com/prometheus/client_golang/api"
"gopkg.in/alecthomas/kingpin.v2"
"time"
)
type alertAddCmd struct {
@ -64,10 +65,10 @@ func configureAddAlertCmd(cc *kingpin.CmdClause) {
addCmd.Flag("start", "Set when the alert should start. RFC3339 format 2006-01-02T15:04:05Z07:00").StringVar(&a.start)
addCmd.Flag("end", "Set when the alert should should end. RFC3339 format 2006-01-02T15:04:05Z07:00").StringVar(&a.end)
addCmd.Flag("annotation", "Set an annotation to be included with the alert").StringsVar(&a.annotations)
addCmd.Action(a.addAlert)
addCmd.Action(execWithTimeout(a.addAlert))
}
func (a *alertAddCmd) addAlert(ctx *kingpin.ParseContext) error {
func (a *alertAddCmd) addAlert(ctx context.Context, _ *kingpin.ParseContext) error {
c, err := api.NewClient(api.Config{Address: alertmanagerURL.String()})
if err != nil {
return err
@ -106,7 +107,7 @@ func (a *alertAddCmd) addAlert(ctx *kingpin.ParseContext) error {
}
}
return alertAPI.Push(context.Background(), client.Alert{
return alertAPI.Push(ctx, client.Alert{
Labels: labels,
Annotations: annotations,
StartsAt: startsAt,

View File

@ -72,10 +72,10 @@ func configureQueryAlertsCmd(cc *kingpin.CmdClause) {
queryCmd.Flag("unprocessed", "Show unprocessed alerts").Short('u').BoolVar(&a.unprocessed)
queryCmd.Flag("receiver", "Show alerts matching receiver (Supports regex syntax)").Short('r').StringVar(&a.receiver)
queryCmd.Arg("matcher-groups", "Query filter").StringsVar(&a.matcherGroups)
queryCmd.Action(a.queryAlerts)
queryCmd.Action(execWithTimeout(a.queryAlerts))
}
func (a *alertQueryCmd) queryAlerts(ctx *kingpin.ParseContext) error {
func (a *alertQueryCmd) queryAlerts(ctx context.Context, _ *kingpin.ParseContext) error {
var filterString = ""
if len(a.matcherGroups) == 1 {
// If the parser fails then we likely don't have a (=|=~|!=|!~) so lets
@ -100,7 +100,7 @@ func (a *alertQueryCmd) queryAlerts(ctx *kingpin.ParseContext) error {
if !a.silenced && !a.inhibited && !a.active && !a.unprocessed {
a.active = true
}
fetchedAlerts, err := alertAPI.List(context.Background(), filterString, a.receiver, a.silenced, a.inhibited, a.active, a.unprocessed)
fetchedAlerts, err := alertAPI.List(ctx, filterString, a.receiver, a.silenced, a.inhibited, a.active, a.unprocessed)
if err != nil {
return err
}

View File

@ -34,17 +34,17 @@ The amount of output is controlled by the output selection flag:
// configCmd represents the config command
func configureConfigCmd(app *kingpin.Application) {
app.Command("config", configHelp).Action(queryConfig).PreAction(requireAlertManagerURL)
app.Command("config", configHelp).Action(execWithTimeout(queryConfig)).PreAction(requireAlertManagerURL)
}
func queryConfig(ctx *kingpin.ParseContext) error {
func queryConfig(ctx context.Context, _ *kingpin.ParseContext) error {
c, err := api.NewClient(api.Config{Address: alertmanagerURL.String()})
if err != nil {
return err
}
statusAPI := client.NewStatusAPI(c)
status, err := statusAPI.Get(context.Background())
status, err := statusAPI.Get(ctx)
if err != nil {
return err
}

View File

@ -16,6 +16,7 @@ package cli
import (
"net/url"
"os"
"time"
"github.com/prometheus/common/version"
"gopkg.in/alecthomas/kingpin.v2"
@ -28,6 +29,7 @@ var (
verbose bool
alertmanagerURL *url.URL
output string
timeout time.Duration
configFiles = []string{os.ExpandEnv("$HOME/.config/amtool/config.yml"), "/etc/amtool/config.yml"}
legacyFlags = map[string]string{"comment_required": "require-comment"}
@ -61,6 +63,8 @@ func Execute() {
app.Flag("verbose", "Verbose running information").Short('v').BoolVar(&verbose)
app.Flag("alertmanager.url", "Alertmanager to talk to").URLVar(&alertmanagerURL)
app.Flag("output", "Output formatter (simple, extended, json)").Short('o').Default("simple").EnumVar(&output, "simple", "extended", "json")
app.Flag("timeout", "Timeout for the executed command").Default("30s").DurationVar(&timeout)
app.Version(version.Print("amtool"))
app.GetFlag("help").Short('h')
app.UsageTemplate(kingpin.CompactUsageTemplate)

View File

@ -13,7 +13,9 @@
package cli
import "gopkg.in/alecthomas/kingpin.v2"
import (
"gopkg.in/alecthomas/kingpin.v2"
)
// silenceCmd represents the silence command
func configureSilenceCmd(app *kingpin.Application) {

View File

@ -82,11 +82,11 @@ func configureSilenceAddCmd(cc *kingpin.CmdClause) {
addCmd.Flag("end", "Set when the silence should end (overwrites duration). RFC3339 format 2006-01-02T15:04:05Z07:00").StringVar(&c.end)
addCmd.Flag("comment", "A comment to help describe the silence").Short('c').StringVar(&c.comment)
addCmd.Arg("matcher-groups", "Query filter").StringsVar(&c.matchers)
addCmd.Action(c.add)
addCmd.Action(execWithTimeout(c.add))
}
func (c *silenceAddCmd) add(ctx *kingpin.ParseContext) error {
func (c *silenceAddCmd) add(ctx context.Context, _ *kingpin.ParseContext) error {
var err error
matchers, err := parseMatchers(c.matchers)
@ -152,7 +152,7 @@ func (c *silenceAddCmd) add(ctx *kingpin.ParseContext) error {
return err
}
silenceAPI := client.NewSilenceAPI(apiClient)
silenceID, err := silenceAPI.Set(context.Background(), silence)
silenceID, err := silenceAPI.Set(ctx, silence)
if err != nil {
return err
}

View File

@ -33,10 +33,10 @@ func configureSilenceExpireCmd(cc *kingpin.CmdClause) {
expireCmd = cc.Command("expire", "expire an alertmanager silence")
)
expireCmd.Arg("silence-ids", "Ids of silences to expire").StringsVar(&c.ids)
expireCmd.Action(c.expire)
expireCmd.Action(execWithTimeout(c.expire))
}
func (c *silenceExpireCmd) expire(ctx *kingpin.ParseContext) error {
func (c *silenceExpireCmd) expire(ctx context.Context, _ *kingpin.ParseContext) error {
if len(c.ids) < 1 {
return errors.New("no silence IDs specified")
}
@ -48,7 +48,7 @@ func (c *silenceExpireCmd) expire(ctx *kingpin.ParseContext) error {
silenceAPI := client.NewSilenceAPI(apiClient)
for _, id := range c.ids {
err := silenceAPI.Expire(context.Background(), id)
err := silenceAPI.Expire(ctx, id)
if err != nil {
return err
}

View File

@ -56,17 +56,17 @@ func configureSilenceImportCmd(cc *kingpin.CmdClause) {
importCmd.Flag("force", "Force adding new silences even if it already exists").Short('f').BoolVar(&c.force)
importCmd.Flag("worker", "Number of concurrent workers to use for import").Short('w').Default("8").IntVar(&c.workers)
importCmd.Arg("input-file", "JSON file with silences").ExistingFileVar(&c.file)
importCmd.Action(c.bulkImport)
importCmd.Action(execWithTimeout(c.bulkImport))
}
func addSilenceWorker(sclient client.SilenceAPI, silencec <-chan *types.Silence, errc chan<- error) {
func addSilenceWorker(ctx context.Context, sclient client.SilenceAPI, silencec <-chan *types.Silence, errc chan<- error) {
for s := range silencec {
silenceID, err := sclient.Set(context.Background(), *s)
silenceID, err := sclient.Set(ctx, *s)
sid := s.ID
if err != nil && strings.Contains(err.Error(), "not found") {
// silence doesn't exists yet, retry to create as a new one
s.ID = ""
silenceID, err = sclient.Set(context.Background(), *s)
silenceID, err = sclient.Set(ctx, *s)
}
if err != nil {
@ -78,7 +78,7 @@ func addSilenceWorker(sclient client.SilenceAPI, silencec <-chan *types.Silence,
}
}
func (c *silenceImportCmd) bulkImport(ctx *kingpin.ParseContext) error {
func (c *silenceImportCmd) bulkImport(ctx context.Context, _ *kingpin.ParseContext) error {
input := os.Stdin
var err error
if c.file != "" {
@ -107,7 +107,7 @@ func (c *silenceImportCmd) bulkImport(ctx *kingpin.ParseContext) error {
for w := 0; w < c.workers; w++ {
wg.Add(1)
go func() {
addSilenceWorker(silenceAPI, silencec, errc)
addSilenceWorker(ctx, silenceAPI, silencec, errc)
wg.Done()
}()
}

View File

@ -88,10 +88,10 @@ func configureSilenceQueryCmd(cc *kingpin.CmdClause) {
queryCmd.Flag("quiet", "Only show silence ids").Short('q').BoolVar(&c.quiet)
queryCmd.Arg("matcher-groups", "Query filter").StringsVar(&c.matchers)
queryCmd.Flag("within", "Show silences that will expire or have expired within a duration").DurationVar(&c.within)
queryCmd.Action(c.query)
queryCmd.Action(execWithTimeout(c.query))
}
func (c *silenceQueryCmd) query(ctx *kingpin.ParseContext) error {
func (c *silenceQueryCmd) query(ctx context.Context, _ *kingpin.ParseContext) error {
var filterString = ""
if len(c.matchers) == 1 {
// If the parser fails then we likely don't have a (=|=~|!=|!~) so lets
@ -112,7 +112,7 @@ func (c *silenceQueryCmd) query(ctx *kingpin.ParseContext) error {
return err
}
silenceAPI := client.NewSilenceAPI(apiClient)
fetchedSilences, err := silenceAPI.List(context.Background(), filterString)
fetchedSilences, err := silenceAPI.List(ctx, filterString)
if err != nil {
return err
}

View File

@ -49,10 +49,10 @@ func configureSilenceUpdateCmd(cc *kingpin.CmdClause) {
updateCmd.Flag("comment", "A comment to help describe the silence").Short('c').StringVar(&c.comment)
updateCmd.Arg("update-ids", "Silence IDs to update").StringsVar(&c.ids)
updateCmd.Action(c.update)
updateCmd.Action(execWithTimeout(c.update))
}
func (c *silenceUpdateCmd) update(ctx *kingpin.ParseContext) error {
func (c *silenceUpdateCmd) update(ctx context.Context, _ *kingpin.ParseContext) error {
if len(c.ids) < 1 {
return fmt.Errorf("no silence IDs specified")
}
@ -65,7 +65,7 @@ func (c *silenceUpdateCmd) update(ctx *kingpin.ParseContext) error {
var updatedSilences []types.Silence
for _, silenceID := range c.ids {
silence, err := silenceAPI.Get(context.Background(), silenceID)
silence, err := silenceAPI.Get(ctx, silenceID)
if err != nil {
return err
}
@ -100,7 +100,7 @@ func (c *silenceUpdateCmd) update(ctx *kingpin.ParseContext) error {
silence.Comment = c.comment
}
newID, err := silenceAPI.Set(context.Background(), *silence)
newID, err := silenceAPI.Set(ctx, *silence)
if err != nil {
return err
}

View File

@ -14,12 +14,15 @@
package cli
import (
"context"
"errors"
"fmt"
"net/url"
"path"
"github.com/prometheus/alertmanager/client"
kingpin "gopkg.in/alecthomas/kingpin.v2"
"github.com/prometheus/alertmanager/pkg/parse"
"github.com/prometheus/alertmanager/types"
"github.com/prometheus/common/model"
@ -114,3 +117,12 @@ func TypeMatcher(matcher labels.Matcher) (types.Matcher, error) {
}
return *typeMatcher, nil
}
// Helper function for adding the ctx with timeout into an action.
func execWithTimeout(fn func(context.Context, *kingpin.ParseContext) error) func(*kingpin.ParseContext) error {
return func(x *kingpin.ParseContext) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
return fn(ctx, x)
}
}