From 74fa2236f75c41f849205d5d44c0f30548b7e479 Mon Sep 17 00:00:00 2001 From: Paul Gier Date: Wed, 13 Mar 2019 11:01:08 -0500 Subject: [PATCH] cli: update amtool to use apiv2 Includes godoc improvements Signed-off-by: Paul Gier --- api/v2/api.go | 2 +- cli/alert_add.go | 35 ++++++++++-------- cli/alert_query.go | 30 ++++++++------- cli/config.go | 2 +- cli/format/format.go | 16 +++++--- cli/format/format_extended.go | 57 +++++++++++++++-------------- cli/format/format_json.go | 9 ++--- cli/format/format_simple.go | 32 ++++++++-------- cli/format/sort.go | 25 ++++++++----- cli/root.go | 24 +++++++++++- cli/routing.go | 8 ++-- cli/silence_add.go | 38 ++++++++++--------- cli/silence_expire.go | 17 ++++----- cli/silence_import.go | 34 ++++++++--------- cli/silence_query.go | 36 +++++++++--------- cli/silence_update.go | 52 ++++++++++++++++---------- cli/test_routing.go | 8 ++-- cli/test_routing_test.go | 14 +++---- cli/utils.go | 69 ++++++++++++++++++++--------------- 19 files changed, 283 insertions(+), 225 deletions(-) diff --git a/api/v2/api.go b/api/v2/api.go index 42370e46..aae4f26a 100644 --- a/api/v2/api.go +++ b/api/v2/api.go @@ -529,7 +529,7 @@ var ( // then by end time or start time depending on the state. // active silences should show the next to expire first // pending silences are ordered based on which one starts next -// expired are ordered base on which one expired most recently +// expired are ordered based on which one expired most recently func sortSilences(sils open_api_models.GettableSilences) { sort.Slice(sils, func(i, j int) bool { state1 := types.SilenceState(*sils[i].Status.State) diff --git a/cli/alert_add.go b/cli/alert_add.go index 52ac4b6d..7ef874f9 100644 --- a/cli/alert_add.go +++ b/cli/alert_add.go @@ -19,9 +19,10 @@ import ( "fmt" "time" - "github.com/prometheus/alertmanager/client" - "github.com/prometheus/client_golang/api" - "gopkg.in/alecthomas/kingpin.v2" + "github.com/go-openapi/strfmt" + "github.com/prometheus/alertmanager/api/v2/client/alert" + "github.com/prometheus/alertmanager/api/v2/models" + kingpin "gopkg.in/alecthomas/kingpin.v2" ) type alertAddCmd struct { @@ -69,11 +70,6 @@ func configureAddAlertCmd(cc *kingpin.CmdClause) { } func (a *alertAddCmd) addAlert(ctx context.Context, _ *kingpin.ParseContext) error { - c, err := api.NewClient(api.Config{Address: alertmanagerURL.String()}) - if err != nil { - return err - } - alertAPI := client.NewAlertAPI(c) if len(a.labels) > 0 { // Allow the alertname label to be defined implicitly as the first argument rather @@ -107,11 +103,20 @@ func (a *alertAddCmd) addAlert(ctx context.Context, _ *kingpin.ParseContext) err } } - return alertAPI.Push(ctx, client.Alert{ - Labels: labels, - Annotations: annotations, - StartsAt: startsAt, - EndsAt: endsAt, - GeneratorURL: a.generatorURL, - }) + pa := &models.PostableAlert{ + Alert: models.Alert{ + GeneratorURL: strfmt.URI(a.generatorURL), + Labels: labels, + }, + Annotations: annotations, + StartsAt: strfmt.DateTime(startsAt), + EndsAt: strfmt.DateTime(endsAt), + } + alertParams := alert.NewPostAlertsParams().WithContext(ctx). + WithAlerts(models.PostableAlerts{pa}) + + amclient := NewAlertmanagerClient(alertmanagerURL) + + _, err = amclient.Alert.PostAlerts(alertParams) + return err } diff --git a/cli/alert_query.go b/cli/alert_query.go index 50c61039..03dd026b 100644 --- a/cli/alert_query.go +++ b/cli/alert_query.go @@ -17,13 +17,11 @@ import ( "context" "errors" "fmt" - "strings" - "github.com/prometheus/client_golang/api" - "gopkg.in/alecthomas/kingpin.v2" + kingpin "gopkg.in/alecthomas/kingpin.v2" + "github.com/prometheus/alertmanager/api/v2/client/alert" "github.com/prometheus/alertmanager/cli/format" - "github.com/prometheus/alertmanager/client" "github.com/prometheus/alertmanager/pkg/parse" ) @@ -76,7 +74,7 @@ func configureQueryAlertsCmd(cc *kingpin.CmdClause) { } func (a *alertQueryCmd) queryAlerts(ctx context.Context, _ *kingpin.ParseContext) error { - var filterString = "" + filter := []string{} if len(a.matcherGroups) > 0 { // Attempt to parse the first argument. If the parser fails // then we likely don't have a (=|=~|!=|!~) so lets assume that @@ -87,19 +85,25 @@ func (a *alertQueryCmd) queryAlerts(ctx context.Context, _ *kingpin.ParseContext if err != nil { a.matcherGroups[0] = fmt.Sprintf("alertname=%s", m) } - filterString = fmt.Sprintf("{%s}", strings.Join(a.matcherGroups, ",")) + filter = append(filter, a.matcherGroups[0]) } - c, err := api.NewClient(api.Config{Address: alertmanagerURL.String()}) - if err != nil { - return err - } - alertAPI := client.NewAlertAPI(c) // If no selector was passed, default to showing active alerts. if !a.silenced && !a.inhibited && !a.active && !a.unprocessed { a.active = true } - fetchedAlerts, err := alertAPI.List(ctx, filterString, a.receiver, a.silenced, a.inhibited, a.active, a.unprocessed) + + alertParams := alert.NewGetAlertsParams().WithContext(ctx) + alertParams.Active = &a.active + alertParams.Inhibited = &a.inhibited + alertParams.Silenced = &a.silenced + alertParams.Unprocessed = &a.unprocessed + alertParams.Filter = filter + + amclient := NewAlertmanagerClient(alertmanagerURL) + + getOk, err := amclient.Alert.GetAlerts(alertParams) + if err != nil { return err } @@ -108,5 +112,5 @@ func (a *alertQueryCmd) queryAlerts(ctx context.Context, _ *kingpin.ParseContext if !found { return errors.New("unknown output formatter") } - return formatter.FormatAlerts(fetchedAlerts) + return formatter.FormatAlerts(getOk.Payload) } diff --git a/cli/config.go b/cli/config.go index 81381ce2..036f3aff 100644 --- a/cli/config.go +++ b/cli/config.go @@ -17,7 +17,7 @@ import ( "context" "errors" - "gopkg.in/alecthomas/kingpin.v2" + kingpin "gopkg.in/alecthomas/kingpin.v2" "github.com/prometheus/alertmanager/cli/format" ) diff --git a/cli/format/format.go b/cli/format/format.go index 471d0202..28a3ba85 100644 --- a/cli/format/format.go +++ b/cli/format/format.go @@ -17,10 +17,10 @@ import ( "io" "time" - "gopkg.in/alecthomas/kingpin.v2" + kingpin "gopkg.in/alecthomas/kingpin.v2" - "github.com/prometheus/alertmanager/client" - "github.com/prometheus/alertmanager/types" + "github.com/go-openapi/strfmt" + "github.com/prometheus/alertmanager/api/v2/models" ) const DefaultDateFormat = "2006-01-02 15:04:05 MST" @@ -36,14 +36,18 @@ func InitFormatFlags(app *kingpin.Application) { // Formatter needs to be implemented for each new output formatter. type Formatter interface { SetOutput(io.Writer) - FormatSilences([]types.Silence) error - FormatAlerts([]*client.ExtendedAlert) error - FormatConfig(*client.ServerStatus) error + FormatSilences([]models.GettableSilence) error + FormatAlerts([]*models.GettableAlert) error + FormatConfig(*models.AlertmanagerStatus) error } // Formatters is a map of cli argument names to formatter interface object. var Formatters = map[string]Formatter{} +func FormatDateTime(input strfmt.DateTime) string { + return FormatDate(time.Time(input)) +} + func FormatDate(input time.Time) string { return input.Format(*dateFormat) } diff --git a/cli/format/format_extended.go b/cli/format/format_extended.go index 924c83f1..08848e92 100644 --- a/cli/format/format_extended.go +++ b/cli/format/format_extended.go @@ -20,9 +20,9 @@ import ( "sort" "strings" "text/tabwriter" + "time" - "github.com/prometheus/alertmanager/client" - "github.com/prometheus/alertmanager/types" + "github.com/prometheus/alertmanager/api/v2/models" ) type ExtendedFormatter struct { @@ -37,7 +37,8 @@ func (formatter *ExtendedFormatter) SetOutput(writer io.Writer) { formatter.writer = writer } -func (formatter *ExtendedFormatter) FormatSilences(silences []types.Silence) error { +// FormatSilences formats the silences into a readable string +func (formatter *ExtendedFormatter) FormatSilences(silences []models.GettableSilence) error { w := tabwriter.NewWriter(formatter.writer, 0, 0, 2, ' ', 0) sort.Sort(ByEndAt(silences)) fmt.Fprintln(w, "ID\tMatchers\tStarts At\tEnds At\tUpdated At\tCreated By\tComment\t") @@ -45,19 +46,20 @@ func (formatter *ExtendedFormatter) FormatSilences(silences []types.Silence) err fmt.Fprintf( w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t\n", - silence.ID, + *silence.ID, extendedFormatMatchers(silence.Matchers), - FormatDate(silence.StartsAt), - FormatDate(silence.EndsAt), - FormatDate(silence.UpdatedAt), - silence.CreatedBy, - silence.Comment, + FormatDateTime(*silence.Silence.StartsAt), + FormatDateTime(*silence.Silence.EndsAt), + FormatDateTime(*silence.UpdatedAt), + *silence.CreatedBy, + *silence.Comment, ) } return w.Flush() } -func (formatter *ExtendedFormatter) FormatAlerts(alerts []*client.ExtendedAlert) error { +// FormatAlerts formats the alerts into a readable string +func (formatter *ExtendedFormatter) FormatAlerts(alerts []*models.GettableAlert) error { w := tabwriter.NewWriter(formatter.writer, 0, 0, 2, ' ', 0) sort.Sort(ByStartsAt(alerts)) fmt.Fprintln(w, "Labels\tAnnotations\tStarts At\tEnds At\tGenerator URL\t") @@ -67,27 +69,28 @@ func (formatter *ExtendedFormatter) FormatAlerts(alerts []*client.ExtendedAlert) "%s\t%s\t%s\t%s\t%s\t\n", extendedFormatLabels(alert.Labels), extendedFormatAnnotations(alert.Annotations), - FormatDate(alert.StartsAt), - FormatDate(alert.EndsAt), + FormatDate(time.Time(*alert.StartsAt)), + FormatDate(time.Time(*alert.EndsAt)), alert.GeneratorURL, ) } return w.Flush() } -func (formatter *ExtendedFormatter) FormatConfig(status *client.ServerStatus) error { - fmt.Fprintln(formatter.writer, status.ConfigYAML) - fmt.Fprintln(formatter.writer, "buildUser", status.VersionInfo["buildUser"]) - fmt.Fprintln(formatter.writer, "goVersion", status.VersionInfo["goVersion"]) - fmt.Fprintln(formatter.writer, "revision", status.VersionInfo["revision"]) - fmt.Fprintln(formatter.writer, "version", status.VersionInfo["version"]) - fmt.Fprintln(formatter.writer, "branch", status.VersionInfo["branch"]) - fmt.Fprintln(formatter.writer, "buildDate", status.VersionInfo["buildDate"]) +// FormatConfig formats the alertmanager status information into a readable string +func (formatter *ExtendedFormatter) FormatConfig(status *models.AlertmanagerStatus) error { + fmt.Fprintln(formatter.writer, status.Config.Original) + fmt.Fprintln(formatter.writer, "buildUser", status.VersionInfo.BuildUser) + fmt.Fprintln(formatter.writer, "goVersion", status.VersionInfo.GoVersion) + fmt.Fprintln(formatter.writer, "revision", status.VersionInfo.Revision) + fmt.Fprintln(formatter.writer, "version", status.VersionInfo.Version) + fmt.Fprintln(formatter.writer, "branch", status.VersionInfo.Branch) + fmt.Fprintln(formatter.writer, "buildDate", status.VersionInfo.BuildDate) fmt.Fprintln(formatter.writer, "uptime", status.Uptime) return nil } -func extendedFormatLabels(labels client.LabelSet) string { +func extendedFormatLabels(labels models.LabelSet) string { output := []string{} for name, value := range labels { output = append(output, fmt.Sprintf("%s=\"%s\"", name, value)) @@ -96,7 +99,7 @@ func extendedFormatLabels(labels client.LabelSet) string { return strings.Join(output, " ") } -func extendedFormatAnnotations(labels client.LabelSet) string { +func extendedFormatAnnotations(labels models.LabelSet) string { output := []string{} for name, value := range labels { output = append(output, fmt.Sprintf("%s=\"%s\"", name, value)) @@ -105,7 +108,7 @@ func extendedFormatAnnotations(labels client.LabelSet) string { return strings.Join(output, " ") } -func extendedFormatMatchers(matchers types.Matchers) string { +func extendedFormatMatchers(matchers models.Matchers) string { output := []string{} for _, matcher := range matchers { output = append(output, extendedFormatMatcher(*matcher)) @@ -113,9 +116,9 @@ func extendedFormatMatchers(matchers types.Matchers) string { return strings.Join(output, " ") } -func extendedFormatMatcher(matcher types.Matcher) string { - if matcher.IsRegex { - return fmt.Sprintf("%s~=%s", matcher.Name, matcher.Value) +func extendedFormatMatcher(matcher models.Matcher) string { + if *matcher.IsRegex { + return fmt.Sprintf("%s~=%s", *matcher.Name, *matcher.Value) } - return fmt.Sprintf("%s=%s", matcher.Name, matcher.Value) + return fmt.Sprintf("%s=%s", *matcher.Name, *matcher.Value) } diff --git a/cli/format/format_json.go b/cli/format/format_json.go index 0a2cd502..94f261d7 100644 --- a/cli/format/format_json.go +++ b/cli/format/format_json.go @@ -18,8 +18,7 @@ import ( "io" "os" - "github.com/prometheus/alertmanager/client" - "github.com/prometheus/alertmanager/types" + "github.com/prometheus/alertmanager/api/v2/models" ) type JSONFormatter struct { @@ -34,17 +33,17 @@ func (formatter *JSONFormatter) SetOutput(writer io.Writer) { formatter.writer = writer } -func (formatter *JSONFormatter) FormatSilences(silences []types.Silence) error { +func (formatter *JSONFormatter) FormatSilences(silences []models.GettableSilence) error { enc := json.NewEncoder(formatter.writer) return enc.Encode(silences) } -func (formatter *JSONFormatter) FormatAlerts(alerts []*client.ExtendedAlert) error { +func (formatter *JSONFormatter) FormatAlerts(alerts []*models.GettableAlert) error { enc := json.NewEncoder(formatter.writer) return enc.Encode(alerts) } -func (formatter *JSONFormatter) FormatConfig(status *client.ServerStatus) error { +func (formatter *JSONFormatter) FormatConfig(status *models.AlertmanagerStatus) error { enc := json.NewEncoder(formatter.writer) return enc.Encode(status) } diff --git a/cli/format/format_simple.go b/cli/format/format_simple.go index daec02b5..5006e182 100644 --- a/cli/format/format_simple.go +++ b/cli/format/format_simple.go @@ -20,9 +20,9 @@ import ( "sort" "strings" "text/tabwriter" + "time" - "github.com/prometheus/alertmanager/client" - "github.com/prometheus/alertmanager/types" + "github.com/prometheus/alertmanager/api/v2/models" ) type SimpleFormatter struct { @@ -37,7 +37,7 @@ func (formatter *SimpleFormatter) SetOutput(writer io.Writer) { formatter.writer = writer } -func (formatter *SimpleFormatter) FormatSilences(silences []types.Silence) error { +func (formatter *SimpleFormatter) FormatSilences(silences []models.GettableSilence) error { w := tabwriter.NewWriter(formatter.writer, 0, 0, 2, ' ', 0) sort.Sort(ByEndAt(silences)) fmt.Fprintln(w, "ID\tMatchers\tEnds At\tCreated By\tComment\t") @@ -45,17 +45,17 @@ func (formatter *SimpleFormatter) FormatSilences(silences []types.Silence) error fmt.Fprintf( w, "%s\t%s\t%s\t%s\t%s\t\n", - silence.ID, + *silence.ID, simpleFormatMatchers(silence.Matchers), - FormatDate(silence.EndsAt), - silence.CreatedBy, - silence.Comment, + FormatDateTime(*silence.EndsAt), + *silence.CreatedBy, + *silence.Comment, ) } return w.Flush() } -func (formatter *SimpleFormatter) FormatAlerts(alerts []*client.ExtendedAlert) error { +func (formatter *SimpleFormatter) FormatAlerts(alerts []*models.GettableAlert) error { w := tabwriter.NewWriter(formatter.writer, 0, 0, 2, ' ', 0) sort.Sort(ByStartsAt(alerts)) fmt.Fprintln(w, "Alertname\tStarts At\tSummary\t") @@ -64,19 +64,19 @@ func (formatter *SimpleFormatter) FormatAlerts(alerts []*client.ExtendedAlert) e w, "%s\t%s\t%s\t\n", alert.Labels["alertname"], - FormatDate(alert.StartsAt), + FormatDate(time.Time(*alert.StartsAt)), alert.Annotations["summary"], ) } return w.Flush() } -func (formatter *SimpleFormatter) FormatConfig(status *client.ServerStatus) error { - fmt.Fprintln(formatter.writer, status.ConfigYAML) +func (formatter *SimpleFormatter) FormatConfig(status *models.AlertmanagerStatus) error { + fmt.Fprintln(formatter.writer, *status) return nil } -func simpleFormatMatchers(matchers types.Matchers) string { +func simpleFormatMatchers(matchers models.Matchers) string { output := []string{} for _, matcher := range matchers { output = append(output, simpleFormatMatcher(*matcher)) @@ -84,9 +84,9 @@ func simpleFormatMatchers(matchers types.Matchers) string { return strings.Join(output, " ") } -func simpleFormatMatcher(matcher types.Matcher) string { - if matcher.IsRegex { - return fmt.Sprintf("%s=~%s", matcher.Name, matcher.Value) +func simpleFormatMatcher(matcher models.Matcher) string { + if *matcher.IsRegex { + return fmt.Sprintf("%s=~%s", *matcher.Name, *matcher.Value) } - return fmt.Sprintf("%s=%s", matcher.Name, matcher.Value) + return fmt.Sprintf("%s=%s", *matcher.Name, *matcher.Value) } diff --git a/cli/format/sort.go b/cli/format/sort.go index 7a34607d..0719c9dc 100644 --- a/cli/format/sort.go +++ b/cli/format/sort.go @@ -14,18 +14,23 @@ package format import ( - "github.com/prometheus/alertmanager/client" - "github.com/prometheus/alertmanager/types" + "time" + + "github.com/prometheus/alertmanager/api/v2/models" ) -type ByEndAt []types.Silence +type ByEndAt []models.GettableSilence -func (s ByEndAt) Len() int { return len(s) } -func (s ByEndAt) Swap(i, j int) { s[i], s[j] = s[j], s[i] } -func (s ByEndAt) Less(i, j int) bool { return s[i].EndsAt.Before(s[j].EndsAt) } +func (s ByEndAt) Len() int { return len(s) } +func (s ByEndAt) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s ByEndAt) Less(i, j int) bool { + return time.Time(*s[i].Silence.EndsAt).Before(time.Time(*s[j].Silence.EndsAt)) +} -type ByStartsAt []*client.ExtendedAlert +type ByStartsAt []*models.GettableAlert -func (s ByStartsAt) Len() int { return len(s) } -func (s ByStartsAt) Swap(i, j int) { s[i], s[j] = s[j], s[i] } -func (s ByStartsAt) Less(i, j int) bool { return s[i].StartsAt.Before(s[j].StartsAt) } +func (s ByStartsAt) Len() int { return len(s) } +func (s ByStartsAt) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s ByStartsAt) Less(i, j int) bool { + return time.Time(*s[i].StartsAt).Before(time.Time(*s[j].StartsAt)) +} diff --git a/cli/root.go b/cli/root.go index 3a234701..8c0020f2 100644 --- a/cli/root.go +++ b/cli/root.go @@ -18,9 +18,11 @@ import ( "os" "time" + "github.com/go-openapi/strfmt" "github.com/prometheus/common/version" - "gopkg.in/alecthomas/kingpin.v2" + kingpin "gopkg.in/alecthomas/kingpin.v2" + "github.com/prometheus/alertmanager/api/v2/client" "github.com/prometheus/alertmanager/cli/config" "github.com/prometheus/alertmanager/cli/format" ) @@ -53,6 +55,26 @@ func requireAlertManagerURL(pc *kingpin.ParseContext) error { return nil } +const ( + defaultAmHost = "localhost" + defaultAmPort = "9093" + defaultAmApiv2path = "/api/v2" +) + +// NewAlertmanagerClient initializes an alertmanager client with the given URL +func NewAlertmanagerClient(amURL *url.URL) *client.Alertmanager { + transportConfig := client.DefaultTransportConfig() + transportConfig.BasePath = defaultAmApiv2path + + if amURL.Host == "" { + transportConfig.Host = defaultAmHost + ":" + defaultAmPort + } else { + transportConfig.Host = amURL.Host + } + return client.NewHTTPClientWithConfig(strfmt.Default, transportConfig) +} + +// Execute is the main function for the amtool command func Execute() { var ( app = kingpin.New("amtool", helpRoot).DefaultEnvars() diff --git a/cli/routing.go b/cli/routing.go index 9d9556b3..30e7cfce 100644 --- a/cli/routing.go +++ b/cli/routing.go @@ -20,9 +20,9 @@ import ( "github.com/xlab/treeprint" - "github.com/prometheus/alertmanager/client" + "github.com/prometheus/alertmanager/api/v2/models" "github.com/prometheus/alertmanager/dispatch" - "gopkg.in/alecthomas/kingpin.v2" + kingpin "gopkg.in/alecthomas/kingpin.v2" ) type routingShow struct { @@ -61,7 +61,7 @@ func configureRoutingCmd(app *kingpin.CmdClause) { } func (c *routingShow) routingShowAction(ctx context.Context, _ *kingpin.ParseContext) error { - // Load configuration form file or URL. + // Load configuration from file or URL. cfg, err := loadAlertmanagerConfig(ctx, alertmanagerURL, c.configFile) if err != nil { kingpin.Fatalf("%s", err) @@ -101,7 +101,7 @@ func convertRouteToTree(route *dispatch.Route, tree treeprint.Tree) { } } -func getMatchingTree(route *dispatch.Route, tree treeprint.Tree, lset client.LabelSet) { +func getMatchingTree(route *dispatch.Route, tree treeprint.Tree, lset models.LabelSet) { final := true branch := tree.AddBranch(getRouteTreeSlug(route, false, false)) for _, r := range route.Routes { diff --git a/cli/silence_add.go b/cli/silence_add.go index bc66cbc5..e45c3e56 100644 --- a/cli/silence_add.go +++ b/cli/silence_add.go @@ -20,12 +20,12 @@ import ( "os/user" "time" - "github.com/prometheus/client_golang/api" + "github.com/go-openapi/strfmt" "github.com/prometheus/common/model" - "gopkg.in/alecthomas/kingpin.v2" + kingpin "gopkg.in/alecthomas/kingpin.v2" - "github.com/prometheus/alertmanager/client" - "github.com/prometheus/alertmanager/types" + "github.com/prometheus/alertmanager/api/v2/client/silence" + "github.com/prometheus/alertmanager/api/v2/models" ) func username() string { @@ -149,24 +149,26 @@ func (c *silenceAddCmd) add(ctx context.Context, _ *kingpin.ParseContext) error return err } - silence := types.Silence{ - Matchers: typeMatchers, - StartsAt: startsAt, - EndsAt: endsAt, - CreatedBy: c.author, - Comment: c.comment, + start := strfmt.DateTime(startsAt) + end := strfmt.DateTime(endsAt) + ps := &models.PostableSilence{ + Silence: models.Silence{ + Matchers: typeMatchers, + StartsAt: &start, + EndsAt: &end, + CreatedBy: &c.author, + Comment: &c.comment, + }, } + silenceParams := silence.NewPostSilencesParams().WithContext(ctx). + WithSilence(ps) - apiClient, err := api.NewClient(api.Config{Address: alertmanagerURL.String()}) + amclient := NewAlertmanagerClient(alertmanagerURL) + + postOk, err := amclient.Silence.PostSilences(silenceParams) if err != nil { return err } - silenceAPI := client.NewSilenceAPI(apiClient) - silenceID, err := silenceAPI.Set(ctx, silence) - if err != nil { - return err - } - - _, err = fmt.Println(silenceID) + fmt.Println(*postOk.Payload) return err } diff --git a/cli/silence_expire.go b/cli/silence_expire.go index 19dfcd61..5bd65c9f 100644 --- a/cli/silence_expire.go +++ b/cli/silence_expire.go @@ -17,10 +17,9 @@ import ( "context" "errors" - "github.com/prometheus/client_golang/api" - "gopkg.in/alecthomas/kingpin.v2" - - "github.com/prometheus/alertmanager/client" + "github.com/go-openapi/strfmt" + "github.com/prometheus/alertmanager/api/v2/client/silence" + kingpin "gopkg.in/alecthomas/kingpin.v2" ) type silenceExpireCmd struct { @@ -41,14 +40,12 @@ func (c *silenceExpireCmd) expire(ctx context.Context, _ *kingpin.ParseContext) return errors.New("no silence IDs specified") } - apiClient, err := api.NewClient(api.Config{Address: alertmanagerURL.String()}) - if err != nil { - return err - } - silenceAPI := client.NewSilenceAPI(apiClient) + amclient := NewAlertmanagerClient(alertmanagerURL) for _, id := range c.ids { - err := silenceAPI.Expire(ctx, id) + params := silence.NewDeleteSilenceParams().WithContext(ctx) + params.SilenceID = strfmt.UUID(id) + _, err := amclient.Silence.DeleteSilence(params) if err != nil { return err } diff --git a/cli/silence_import.go b/cli/silence_import.go index 68610a98..92452ad1 100644 --- a/cli/silence_import.go +++ b/cli/silence_import.go @@ -22,11 +22,10 @@ import ( "sync" "github.com/pkg/errors" - "github.com/prometheus/client_golang/api" - "gopkg.in/alecthomas/kingpin.v2" + kingpin "gopkg.in/alecthomas/kingpin.v2" - "github.com/prometheus/alertmanager/client" - "github.com/prometheus/alertmanager/types" + "github.com/prometheus/alertmanager/api/v2/client/silence" + "github.com/prometheus/alertmanager/api/v2/models" ) type silenceImportCmd struct { @@ -59,20 +58,21 @@ func configureSilenceImportCmd(cc *kingpin.CmdClause) { importCmd.Action(execWithTimeout(c.bulkImport)) } -func addSilenceWorker(ctx context.Context, sclient client.SilenceAPI, silencec <-chan *types.Silence, errc chan<- error) { +func addSilenceWorker(ctx context.Context, sclient *silence.Client, silencec <-chan *models.PostableSilence, errc chan<- error) { for s := range silencec { - silenceID, err := sclient.Set(ctx, *s) - sid := s.ID + params := silence.NewPostSilencesParams().WithContext(ctx) + params.Silence = s + postOk, err := sclient.PostSilences(params) 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(ctx, *s) + params.Silence.ID = "" + postOk, _ = sclient.PostSilences(params) } if err != nil { - fmt.Fprintf(os.Stderr, "Error adding silence id='%v': %v\n", sid, err) + fmt.Fprintf(os.Stderr, "Error adding silence id='%v': %v\n", s.ID, err) } else { - fmt.Println(silenceID) + fmt.Println(postOk.Payload.SilenceID) } errc <- err } @@ -96,18 +96,14 @@ func (c *silenceImportCmd) bulkImport(ctx context.Context, _ *kingpin.ParseConte return errors.Wrap(err, "couldn't unmarshal input data, is it JSON?") } - apiClient, err := api.NewClient(api.Config{Address: alertmanagerURL.String()}) - if err != nil { - return err - } - silenceAPI := client.NewSilenceAPI(apiClient) - silencec := make(chan *types.Silence, 100) + amclient := NewAlertmanagerClient(alertmanagerURL) + silencec := make(chan *models.PostableSilence, 100) errc := make(chan error, 100) var wg sync.WaitGroup for w := 0; w < c.workers; w++ { wg.Add(1) go func() { - addSilenceWorker(ctx, silenceAPI, silencec, errc) + addSilenceWorker(ctx, amclient.Silence, silencec, errc) wg.Done() }() } @@ -123,7 +119,7 @@ func (c *silenceImportCmd) bulkImport(ctx context.Context, _ *kingpin.ParseConte count := 0 for dec.More() { - var s types.Silence + var s models.PostableSilence err := dec.Decode(&s) if err != nil { return errors.Wrap(err, "couldn't unmarshal input data, is it JSON?") diff --git a/cli/silence_query.go b/cli/silence_query.go index 3d870083..2bc5fe01 100644 --- a/cli/silence_query.go +++ b/cli/silence_query.go @@ -17,16 +17,14 @@ import ( "context" "errors" "fmt" - "strings" "time" - "github.com/prometheus/client_golang/api" - "gopkg.in/alecthomas/kingpin.v2" + kingpin "gopkg.in/alecthomas/kingpin.v2" + "github.com/prometheus/alertmanager/api/v2/client/silence" + "github.com/prometheus/alertmanager/api/v2/models" "github.com/prometheus/alertmanager/cli/format" - "github.com/prometheus/alertmanager/client" "github.com/prometheus/alertmanager/pkg/parse" - "github.com/prometheus/alertmanager/types" ) type silenceQueryCmd struct { @@ -92,7 +90,7 @@ func configureSilenceQueryCmd(cc *kingpin.CmdClause) { } func (c *silenceQueryCmd) query(ctx context.Context, _ *kingpin.ParseContext) error { - var filterString = "" + filter := []string{} if len(c.matchers) > 0 { // If the parser fails then we likely don't have a (=|=~|!=|!~) so lets // assume that the user wants alertname= and prepend `alertname=` @@ -101,35 +99,35 @@ func (c *silenceQueryCmd) query(ctx context.Context, _ *kingpin.ParseContext) er if err != nil { c.matchers[0] = fmt.Sprintf("alertname=%s", c.matchers[0]) } - filterString = fmt.Sprintf("{%s}", strings.Join(c.matchers, ",")) + filter = append(filter, c.matchers[0]) } - apiClient, err := api.NewClient(api.Config{Address: alertmanagerURL.String()}) - if err != nil { - return err - } - silenceAPI := client.NewSilenceAPI(apiClient) - fetchedSilences, err := silenceAPI.List(ctx, filterString) + silenceParams := silence.NewGetSilencesParams().WithContext(ctx) + silenceParams.Filter = filter + + amclient := NewAlertmanagerClient(alertmanagerURL) + + getOk, err := amclient.Silence.GetSilences(silenceParams) if err != nil { return err } - displaySilences := []types.Silence{} - for _, silence := range fetchedSilences { + displaySilences := []models.GettableSilence{} + for _, silence := range getOk.Payload { // skip expired silences if --expired is not set - if !c.expired && silence.EndsAt.Before(time.Now()) { + if !c.expired && time.Time(*silence.EndsAt).Before(time.Now()) { continue } // skip active silences if --expired is set - if c.expired && silence.EndsAt.After(time.Now()) { + if c.expired && time.Time(*silence.EndsAt).After(time.Now()) { continue } // skip active silences expiring after "--within" - if !c.expired && int64(c.within) > 0 && silence.EndsAt.After(time.Now().UTC().Add(c.within)) { + if !c.expired && int64(c.within) > 0 && time.Time(*silence.EndsAt).After(time.Now().UTC().Add(c.within)) { continue } // skip silences that expired before "--within" - if c.expired && int64(c.within) > 0 && silence.EndsAt.Before(time.Now().UTC().Add(-c.within)) { + if c.expired && int64(c.within) > 0 && time.Time(*silence.EndsAt).Before(time.Now().UTC().Add(-c.within)) { continue } diff --git a/cli/silence_update.go b/cli/silence_update.go index bd92a80a..44b5c826 100644 --- a/cli/silence_update.go +++ b/cli/silence_update.go @@ -19,12 +19,12 @@ import ( "fmt" "time" - "github.com/prometheus/client_golang/api" - "gopkg.in/alecthomas/kingpin.v2" + kingpin "gopkg.in/alecthomas/kingpin.v2" + "github.com/go-openapi/strfmt" + "github.com/prometheus/alertmanager/api/v2/client/silence" + "github.com/prometheus/alertmanager/api/v2/models" "github.com/prometheus/alertmanager/cli/format" - "github.com/prometheus/alertmanager/client" - "github.com/prometheus/alertmanager/types" "github.com/prometheus/common/model" ) @@ -57,30 +57,33 @@ func (c *silenceUpdateCmd) update(ctx context.Context, _ *kingpin.ParseContext) return fmt.Errorf("no silence IDs specified") } - apiClient, err := api.NewClient(api.Config{Address: alertmanagerURL.String()}) - if err != nil { - return err - } - silenceAPI := client.NewSilenceAPI(apiClient) + amclient := NewAlertmanagerClient(alertmanagerURL) - var updatedSilences []types.Silence + var updatedSilences []models.GettableSilence for _, silenceID := range c.ids { - silence, err := silenceAPI.Get(ctx, silenceID) + params := silence.NewGetSilenceParams() + params.SilenceID = strfmt.UUID(silenceID) + response, err := amclient.Silence.GetSilence(params) + sil := response.Payload if err != nil { return err } if c.start != "" { - silence.StartsAt, err = time.Parse(time.RFC3339, c.start) + startsAtTime, err := time.Parse(time.RFC3339, c.start) if err != nil { return err } + startsAt := strfmt.DateTime(startsAtTime) + sil.StartsAt = &startsAt } if c.end != "" { - silence.EndsAt, err = time.Parse(time.RFC3339, c.end) + endsAtTime, err := time.Parse(time.RFC3339, c.end) if err != nil { return err } + endsAt := strfmt.DateTime(endsAtTime) + sil.EndsAt = &endsAt } else if c.duration != "" { d, err := model.ParseDuration(c.duration) if err != nil { @@ -89,24 +92,35 @@ func (c *silenceUpdateCmd) update(ctx context.Context, _ *kingpin.ParseContext) if d == 0 { return fmt.Errorf("silence duration must be greater than 0") } - silence.EndsAt = silence.StartsAt.UTC().Add(time.Duration(d)) + endsAt := strfmt.DateTime(time.Time(*sil.StartsAt).UTC().Add(time.Duration(d))) + sil.EndsAt = &endsAt } - if silence.StartsAt.After(silence.EndsAt) { + if time.Time(*sil.StartsAt).After(time.Time(*sil.EndsAt)) { return errors.New("silence cannot start after it ends") } if c.comment != "" { - silence.Comment = c.comment + sil.Comment = &c.comment } - newID, err := silenceAPI.Set(ctx, *silence) + ps := &models.PostableSilence{ + ID: *sil.ID, + Silence: sil.Silence, + } + + silenceParams := silence.NewPostSilencesParams().WithContext(ctx) + silenceParams.Silence = ps + + amclient := NewAlertmanagerClient(alertmanagerURL) + + postOk, err := amclient.Silence.PostSilences(silenceParams) if err != nil { return err } - silence.ID = newID - updatedSilences = append(updatedSilences, *silence) + sil.ID = &postOk.Payload.SilenceID + updatedSilences = append(updatedSilences, *sil) } if c.quiet { diff --git a/cli/test_routing.go b/cli/test_routing.go index 91127693..3d0216dd 100644 --- a/cli/test_routing.go +++ b/cli/test_routing.go @@ -19,10 +19,10 @@ import ( "os" "strings" - "github.com/prometheus/alertmanager/client" + "github.com/prometheus/alertmanager/api/v2/models" "github.com/prometheus/alertmanager/dispatch" "github.com/xlab/treeprint" - "gopkg.in/alecthomas/kingpin.v2" + kingpin "gopkg.in/alecthomas/kingpin.v2" ) const routingTestHelp = `Test alert routing @@ -49,7 +49,7 @@ func configureRoutingTestCmd(cc *kingpin.CmdClause, c *routingShow) { } // resolveAlertReceivers returns list of receiver names which given LabelSet resolves to. -func resolveAlertReceivers(mainRoute *dispatch.Route, labels *client.LabelSet) ([]string, error) { +func resolveAlertReceivers(mainRoute *dispatch.Route, labels *models.LabelSet) ([]string, error) { var ( finalRoutes []*dispatch.Route receivers []string @@ -61,7 +61,7 @@ func resolveAlertReceivers(mainRoute *dispatch.Route, labels *client.LabelSet) ( return receivers, nil } -func printMatchingTree(mainRoute *dispatch.Route, ls client.LabelSet) { +func printMatchingTree(mainRoute *dispatch.Route, ls models.LabelSet) { tree := treeprint.New() getMatchingTree(mainRoute, tree, ls) fmt.Println("Matching routes:") diff --git a/cli/test_routing_test.go b/cli/test_routing_test.go index 9c83211f..4aea2bce 100644 --- a/cli/test_routing_test.go +++ b/cli/test_routing_test.go @@ -19,18 +19,18 @@ import ( "strings" "testing" - "github.com/prometheus/alertmanager/client" + "github.com/prometheus/alertmanager/api/v2/models" "github.com/prometheus/alertmanager/config" "github.com/prometheus/alertmanager/dispatch" ) type routingTestDefinition struct { - alert client.LabelSet + alert models.LabelSet expectedReceivers []string configFile string } -func checkResolvedReceivers(mainRoute *dispatch.Route, ls client.LabelSet, expectedReceivers []string) error { +func checkResolvedReceivers(mainRoute *dispatch.Route, ls models.LabelSet, expectedReceivers []string) error { resolvedReceivers, err := resolveAlertReceivers(mainRoute, &ls) if err != nil { return err @@ -43,10 +43,10 @@ func checkResolvedReceivers(mainRoute *dispatch.Route, ls client.LabelSet, expec func TestRoutingTest(t *testing.T) { tests := []*routingTestDefinition{ - &routingTestDefinition{configFile: "testdata/conf.routing.yml", alert: client.LabelSet{"test": "1"}, expectedReceivers: []string{"test1"}}, - &routingTestDefinition{configFile: "testdata/conf.routing.yml", alert: client.LabelSet{"test": "2"}, expectedReceivers: []string{"test1", "test2"}}, - &routingTestDefinition{configFile: "testdata/conf.routing-reverted.yml", alert: client.LabelSet{"test": "2"}, expectedReceivers: []string{"test2", "test1"}}, - &routingTestDefinition{configFile: "testdata/conf.routing.yml", alert: client.LabelSet{"test": "volovina"}, expectedReceivers: []string{"default"}}, + &routingTestDefinition{configFile: "testdata/conf.routing.yml", alert: models.LabelSet{"test": "1"}, expectedReceivers: []string{"test1"}}, + &routingTestDefinition{configFile: "testdata/conf.routing.yml", alert: models.LabelSet{"test": "2"}, expectedReceivers: []string{"test1", "test2"}}, + &routingTestDefinition{configFile: "testdata/conf.routing-reverted.yml", alert: models.LabelSet{"test": "2"}, expectedReceivers: []string{"test2", "test1"}}, + &routingTestDefinition{configFile: "testdata/conf.routing.yml", alert: models.LabelSet{"test": "volovina"}, expectedReceivers: []string{"default"}}, } for _, test := range tests { diff --git a/cli/utils.go b/cli/utils.go index 929ded42..c88c393a 100644 --- a/cli/utils.go +++ b/cli/utils.go @@ -21,13 +21,13 @@ import ( "os" "path" - "github.com/prometheus/alertmanager/client" + "github.com/prometheus/alertmanager/api/v2/client/general" + "github.com/prometheus/alertmanager/api/v2/models" + "github.com/prometheus/alertmanager/config" amconfig "github.com/prometheus/alertmanager/config" - "github.com/prometheus/client_golang/api" kingpin "gopkg.in/alecthomas/kingpin.v2" "github.com/prometheus/alertmanager/pkg/parse" - "github.com/prometheus/alertmanager/types" "github.com/prometheus/common/model" "github.com/prometheus/prometheus/pkg/labels" ) @@ -74,17 +74,15 @@ func parseMatchers(inputMatchers []string) ([]labels.Matcher, error) { } // getRemoteAlertmanagerConfigStatus returns status responsecontaining configuration from remote Alertmanager -func getRemoteAlertmanagerConfigStatus(ctx context.Context, alertmanagerURL *url.URL) (*client.ServerStatus, error) { - c, err := api.NewClient(api.Config{Address: alertmanagerURL.String()}) +func getRemoteAlertmanagerConfigStatus(ctx context.Context, alertmanagerURL *url.URL) (*models.AlertmanagerStatus, error) { + amclient := NewAlertmanagerClient(alertmanagerURL) + params := general.NewGetStatusParams().WithContext(ctx) + getOk, err := amclient.General.GetStatus(params) if err != nil { return nil, err } - statusAPI := client.NewStatusAPI(c) - status, err := statusAPI.Get(ctx) - if err != nil { - return nil, err - } - return status, nil + + return getOk.Payload, nil } func checkRoutingConfigInputFlags(alertmanagerURL *url.URL, configFile string) { @@ -106,17 +104,21 @@ func loadAlertmanagerConfig(ctx context.Context, alertmanagerURL *url.URL, confi return cfg, nil } if alertmanagerURL != nil { - status, err := getRemoteAlertmanagerConfigStatus(ctx, alertmanagerURL) + configStatus, err := getRemoteAlertmanagerConfigStatus(ctx, alertmanagerURL) if err != nil { return nil, err } - return status.ConfigJSON, nil + conf, err := config.Load(*configStatus.Config.Original) + if err != nil { + return nil, err + } + return conf, nil } return nil, errors.New("failed to get Alertmanager configuration") } // convertClientToCommonLabelSet converts client.LabelSet to model.Labelset -func convertClientToCommonLabelSet(cls client.LabelSet) model.LabelSet { +func convertClientToCommonLabelSet(cls models.LabelSet) model.LabelSet { mls := make(model.LabelSet, len(cls)) for ln, lv := range cls { mls[model.LabelName(ln)] = model.LabelValue(lv) @@ -125,51 +127,58 @@ func convertClientToCommonLabelSet(cls client.LabelSet) model.LabelSet { } // Parse a list of labels (cli arguments) -func parseLabels(inputLabels []string) (client.LabelSet, error) { - labelSet := make(client.LabelSet, len(inputLabels)) +func parseLabels(inputLabels []string) (models.LabelSet, error) { + labelSet := make(models.LabelSet, len(inputLabels)) for _, l := range inputLabels { name, value, matchType, err := parse.Input(l) if err != nil { - return client.LabelSet{}, err + return models.LabelSet{}, err } if matchType != labels.MatchEqual { - return client.LabelSet{}, errors.New("labels must be specified as key=value pairs") + return models.LabelSet{}, errors.New("labels must be specified as key=value pairs") } - labelSet[client.LabelName(name)] = client.LabelValue(value) + labelSet[name] = value } return labelSet, nil } -// Only valid for when you are going to add a silence -func TypeMatchers(matchers []labels.Matcher) (types.Matchers, error) { - typeMatchers := types.Matchers{} +// TypeMatchers Only valid for when you are going to add a silence +func TypeMatchers(matchers []labels.Matcher) (models.Matchers, error) { + typeMatchers := models.Matchers{} for _, matcher := range matchers { typeMatcher, err := TypeMatcher(matcher) if err != nil { - return types.Matchers{}, err + return models.Matchers{}, err } typeMatchers = append(typeMatchers, &typeMatcher) } return typeMatchers, nil } -// Only valid for when you are going to add a silence +// TypeMatcher Only valid for when you are going to add a silence // Doesn't allow negative operators -func TypeMatcher(matcher labels.Matcher) (types.Matcher, error) { - typeMatcher := types.NewMatcher(model.LabelName(matcher.Name), matcher.Value) +func TypeMatcher(matcher labels.Matcher) (models.Matcher, error) { + name := matcher.Name + value := matcher.Value + typeMatcher := models.Matcher{ + Name: &name, + Value: &value, + } + isRegex := false switch matcher.Type { case labels.MatchEqual: - typeMatcher.IsRegex = false + isRegex = false case labels.MatchRegexp: - typeMatcher.IsRegex = true + isRegex = true default: - return types.Matcher{}, fmt.Errorf("invalid match type for creation operation: %s", matcher.Type) + return models.Matcher{}, fmt.Errorf("invalid match type for creation operation: %s", matcher.Type) } - return *typeMatcher, nil + typeMatcher.IsRegex = &isRegex + return typeMatcher, nil } // Helper function for adding the ctx with timeout into an action.