Skip to content

Commit

Permalink
[amtool] - Add new command to update silence (#1123)
Browse files Browse the repository at this point in the history
This adds a new command, update (and also its alias, extend), to update
existing silence in Alertmanager. User can use this command to update the
expiration or comment on existing silences. The API already support this
so I only expose the same functionality to amtool.

Don't allow update CreatedBy field as it is "Created" not "Updated", so
we should keep the original author.
  • Loading branch information
lebinh authored and stuartnelson3 committed Dec 11, 2017
1 parent f9e96fc commit dbff31d
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 0 deletions.
1 change: 1 addition & 0 deletions cli/silence.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@ func init() {
silenceCmd.AddCommand(expireCmd)
silenceCmd.AddCommand(queryCmd)
silenceCmd.AddCommand(importCmd)
silenceCmd.AddCommand(updateCmd)
}
147 changes: 147 additions & 0 deletions cli/silence_update.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package cli

import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"path"
"time"

"github.com/prometheus/alertmanager/cli/format"
"github.com/prometheus/alertmanager/types"
"github.com/spf13/cobra"
flag "github.com/spf13/pflag"
"github.com/spf13/viper"
)

type getResponse struct {
Status string `json:"status"`
Data types.Silence `json:"data,omitempty"`
ErrorType string `json:"errorType,omitempty"`
Error string `json:"error,omitempty"`
}

var updateFlags *flag.FlagSet
var updateCmd = &cobra.Command{
Use: "update <id> ...",
Aliases: []string{"extend"},
Args: cobra.MinimumNArgs(1),
Short: "Update silences",
Long: `Extend or update existing silence in Alertmanager.`,
Run: CommandWrapper(update),
}

func init() {
updateCmd.Flags().StringP("expires", "e", "", "Duration of silence (100h)")
updateCmd.Flags().String("expire-on", "", "Expire at a certain time (Overwrites expires) RFC3339 format 2006-01-02T15:04:05Z07:00")
updateCmd.Flags().StringP("comment", "c", "", "A comment to help describe the silence")
updateFlags = updateCmd.Flags()
}

func update(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("no silence IDs specified")
}

alertmanagerUrl, err := GetAlertmanagerURL()
if err != nil {
return err
}

var updatedSilences []types.Silence
for _, silenceId := range args {
silence, err := getSilenceById(silenceId, *alertmanagerUrl)
if err != nil {
return err
}
silence, err = updateSilence(silence)
if err != nil {
return err
}
updatedSilences = append(updatedSilences, *silence)
}

quiet := viper.GetBool("quiet")
if quiet {
for _, silence := range updatedSilences {
fmt.Println(silence.ID)
}
} else {
formatter, found := format.Formatters[viper.GetString("output")]
if !found {
return fmt.Errorf("unknown output formatter")
}
formatter.FormatSilences(updatedSilences)
}
return nil
}

// This takes an url.URL and not a pointer as we will modify it for our API call.
func getSilenceById(silenceId string, baseUrl url.URL) (*types.Silence, error) {
baseUrl.Path = path.Join(baseUrl.Path, "/api/v1/silence", silenceId)
res, err := http.Get(baseUrl.String())
if err != nil {
return nil, err
}

defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, fmt.Errorf("couldn't read response body: %v", err)
}

if res.StatusCode == 404 {
return nil, fmt.Errorf("no silence found with id: %v", silenceId)
}
if res.StatusCode != 200 {
return nil, fmt.Errorf("received %d response from Alertmanager: %v", res.StatusCode, body)
}

var response getResponse
err = json.Unmarshal(body, &response)
return &response.Data, nil
}

func updateSilence(silence *types.Silence) (*types.Silence, error) {
if updateFlags.Changed("expires") {
expires, err := updateFlags.GetString("expires")
if err != nil {
return nil, err
}
duration, err := time.ParseDuration(expires)
if err != nil {
return nil, err
}
silence.EndsAt = time.Now().UTC().Add(duration)
}

// expire-on will override expires value if both are specified
if updateFlags.Changed("expire-on") {
expireOn, err := updateFlags.GetString("expire-on")
if err != nil {
return nil, err
}
endsAt, err := time.Parse(time.RFC3339, expireOn)
if err != nil {
return nil, err
}
silence.EndsAt = endsAt
}

if updateFlags.Changed("comment") {
comment, err := updateFlags.GetString("comment")
if err != nil {
return nil, err
}
silence.Comment = comment
}

// addSilence can also be used to update an existing silence
_, err := addSilence(silence)
if err != nil {
return nil, err
}
return silence, nil
}

0 comments on commit dbff31d

Please sign in to comment.