Skip to content
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

Hwa/new version #167

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions cmd/jiralert/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ import (
"runtime"
"strconv"

"github.com/andygrunwald/go-jira"
jira "github.com/andygrunwald/go-jira"

"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/jiralert/pkg/alertmanager"
Expand All @@ -36,9 +37,10 @@ import (
)

const (
unknownReceiver = "<unknown>"
logFormatLogfmt = "logfmt"
logFormatJSON = "json"
unknownReceiver = "<unknown>"
logFormatLogfmt = "logfmt"
logFormatJSON = "json"
defaultMaxDescriptionLength = 32768 // https://jira.atlassian.com/browse/JRASERVER-64351
)

var (
Expand All @@ -48,9 +50,10 @@ var (
logFormat = flag.String("log.format", logFormatLogfmt, "Log format to use ("+logFormatLogfmt+", "+logFormatJSON+")")
hashJiraLabel = flag.Bool("hash-jira-label", false, "if enabled: renames ALERT{...} to JIRALERT{...}; also hashes the key-value pairs inside of JIRALERT{...} in the created jira issue labels"+
"- this ensures that the label text does not overflow the allowed length in jira (255)")
updateSummary = flag.Bool("update-summary", true, "When false, jiralert does not update the summary of the existing jira issue, even when changes are spotted.")
updateDescription = flag.Bool("update-description", true, "When false, jiralert does not update the description of the existing jira issue, even when changes are spotted.")
reopenTickets = flag.Bool("reopen-tickets", true, "When false, jiralert does not reopen tickets.")
updateSummary = flag.Bool("update-summary", true, "When false, jiralert does not update the summary of the existing jira issue, even when changes are spotted.")
updateDescription = flag.Bool("update-description", true, "When false, jiralert does not update the description of the existing jira issue, even when changes are spotted.")
reopenTickets = flag.Bool("reopen-tickets", true, "When false, jiralert does not reopen tickets.")
maxDescriptionLength = flag.Int("max-description-length", defaultMaxDescriptionLength, "Maximum length of Descriptions. Truncate to this size avoid server errors.")

// Version is the build version, set by make to latest git tag/hash via `-ldflags "-X main.Version=$(VERSION)"`.
Version = "<local build>"
Expand Down Expand Up @@ -124,7 +127,7 @@ func main() {
return
}

if retry, err := notify.NewReceiver(logger, conf, tmpl, client.Issue).Notify(&data, *hashJiraLabel, *updateSummary, *updateDescription, *reopenTickets); err != nil {
if retry, err := notify.NewReceiver(logger, conf, tmpl, client.Issue).Notify(&data, *hashJiraLabel, *updateSummary, *updateDescription, *reopenTickets, *maxDescriptionLength); err != nil {
var status int
if retry {
// Instruct Alertmanager to retry.
Expand Down
4 changes: 3 additions & 1 deletion examples/jiralert.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ defaults:
reopen_duration: 0h
# Static label that will be added to the JIRA ticket alongisde the JIRALERT{...} or ALERT{...} label
static_labels: ["custom"]
# Other projects that issues may be moved to and further updates are desired
other_projects: ["OTHER1", "OTHER2"]

# Receiver definitions. At least one must be defined.
receivers:
Expand Down Expand Up @@ -56,7 +58,7 @@ receivers:
#
# Automatically resolve jira issues when alert is resolved. Optional. If declared, ensure state is not an empty string.
auto_resolve:
state: 'Done'
state: 'Done'

# File containing template definitions. Required.
template: jiralert.tmpl
1 change: 1 addition & 0 deletions pkg/alertmanager/alertmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ type Alert struct {
StartsAt time.Time `json:"startsAt"`
EndsAt time.Time `json:"endsAt"`
GeneratorURL string `json:"generatorURL"`
Fingerprint string `json:"fingerprint"`
}

// Alerts is a list of Alert objects.
Expand Down
8 changes: 6 additions & 2 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ type ReceiverConfig struct {

// Required issue fields
Project string `yaml:"project" json:"project"`
OtherProjects []string `yaml:"other_projects" json:"other_projects"`
IssueType string `yaml:"issue_type" json:"issue_type"`
Summary string `yaml:"summary" json:"summary"`
ReopenState string `yaml:"reopen_state" json:"reopen_state"`
Expand Down Expand Up @@ -311,6 +312,9 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
if len(c.Defaults.StaticLabels) > 0 {
rc.StaticLabels = append(rc.StaticLabels, c.Defaults.StaticLabels...)
}
if len(c.Defaults.OtherProjects) > 0 {
rc.OtherProjects = append(rc.OtherProjects, c.Defaults.OtherProjects...)
}
}

if len(c.Receivers) == 0 {
Expand Down Expand Up @@ -354,7 +358,7 @@ var durationRE = regexp.MustCompile("^([0-9]+)(y|w|d|h|m|s|ms)$")
func ParseDuration(durationStr string) (Duration, error) {
matches := durationRE.FindStringSubmatch(durationStr)
if len(matches) != 3 {
return 0, fmt.Errorf("not a valid duration string: %q", durationStr)
return Duration(time.Duration(0)), fmt.Errorf("not a valid duration string: %q", durationStr)
}
var (
n, _ = strconv.Atoi(matches[1])
Expand All @@ -376,7 +380,7 @@ func ParseDuration(durationStr string) (Duration, error) {
case "ms":
// Value already correct
default:
return 0, fmt.Errorf("invalid time unit in duration string: %q", unit)
return Duration(time.Duration(0)), fmt.Errorf("invalid time unit in duration string: %q", unit)
}
return Duration(dur), nil
}
Expand Down
26 changes: 21 additions & 5 deletions pkg/notify/notify.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
"strings"
"time"

"github.com/andygrunwald/go-jira"
jira "github.com/andygrunwald/go-jira"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/pkg/errors"
Expand Down Expand Up @@ -60,7 +60,7 @@ func NewReceiver(logger log.Logger, c *config.ReceiverConfig, t *template.Templa
}

// Notify manages JIRA issues based on alertmanager webhook notify message.
func (r *Receiver) Notify(data *alertmanager.Data, hashJiraLabel bool, updateSummary bool, updateDescription bool, reopenTickets bool) (bool, error) {
func (r *Receiver) Notify(data *alertmanager.Data, hashJiraLabel bool, updateSummary bool, updateDescription bool, reopenTickets bool, maxDescriptionLength int) (bool, error) {
project, err := r.tmpl.Execute(r.conf.Project, data)
if err != nil {
return false, errors.Wrap(err, "generate project from template")
Expand All @@ -85,6 +85,11 @@ func (r *Receiver) Notify(data *alertmanager.Data, hashJiraLabel bool, updateSum
return false, errors.Wrap(err, "render issue description")
}

if len(issueDesc) > maxDescriptionLength {
level.Warn(r.logger).Log("msg", "truncating description", "original", len(issueDesc), "limit", maxDescriptionLength)
issueDesc = issueDesc[:maxDescriptionLength]
}

if issue != nil {

// Update summary if needed.
Expand Down Expand Up @@ -178,6 +183,7 @@ func (r *Receiver) Notify(data *alertmanager.Data, hashJiraLabel bool, updateSum
if len(r.conf.Components) > 0 {
issue.Fields.Components = make([]*jira.Component, 0, len(r.conf.Components))
for _, component := range r.conf.Components {
//nolint:typecheck // lint flags issueComp as not being used, even though it's referenced below.
issueComp, err := r.tmpl.Execute(component, data)
if err != nil {
return false, errors.Wrap(err, "render issue component")
Expand Down Expand Up @@ -283,8 +289,10 @@ func toGroupTicketLabel(groupLabels alertmanager.KV, hashJiraLabel bool) string
return strings.Replace(buf.String(), " ", "", -1)
}

func (r *Receiver) search(project, issueLabel string) (*jira.Issue, bool, error) {
query := fmt.Sprintf("project=\"%s\" and labels=%q order by resolutiondate desc", project, issueLabel)
func (r *Receiver) search(projects []string, issueLabel string) (*jira.Issue, bool, error) {
// 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)
options := &jira.SearchOptions{
Fields: []string{"summary", "status", "resolution", "resolutiondate"},
MaxResults: 2,
Expand Down Expand Up @@ -312,7 +320,15 @@ func (r *Receiver) search(project, issueLabel string) (*jira.Issue, bool, error)
}

func (r *Receiver) findIssueToReuse(project string, issueGroupLabel string) (*jira.Issue, bool, error) {
issue, retry, err := r.search(project, issueGroupLabel)
projectsToSearch := []string{project}
// in case issue was moved to a different project, include configured potential other projects in search
for _, other := range r.conf.OtherProjects {
if other != project {
projectsToSearch = append(projectsToSearch, other)
}
}

issue, retry, err := r.search(projectsToSearch, issueGroupLabel)
if err != nil {
return nil, retry, err
}
Expand Down
40 changes: 20 additions & 20 deletions pkg/notify/notify_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
"testing"
"time"

"github.com/andygrunwald/go-jira"
jira "github.com/andygrunwald/go-jira"

"github.com/trivago/tgo/tcontainer"

Expand Down Expand Up @@ -107,7 +107,7 @@ func (f *fakeJira) Create(issue *jira.Issue) (*jira.Issue, *jira.Response, error

// Assuming single label.
query := fmt.Sprintf(
"project=\"%s\" and labels=%q order by resolutiondate desc",
"project in('%s') and labels=%q order by resolutiondate desc",
issue.Fields.Project.Key,
issue.Fields.Labels[0],
)
Expand Down Expand Up @@ -216,9 +216,9 @@ func TestNotify_JIRAInteraction(t *testing.T) {
initJira: func(t *testing.T) *fakeJira { return newTestFakeJira() },
inputAlert: &alertmanager.Data{
Alerts: alertmanager.Alerts{
{Status: alertmanager.AlertFiring},
{Status: "not firing"},
{Status: alertmanager.AlertFiring},
alertmanager.Alert{Status: alertmanager.AlertFiring},
alertmanager.Alert{Status: "not firing"},
alertmanager.Alert{Status: alertmanager.AlertFiring},
},
Status: alertmanager.AlertFiring,
GroupLabels: alertmanager.KV{"a": "b", "c": "d"},
Expand Down Expand Up @@ -259,8 +259,8 @@ func TestNotify_JIRAInteraction(t *testing.T) {
},
inputAlert: &alertmanager.Data{
Alerts: alertmanager.Alerts{
{Status: "not firing"},
{Status: alertmanager.AlertFiring}, // Only one firing now.
alertmanager.Alert{Status: "not firing"},
alertmanager.Alert{Status: alertmanager.AlertFiring}, // Only one firing now.
},
Status: alertmanager.AlertFiring,
GroupLabels: alertmanager.KV{"a": "b", "c": "d"},
Expand Down Expand Up @@ -302,8 +302,8 @@ func TestNotify_JIRAInteraction(t *testing.T) {
},
inputAlert: &alertmanager.Data{
Alerts: alertmanager.Alerts{
{Status: "not firing"},
{Status: alertmanager.AlertFiring}, // Only one firing now.
alertmanager.Alert{Status: "not firing"},
alertmanager.Alert{Status: alertmanager.AlertFiring}, // Only one firing now.
},
Status: alertmanager.AlertFiring,
GroupLabels: alertmanager.KV{"a": "b", "c": "d"},
Expand Down Expand Up @@ -354,8 +354,8 @@ func TestNotify_JIRAInteraction(t *testing.T) {
},
inputAlert: &alertmanager.Data{
Alerts: alertmanager.Alerts{
{Status: "not firing"},
{Status: alertmanager.AlertFiring}, // Only one firing now.
alertmanager.Alert{Status: "not firing"},
alertmanager.Alert{Status: alertmanager.AlertFiring}, // Only one firing now.
},
Status: alertmanager.AlertFiring,
GroupLabels: alertmanager.KV{"a": "b", "c": "d"},
Expand Down Expand Up @@ -409,8 +409,8 @@ func TestNotify_JIRAInteraction(t *testing.T) {
},
inputAlert: &alertmanager.Data{
Alerts: alertmanager.Alerts{
{Status: "not firing"},
{Status: alertmanager.AlertFiring}, // Only one firing now.
alertmanager.Alert{Status: "not firing"},
alertmanager.Alert{Status: alertmanager.AlertFiring}, // Only one firing now.
},
Status: alertmanager.AlertFiring,
GroupLabels: alertmanager.KV{"a": "b", "c": "d"},
Expand Down Expand Up @@ -464,8 +464,8 @@ func TestNotify_JIRAInteraction(t *testing.T) {
},
inputAlert: &alertmanager.Data{
Alerts: alertmanager.Alerts{
{Status: "not firing"},
{Status: alertmanager.AlertFiring}, // Only one firing now.
alertmanager.Alert{Status: "not firing"},
alertmanager.Alert{Status: alertmanager.AlertFiring}, // Only one firing now.
},
Status: alertmanager.AlertFiring,
GroupLabels: alertmanager.KV{"a": "b", "c": "d"},
Expand Down Expand Up @@ -510,7 +510,7 @@ func TestNotify_JIRAInteraction(t *testing.T) {
inputConfig: testReceiverConfigAutoResolve(),
inputAlert: &alertmanager.Data{
Alerts: alertmanager.Alerts{
{Status: "resolved"},
alertmanager.Alert{Status: "resolved"},
},
Status: alertmanager.AlertResolved,
GroupLabels: alertmanager.KV{"a": "b", "c": "d"},
Expand Down Expand Up @@ -554,9 +554,9 @@ func TestNotify_JIRAInteraction(t *testing.T) {
initJira: func(t *testing.T) *fakeJira { return newTestFakeJira() },
inputAlert: &alertmanager.Data{
Alerts: alertmanager.Alerts{
{Status: alertmanager.AlertFiring},
{Status: "not firing"},
{Status: alertmanager.AlertFiring},
alertmanager.Alert{Status: alertmanager.AlertFiring},
alertmanager.Alert{Status: "not firing"},
alertmanager.Alert{Status: alertmanager.AlertFiring},
},
Status: alertmanager.AlertFiring,
GroupLabels: alertmanager.KV{"a": "b", "c": "d"},
Expand Down Expand Up @@ -592,7 +592,7 @@ func TestNotify_JIRAInteraction(t *testing.T) {
return testNowTime
}

_, err := receiver.Notify(tcase.inputAlert, true, true, true, true)
_, err := receiver.Notify(tcase.inputAlert, true, true, true, true, 32768)
require.NoError(t, err)
require.Equal(t, tcase.expectedJiraIssues, fakeJira.issuesByKey)
}); !ok {
Expand Down