-
Notifications
You must be signed in to change notification settings - Fork 140
feat(conf): add optional field_labels to override Labels #189
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
base: master
Are you sure you want to change the base?
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 |
|---|---|---|
|
|
@@ -23,6 +23,7 @@ import ( | |
| "strings" | ||
| "time" | ||
|
|
||
| "github.com/andygrunwald/go-jira" | ||
| "github.com/go-kit/log" | ||
| "github.com/go-kit/log/level" | ||
|
|
||
|
|
@@ -143,6 +144,8 @@ type ReceiverConfig struct { | |
| Priority string `yaml:"priority" json:"priority"` | ||
| Description string `yaml:"description" json:"description"` | ||
| WontFixResolution string `yaml:"wont_fix_resolution" json:"wont_fix_resolution"` | ||
| FieldLabels string `yaml:"field_labels" json:"field_labels"` | ||
| FieldLabelsKey string `yaml:"field_labels_key" json:"field_labels_key"` | ||
| Fields map[string]interface{} `yaml:"fields" json:"fields"` | ||
| Components []string `yaml:"components" json:"components"` | ||
| StaticLabels []string `yaml:"static_labels" json:"static_labels"` | ||
|
|
@@ -194,6 +197,58 @@ func (c Config) String() string { | |
| return string(b) | ||
| } | ||
|
|
||
| // GetJiraFieldKey returns the jira key associated to a field. | ||
|
Member
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. Comment is different vs actual function signature -- GetJiraFieldKey only returns error or nil. |
||
| func (c *Config) GetJiraFieldKey() error { | ||
|
Member
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. Do you mind moving this function out of the config.go? It would be epic to separate JIRA API integrations vs config logic. Also let's avoid extra config state that does not belong to user config directly - it's ok to store this fieldkey as variable somewhere. Also probably you mean |
||
| var missingField []string | ||
| for _, rc := range c.Receivers { | ||
| // descover jira labels key. | ||
| var client *jira.Client | ||
| var err error | ||
| if rc.User != "" && rc.Password != "" { | ||
| tp := jira.BasicAuthTransport{ | ||
| Username: rc.User, | ||
| Password: string(rc.Password), | ||
| } | ||
| client, err = jira.NewClient(tp.Client(), rc.APIURL) | ||
| } else if rc.PersonalAccessToken != "" { | ||
| tp := jira.PATAuthTransport{ | ||
| Token: string(rc.PersonalAccessToken), | ||
| } | ||
| client, err = jira.NewClient(tp.Client(), rc.APIURL) | ||
| } | ||
|
|
||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| options := &jira.GetQueryOptions{ | ||
| ProjectKeys: rc.Project, | ||
| Expand: "projects.issuetypes.fields", | ||
| } | ||
| meta, _, err := client.Issue.GetCreateMetaWithOptions(options) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| it := meta.Projects[0].GetIssueTypeWithName(rc.IssueType) | ||
| if it == nil { | ||
| return fmt.Errorf("jira: Issue type %s not found", rc.IssueType) | ||
| } | ||
| fields, err := it.GetAllFields() | ||
| if err != nil { | ||
| return err | ||
| } | ||
| if val, ok := fields[rc.FieldLabels]; ok { | ||
| rc.FieldLabelsKey = val | ||
| continue | ||
| } | ||
| missingField = append(missingField, rc.FieldLabels) | ||
| } | ||
| if len(missingField) != 0 { | ||
| return fmt.Errorf("jira: Fields %s ", missingField) | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| // UnmarshalYAML implements the yaml.Unmarshaler interface. | ||
| func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { | ||
| // We want to set c to the defaults and then overwrite it with the input. | ||
|
|
@@ -297,6 +352,14 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { | |
| if rc.WontFixResolution == "" && c.Defaults.WontFixResolution != "" { | ||
| rc.WontFixResolution = c.Defaults.WontFixResolution | ||
| } | ||
| if rc.FieldLabels == "" { | ||
| if c.Defaults.FieldLabels != "" { | ||
| rc.FieldLabels = c.Defaults.FieldLabels | ||
| } else { | ||
| rc.FieldLabels = "Labels" | ||
|
Member
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. We might need constant variable for this |
||
| } | ||
| } | ||
|
|
||
| if rc.AutoResolve != nil { | ||
| if rc.AutoResolve.State == "" { | ||
| return fmt.Errorf("bad config in receiver %q, 'auto_resolve' was defined with empty 'state' field", rc.Name) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -137,32 +137,32 @@ func (r *Receiver) Notify(data *alertmanager.Data, hashJiraLabel bool, updateSum | |
|
|
||
| if len(data.Alerts.Firing()) == 0 { | ||
| if r.conf.AutoResolve != nil { | ||
| level.Debug(r.logger).Log("msg", "no firing alert; resolving issue", "key", issue.Key, "label", issueGroupLabel) | ||
| level.Debug(r.logger).Log("msg", "no firing alert; resolving issue", "key", issue.Key, r.conf.FieldLabels, issueGroupLabel) | ||
| retry, err := r.resolveIssue(issue.Key) | ||
| if err != nil { | ||
| return retry, err | ||
| } | ||
| return false, nil | ||
| } | ||
|
|
||
| level.Debug(r.logger).Log("msg", "no firing alert; summary checked, nothing else to do.", "key", issue.Key, "label", issueGroupLabel) | ||
| level.Debug(r.logger).Log("msg", "no firing alert; summary checked, nothing else to do.", "key", issue.Key, r.conf.FieldLabels, issueGroupLabel) | ||
| return false, nil | ||
| } | ||
|
|
||
| // The set of JIRA status categories is fixed, this is a safe check to make. | ||
| if issue.Fields.Status.StatusCategory.Key != "done" { | ||
| level.Debug(r.logger).Log("msg", "issue is unresolved, all is done", "key", issue.Key, "label", issueGroupLabel) | ||
| level.Debug(r.logger).Log("msg", "issue is unresolved, all is done", "key", issue.Key, r.conf.FieldLabels, issueGroupLabel) | ||
| return false, nil | ||
| } | ||
|
|
||
| if reopenTickets { | ||
| if r.conf.WontFixResolution != "" && issue.Fields.Resolution != nil && | ||
| issue.Fields.Resolution.Name == r.conf.WontFixResolution { | ||
| level.Info(r.logger).Log("msg", "issue was resolved as won't fix, not reopening", "key", issue.Key, "label", issueGroupLabel, "resolution", issue.Fields.Resolution.Name) | ||
| level.Info(r.logger).Log("msg", "issue was resolved as won't fix, not reopening", "key", issue.Key, r.conf.FieldLabels, issueGroupLabel, "resolution", issue.Fields.Resolution.Name) | ||
| return false, nil | ||
| } | ||
|
|
||
| level.Info(r.logger).Log("msg", "issue was recently resolved, reopening", "key", issue.Key, "label", issueGroupLabel) | ||
| level.Info(r.logger).Log("msg", "issue was recently resolved, reopening", "key", issue.Key, r.conf.FieldLabels, issueGroupLabel) | ||
| return r.reopen(issue.Key) | ||
| } | ||
|
|
||
|
|
@@ -171,11 +171,11 @@ func (r *Receiver) Notify(data *alertmanager.Data, hashJiraLabel bool, updateSum | |
| } | ||
|
|
||
| if len(data.Alerts.Firing()) == 0 { | ||
| level.Debug(r.logger).Log("msg", "no firing alert; nothing to do.", "label", issueGroupLabel) | ||
| level.Debug(r.logger).Log("msg", "no firing alert; nothing to do.", r.conf.FieldLabels, issueGroupLabel) | ||
|
Member
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. Not sure if we should use Also I would rename |
||
| return false, nil | ||
| } | ||
|
|
||
| level.Info(r.logger).Log("msg", "no recent matching issue found, creating new issue", "label", issueGroupLabel) | ||
| level.Info(r.logger).Log("msg", "no recent matching issue found, creating new issue", r.conf.FieldLabels, issueGroupLabel) | ||
|
|
||
| issueType, err := r.tmpl.Execute(r.conf.IssueType, data) | ||
| if err != nil { | ||
|
|
@@ -190,10 +190,14 @@ func (r *Receiver) Notify(data *alertmanager.Data, hashJiraLabel bool, updateSum | |
| Type: jira.IssueType{Name: issueType}, | ||
| Description: issueDesc, | ||
| Summary: issueSummary, | ||
| Labels: append(staticLabels, issueGroupLabel), | ||
| Unknowns: tcontainer.NewMarshalMap(), | ||
| }, | ||
| } | ||
| if r.conf.FieldLabels == "Labels" { | ||
| issue.Fields.Labels = append(staticLabels, issueGroupLabel) | ||
| } else { | ||
| issue.Fields.Unknowns[r.conf.FieldLabelsKey] = append(staticLabels, issueGroupLabel) | ||
| } | ||
| if r.conf.Priority != "" { | ||
| issuePrio, err := r.tmpl.Execute(r.conf.Priority, data) | ||
| if err != nil { | ||
|
|
@@ -312,9 +316,15 @@ func toGroupTicketLabel(groupLabels alertmanager.KV, hashJiraLabel bool) string | |
| } | ||
|
|
||
| func (r *Receiver) search(projects []string, issueLabel string) (*jira.Issue, bool, error) { | ||
| var labelKey string | ||
| // Search multiple projects in case issue was moved and further alert firings are desired in existing JIRA. | ||
| projectList := "'" + strings.Join(projects, "', '") + "'" | ||
| query := fmt.Sprintf("project in(%s) and labels=%q order by resolutiondate desc", projectList, issueLabel) | ||
| if r.conf.FieldLabels == "Labels" { | ||
| labelKey = "labels" | ||
|
Member
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. Why not using |
||
| } else { | ||
| labelKey = fmt.Sprintf("cf[%s]", strings.Split(r.conf.FieldLabelsKey, "_")[1]) | ||
| } | ||
| query := fmt.Sprintf("project in(%s) and %s=%q order by resolutiondate desc", projectList, labelKey, issueLabel) | ||
| options := &jira.SearchOptions{ | ||
| Fields: []string{"summary", "status", "resolution", "resolutiondate", "description", "comment"}, | ||
| MaxResults: 2, | ||
|
|
@@ -361,7 +371,7 @@ func (r *Receiver) findIssueToReuse(project string, issueGroupLabel string) (*ji | |
|
|
||
| resolutionTime := time.Time(issue.Fields.Resolutiondate) | ||
| if resolutionTime != (time.Time{}) && resolutionTime.Add(time.Duration(*r.conf.ReopenDuration)).Before(r.timeNow()) && *r.conf.ReopenDuration != 0 { | ||
| level.Debug(r.logger).Log("msg", "existing resolved issue is too old to reopen, skipping", "key", issue.Key, "label", issueGroupLabel, "resolution_time", resolutionTime.Format(time.RFC3339), "reopen_duration", *r.conf.ReopenDuration) | ||
| level.Debug(r.logger).Log("msg", "existing resolved issue is too old to reopen, skipping", "key", issue.Key, r.conf.FieldLabels, issueGroupLabel, "resolution_time", resolutionTime.Format(time.RFC3339), "reopen_duration", *r.conf.ReopenDuration) | ||
| return nil, false, nil | ||
| } | ||
|
|
||
|
|
@@ -423,7 +433,6 @@ func (r *Receiver) reopen(issueKey string) (bool, error) { | |
| } | ||
|
|
||
| func (r *Receiver) create(issue *jira.Issue) (bool, error) { | ||
| level.Debug(r.logger).Log("msg", "create", "issue", fmt.Sprintf("%+v", *issue.Fields)) | ||
| newIssue, resp, err := r.client.Create(issue) | ||
| if err != nil { | ||
| return handleJiraErrResponse("Issue.Create", resp, err, r.logger) | ||
|
|
||
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.
Hm, what about
id_field?