-
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
[amtool] - Add new command to update silence #1123
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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)") | ||
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. I'm thinking we should rename these. Maybe 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. I was just following the parameter names in 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. good point |
||
updateCmd.Flags().String("expire-on", "", "Expire at a certain time (Overwrites expires) RFC3339 format 2006-01-02T15:04:05Z07:00") | ||
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. In alertmanager, you can also update the starting time when editing a silence. How about adding that as well, and naming the flags 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. hm, thinking more about the name .. not sure i like 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. Should we just follow the field names in Alertmanager API, 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. hrm, those are more json-idiomatic names. for a CLI I think something different would be more appropriate. |
||
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 | ||
} |
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.
Could you update this with a longer description, with a usage example? It would also be good to note that
expires-on
will overrideexpires
.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.
I'dd add that in once we decided on the parameter names :)