mirror of
https://github.com/prometheus/alertmanager.git
synced 2026-02-05 15:45:34 +01:00
Jira Integration: Allow configuring issue update via parameter (#4621)
* Jira Integration: Allow configuring issue update via parameter --------- Signed-off-by: Holger Waschke <waschkester@gmail.com>
This commit is contained in:
@@ -206,10 +206,14 @@ var (
|
||||
NotifierConfig: NotifierConfig{
|
||||
VSendResolved: true,
|
||||
},
|
||||
APIType: "auto",
|
||||
Summary: `{{ template "jira.default.summary" . }}`,
|
||||
Description: `{{ template "jira.default.description" . }}`,
|
||||
Priority: `{{ template "jira.default.priority" . }}`,
|
||||
APIType: "auto",
|
||||
Summary: JiraFieldConfig{
|
||||
Template: `{{ template "jira.default.summary" . }}`,
|
||||
},
|
||||
Description: JiraFieldConfig{
|
||||
Template: `{{ template "jira.default.description" . }}`,
|
||||
},
|
||||
Priority: `{{ template "jira.default.priority" . }}`,
|
||||
}
|
||||
|
||||
DefaultMattermostConfig = MattermostConfig{
|
||||
@@ -969,6 +973,13 @@ func (c *MSTeamsV2Config) UnmarshalYAML(unmarshal func(any) error) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type JiraFieldConfig struct {
|
||||
// Template is the template string used to render the field.
|
||||
Template string `yaml:"template,omitempty" json:"template,omitempty"`
|
||||
// EnableUpdate indicates whether this field should be omitted when updating an existing issue.
|
||||
EnableUpdate *bool `yaml:"enable_update,omitempty" json:"enable_update,omitempty"`
|
||||
}
|
||||
|
||||
type JiraConfig struct {
|
||||
NotifierConfig `yaml:",inline" json:",inline"`
|
||||
HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`
|
||||
@@ -976,12 +987,12 @@ type JiraConfig struct {
|
||||
APIURL *URL `yaml:"api_url,omitempty" json:"api_url,omitempty"`
|
||||
APIType string `yaml:"api_type,omitempty" json:"api_type,omitempty"`
|
||||
|
||||
Project string `yaml:"project,omitempty" json:"project,omitempty"`
|
||||
Summary string `yaml:"summary,omitempty" json:"summary,omitempty"`
|
||||
Description string `yaml:"description,omitempty" json:"description,omitempty"`
|
||||
Labels []string `yaml:"labels,omitempty" json:"labels,omitempty"`
|
||||
Priority string `yaml:"priority,omitempty" json:"priority,omitempty"`
|
||||
IssueType string `yaml:"issue_type,omitempty" json:"issue_type,omitempty"`
|
||||
Project string `yaml:"project,omitempty" json:"project,omitempty"`
|
||||
Summary JiraFieldConfig `yaml:"summary,omitempty" json:"summary,omitempty"`
|
||||
Description JiraFieldConfig `yaml:"description,omitempty" json:"description,omitempty"`
|
||||
Labels []string `yaml:"labels,omitempty" json:"labels,omitempty"`
|
||||
Priority string `yaml:"priority,omitempty" json:"priority,omitempty"`
|
||||
IssueType string `yaml:"issue_type,omitempty" json:"issue_type,omitempty"`
|
||||
|
||||
ReopenTransition string `yaml:"reopen_transition,omitempty" json:"reopen_transition,omitempty"`
|
||||
ResolveTransition string `yaml:"resolve_transition,omitempty" json:"resolve_transition,omitempty"`
|
||||
@@ -991,6 +1002,28 @@ type JiraConfig struct {
|
||||
Fields map[string]any `yaml:"fields,omitempty" json:"custom_fields,omitempty"`
|
||||
}
|
||||
|
||||
func (f *JiraFieldConfig) EnableUpdateValue() bool {
|
||||
if f.EnableUpdate == nil {
|
||||
return true
|
||||
}
|
||||
return *f.EnableUpdate
|
||||
}
|
||||
|
||||
// Supports both the legacy string and the new object form.
|
||||
func (f *JiraFieldConfig) UnmarshalYAML(unmarshal func(any) error) error {
|
||||
// Try simple string first (backward compatibility).
|
||||
var s string
|
||||
if err := unmarshal(&s); err == nil {
|
||||
f.Template = s
|
||||
// DisableUpdate stays false by default.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Fallback to full object form.
|
||||
type plain JiraFieldConfig
|
||||
return unmarshal((*plain)(f))
|
||||
}
|
||||
|
||||
func (c *JiraConfig) UnmarshalYAML(unmarshal func(any) error) error {
|
||||
*c = DefaultJiraConfig
|
||||
type plain JiraConfig
|
||||
|
||||
@@ -1159,11 +1159,23 @@ The default `jira.default.description` template only works with V2.
|
||||
# The project key where issues are created.
|
||||
project: <string>
|
||||
|
||||
# Issue summary template.
|
||||
[ summary: <tmpl_string> | default = '{{ template "jira.default.summary" . }}' ]
|
||||
# Issue summary configuration.
|
||||
[ summary:
|
||||
# Template for the issue summary.
|
||||
[ template: <tmpl_string> | default = '{{ template "jira.default.summary" . }}' ]
|
||||
|
||||
# If true, the summary will not be updated when updating an existing issue.
|
||||
[ enable_update: <boolean> | default = false ]
|
||||
]
|
||||
|
||||
# Issue description template.
|
||||
[ description: <tmpl_string> | default = '{{ template "jira.default.description" . }}' ]
|
||||
# Issue description configuration.
|
||||
[ description:
|
||||
# Template for the issue description.
|
||||
[ template: <tmpl_string> | default = '{{ template "jira.default.description" . }}' ]
|
||||
|
||||
# If true, the description will not be updated when updating an existing issue.
|
||||
[ enable_update: <boolean> | default = false ]
|
||||
]
|
||||
|
||||
# Labels to be added to the issue.
|
||||
labels:
|
||||
|
||||
@@ -103,8 +103,7 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error)
|
||||
} else {
|
||||
path = "issue/" + existingIssue.Key
|
||||
method = http.MethodPut
|
||||
|
||||
logger.Debug("updating existing issue", "issue_key", existingIssue.Key)
|
||||
logger.Debug("updating existing issue", "issue_key", existingIssue.Key, "summary_update_enabled", n.conf.Summary.EnableUpdateValue(), "description_update_enabled", n.conf.Description.EnableUpdateValue())
|
||||
}
|
||||
|
||||
requestBody, err := n.prepareIssueRequestBody(ctx, logger, key.Hash(), tmplTextFunc)
|
||||
@@ -112,6 +111,15 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error)
|
||||
return false, err
|
||||
}
|
||||
|
||||
if method == http.MethodPut && requestBody.Fields != nil {
|
||||
if !n.conf.Description.EnableUpdateValue() {
|
||||
requestBody.Fields.Description = nil
|
||||
}
|
||||
if !n.conf.Summary.EnableUpdateValue() {
|
||||
requestBody.Fields.Summary = nil
|
||||
}
|
||||
}
|
||||
|
||||
_, shouldRetry, err = n.doAPIRequest(ctx, method, path, requestBody)
|
||||
if err != nil {
|
||||
return shouldRetry, fmt.Errorf("failed to %s request to %q: %w", method, path, err)
|
||||
@@ -121,10 +129,11 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error)
|
||||
}
|
||||
|
||||
func (n *Notifier) prepareIssueRequestBody(_ context.Context, logger *slog.Logger, groupID string, tmplTextFunc template.TemplateFunc) (issue, error) {
|
||||
summary, err := tmplTextFunc(n.conf.Summary)
|
||||
summary, err := tmplTextFunc(n.conf.Summary.Template)
|
||||
if err != nil {
|
||||
return issue{}, fmt.Errorf("summary template: %w", err)
|
||||
}
|
||||
|
||||
project, err := tmplTextFunc(n.conf.Project)
|
||||
if err != nil {
|
||||
return issue{}, fmt.Errorf("project template: %w", err)
|
||||
@@ -156,12 +165,12 @@ func (n *Notifier) prepareIssueRequestBody(_ context.Context, logger *slog.Logge
|
||||
requestBody := issue{Fields: &issueFields{
|
||||
Project: &issueProject{Key: project},
|
||||
Issuetype: &idNameValue{Name: issueType},
|
||||
Summary: summary,
|
||||
Summary: &summary,
|
||||
Labels: make([]string, 0, len(n.conf.Labels)+1),
|
||||
Fields: fieldsWithStringKeys,
|
||||
}}
|
||||
|
||||
issueDescriptionString, err := tmplTextFunc(n.conf.Description)
|
||||
issueDescriptionString, err := tmplTextFunc(n.conf.Description.Template)
|
||||
if err != nil {
|
||||
return issue{}, fmt.Errorf("description template: %w", err)
|
||||
}
|
||||
@@ -171,14 +180,13 @@ func (n *Notifier) prepareIssueRequestBody(_ context.Context, logger *slog.Logge
|
||||
logger.Warn("Truncated description", "max_runes", maxDescriptionLenRunes)
|
||||
}
|
||||
|
||||
requestBody.Fields.Description = issueDescriptionString
|
||||
if strings.HasSuffix(n.conf.APIURL.Path, "/3") {
|
||||
var issueDescription any
|
||||
if err := json.Unmarshal([]byte(issueDescriptionString), &issueDescription); err != nil {
|
||||
return issue{}, fmt.Errorf("description unmarshaling: %w", err)
|
||||
descriptionCopy := issueDescriptionString
|
||||
if isAPIv3Path(n.conf.APIURL.Path) {
|
||||
if !json.Valid([]byte(descriptionCopy)) {
|
||||
return issue{}, fmt.Errorf("description template: invalid JSON for API v3")
|
||||
}
|
||||
requestBody.Fields.Description = issueDescription
|
||||
}
|
||||
requestBody.Fields.Description = &descriptionCopy
|
||||
|
||||
for i, label := range n.conf.Labels {
|
||||
label, err = tmplTextFunc(label)
|
||||
@@ -395,3 +403,7 @@ func (n *Notifier) doAPIRequestFullPath(ctx context.Context, method, path string
|
||||
|
||||
return responseBody, false, nil
|
||||
}
|
||||
|
||||
func isAPIv3Path(path string) bool {
|
||||
return strings.HasSuffix(strings.TrimRight(path, "/"), "/3")
|
||||
}
|
||||
|
||||
@@ -36,6 +36,14 @@ import (
|
||||
"github.com/prometheus/alertmanager/types"
|
||||
)
|
||||
|
||||
func stringPtr(v string) *string {
|
||||
return &v
|
||||
}
|
||||
|
||||
func boolPtr(v bool) *bool {
|
||||
return &v
|
||||
}
|
||||
|
||||
func TestJiraRetry(t *testing.T) {
|
||||
notifier, err := New(
|
||||
&config.JiraConfig{
|
||||
@@ -109,8 +117,8 @@ func TestSearchExistingIssue(t *testing.T) {
|
||||
{
|
||||
title: "search existing issue with project template for firing alert",
|
||||
cfg: &config.JiraConfig{
|
||||
Summary: `{{ template "jira.default.summary" . }}`,
|
||||
Description: `{{ template "jira.default.description" . }}`,
|
||||
Summary: config.JiraFieldConfig{Template: `{{ template "jira.default.summary" . }}`},
|
||||
Description: config.JiraFieldConfig{Template: `{{ template "jira.default.description" . }}`},
|
||||
Project: `{{ .CommonLabels.project }}`,
|
||||
},
|
||||
groupKey: "1",
|
||||
@@ -120,8 +128,8 @@ func TestSearchExistingIssue(t *testing.T) {
|
||||
{
|
||||
title: "search existing issue with reopen duration for firing alert",
|
||||
cfg: &config.JiraConfig{
|
||||
Summary: `{{ template "jira.default.summary" . }}`,
|
||||
Description: `{{ template "jira.default.description" . }}`,
|
||||
Summary: config.JiraFieldConfig{Template: `{{ template "jira.default.summary" . }}`},
|
||||
Description: config.JiraFieldConfig{Template: `{{ template "jira.default.description" . }}`},
|
||||
Project: `{{ .CommonLabels.project }}`,
|
||||
ReopenDuration: model.Duration(60 * time.Minute),
|
||||
ReopenTransition: "REOPEN",
|
||||
@@ -133,8 +141,8 @@ func TestSearchExistingIssue(t *testing.T) {
|
||||
{
|
||||
title: "search existing issue for resolved alert",
|
||||
cfg: &config.JiraConfig{
|
||||
Summary: `{{ template "jira.default.summary" . }}`,
|
||||
Description: `{{ template "jira.default.description" . }}`,
|
||||
Summary: config.JiraFieldConfig{Template: `{{ template "jira.default.summary" . }}`},
|
||||
Description: config.JiraFieldConfig{Template: `{{ template "jira.default.description" . }}`},
|
||||
Project: `{{ .CommonLabels.project }}`,
|
||||
},
|
||||
groupKey: "1",
|
||||
@@ -347,8 +355,8 @@ func TestJiraTemplating(t *testing.T) {
|
||||
{
|
||||
title: "full-blown message with templated custom field",
|
||||
cfg: &config.JiraConfig{
|
||||
Summary: `{{ template "jira.default.summary" . }}`,
|
||||
Description: `{{ template "jira.default.description" . }}`,
|
||||
Summary: config.JiraFieldConfig{Template: `{{ template "jira.default.summary" . }}`},
|
||||
Description: config.JiraFieldConfig{Template: `{{ template "jira.default.description" . }}`},
|
||||
Fields: map[string]any{
|
||||
"customfield_14400": `{{ template "jira.host" . }}`,
|
||||
},
|
||||
@@ -361,8 +369,8 @@ func TestJiraTemplating(t *testing.T) {
|
||||
title: "template project",
|
||||
cfg: &config.JiraConfig{
|
||||
Project: `{{ .CommonLabels.lbl1 }}`,
|
||||
Summary: `{{ template "jira.default.summary" . }}`,
|
||||
Description: `{{ template "jira.default.description" . }}`,
|
||||
Summary: config.JiraFieldConfig{Template: `{{ template "jira.default.summary" . }}`},
|
||||
Description: config.JiraFieldConfig{Template: `{{ template "jira.default.description" . }}`},
|
||||
},
|
||||
retry: false,
|
||||
},
|
||||
@@ -370,31 +378,31 @@ func TestJiraTemplating(t *testing.T) {
|
||||
title: "template issue type",
|
||||
cfg: &config.JiraConfig{
|
||||
IssueType: `{{ .CommonLabels.lbl1 }}`,
|
||||
Summary: `{{ template "jira.default.summary" . }}`,
|
||||
Description: `{{ template "jira.default.description" . }}`,
|
||||
Summary: config.JiraFieldConfig{Template: `{{ template "jira.default.summary" . }}`},
|
||||
Description: config.JiraFieldConfig{Template: `{{ template "jira.default.description" . }}`},
|
||||
},
|
||||
retry: false,
|
||||
},
|
||||
{
|
||||
title: "summary with templating errors",
|
||||
cfg: &config.JiraConfig{
|
||||
Summary: "{{ ",
|
||||
Summary: config.JiraFieldConfig{Template: "{{ "},
|
||||
},
|
||||
errMsg: "template: :1: unclosed action",
|
||||
},
|
||||
{
|
||||
title: "description with templating errors",
|
||||
cfg: &config.JiraConfig{
|
||||
Summary: `{{ template "jira.default.summary" . }}`,
|
||||
Description: "{{ ",
|
||||
Summary: config.JiraFieldConfig{Template: `{{ template "jira.default.summary" . }}`},
|
||||
Description: config.JiraFieldConfig{Template: "{{ "},
|
||||
},
|
||||
errMsg: "template: :1: unclosed action",
|
||||
},
|
||||
{
|
||||
title: "priority with templating errors",
|
||||
cfg: &config.JiraConfig{
|
||||
Summary: `{{ template "jira.default.summary" . }}`,
|
||||
Description: `{{ template "jira.default.description" . }}`,
|
||||
Summary: config.JiraFieldConfig{Template: `{{ template "jira.default.summary" . }}`},
|
||||
Description: config.JiraFieldConfig{Template: `{{ template "jira.default.description" . }}`},
|
||||
Priority: "{{ ",
|
||||
},
|
||||
errMsg: "template: :1: unclosed action",
|
||||
@@ -467,8 +475,8 @@ func TestJiraNotify(t *testing.T) {
|
||||
{
|
||||
title: "create new issue",
|
||||
cfg: &config.JiraConfig{
|
||||
Summary: `{{ template "jira.default.summary" . }}`,
|
||||
Description: `{{ template "jira.default.description" . }}`,
|
||||
Summary: config.JiraFieldConfig{Template: `{{ template "jira.default.summary" . }}`},
|
||||
Description: config.JiraFieldConfig{Template: `{{ template "jira.default.description" . }}`},
|
||||
IssueType: "Incident",
|
||||
Project: "OPS",
|
||||
Priority: `{{ template "jira.default.priority" . }}`,
|
||||
@@ -495,8 +503,8 @@ func TestJiraNotify(t *testing.T) {
|
||||
issue: issue{
|
||||
Key: "",
|
||||
Fields: &issueFields{
|
||||
Summary: "[FIRING:1] test (vm1 critical)",
|
||||
Description: "\n\n# Alerts Firing:\n\nLabels:\n - alertname = test\n - instance = vm1\n - severity = critical\n\nAnnotations:\n\nSource: \n\n\n\n\n",
|
||||
Summary: stringPtr("[FIRING:1] test (vm1 critical)"),
|
||||
Description: stringPtr("\n\n# Alerts Firing:\n\nLabels:\n - alertname = test\n - instance = vm1\n - severity = critical\n\nAnnotations:\n\nSource: \n\n\n\n\n"),
|
||||
Issuetype: &idNameValue{Name: "Incident"},
|
||||
Labels: []string{"ALERT{6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b}", "alertmanager", "test"},
|
||||
Project: &issueProject{Key: "OPS"},
|
||||
@@ -506,11 +514,82 @@ func TestJiraNotify(t *testing.T) {
|
||||
customFieldAssetFn: func(t *testing.T, issue map[string]any) {},
|
||||
errMsg: "",
|
||||
},
|
||||
{
|
||||
title: "update existing issue with disabled summary and description",
|
||||
cfg: &config.JiraConfig{
|
||||
Summary: config.JiraFieldConfig{
|
||||
Template: `{{ template "jira.default.summary" . }}`,
|
||||
EnableUpdate: boolPtr(false),
|
||||
},
|
||||
Description: config.JiraFieldConfig{
|
||||
Template: `{{ template "jira.default.description" . }}`,
|
||||
EnableUpdate: boolPtr(false),
|
||||
},
|
||||
IssueType: "{{ .CommonLabels.issue_type }}",
|
||||
Project: "{{ .CommonLabels.project }}",
|
||||
Priority: `{{ template "jira.default.priority" . }}`,
|
||||
Labels: []string{"alertmanager", "{{ .GroupLabels.alertname }}"},
|
||||
ReopenDuration: model.Duration(1 * time.Hour),
|
||||
ReopenTransition: "REOPEN",
|
||||
ResolveTransition: "CLOSE",
|
||||
WontFixResolution: "WONTFIX",
|
||||
},
|
||||
alert: &types.Alert{
|
||||
Alert: model.Alert{
|
||||
Labels: model.LabelSet{
|
||||
"alertname": "test",
|
||||
"instance": "vm1",
|
||||
"severity": "critical",
|
||||
"project": "MONITORING",
|
||||
"issue_type": "MINOR",
|
||||
},
|
||||
StartsAt: time.Now(),
|
||||
EndsAt: time.Now().Add(time.Hour),
|
||||
},
|
||||
},
|
||||
searchResponse: issueSearchResult{
|
||||
Issues: []issue{
|
||||
{
|
||||
Key: "MONITORING-1",
|
||||
Fields: &issueFields{
|
||||
Summary: stringPtr("Original Summary"),
|
||||
Description: stringPtr("Original Description"),
|
||||
Status: &issueStatus{
|
||||
Name: "Open",
|
||||
StatusCategory: struct {
|
||||
Key string `json:"key"`
|
||||
}{
|
||||
Key: "open",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
issue: issue{
|
||||
Key: "MONITORING-1",
|
||||
Fields: &issueFields{
|
||||
// Summary and Description should NOT be present in the update request
|
||||
Issuetype: &idNameValue{Name: "MINOR"},
|
||||
Labels: []string{"ALERT{6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b}", "alertmanager", "test"},
|
||||
Project: &issueProject{Key: "MONITORING"},
|
||||
Priority: &idNameValue{Name: "High"},
|
||||
},
|
||||
},
|
||||
customFieldAssetFn: func(t *testing.T, issue map[string]any) {
|
||||
// Verify that summary and description are NOT in the update request
|
||||
_, hasSummary := issue["summary"]
|
||||
_, hasDescription := issue["description"]
|
||||
require.False(t, hasSummary, "summary should not be present in update request")
|
||||
require.False(t, hasDescription, "description should not be present in update request")
|
||||
},
|
||||
errMsg: "",
|
||||
},
|
||||
{
|
||||
title: "create new issue with template project and issue type",
|
||||
cfg: &config.JiraConfig{
|
||||
Summary: `{{ template "jira.default.summary" . }}`,
|
||||
Description: `{{ template "jira.default.description" . }}`,
|
||||
Summary: config.JiraFieldConfig{Template: `{{ template "jira.default.summary" . }}`},
|
||||
Description: config.JiraFieldConfig{Template: `{{ template "jira.default.description" . }}`},
|
||||
IssueType: "{{ .CommonLabels.issue_type }}",
|
||||
Project: "{{ .CommonLabels.project }}",
|
||||
Priority: `{{ template "jira.default.priority" . }}`,
|
||||
@@ -539,8 +618,8 @@ func TestJiraNotify(t *testing.T) {
|
||||
issue: issue{
|
||||
Key: "",
|
||||
Fields: &issueFields{
|
||||
Summary: "[FIRING:1] test (vm1 MINOR MONITORING critical)",
|
||||
Description: "\n\n# Alerts Firing:\n\nLabels:\n - alertname = test\n - instance = vm1\n - issue_type = MINOR\n - project = MONITORING\n - severity = critical\n\nAnnotations:\n\nSource: \n\n\n\n\n",
|
||||
Summary: stringPtr("[FIRING:1] test (vm1 MINOR MONITORING critical)"),
|
||||
Description: stringPtr("\n\n# Alerts Firing:\n\nLabels:\n - alertname = test\n - instance = vm1\n - issue_type = MINOR\n - project = MONITORING\n - severity = critical\n\nAnnotations:\n\nSource: \n\n\n\n\n"),
|
||||
Issuetype: &idNameValue{Name: "MINOR"},
|
||||
Labels: []string{"ALERT{6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b}", "alertmanager", "test"},
|
||||
Project: &issueProject{Key: "MONITORING"},
|
||||
@@ -553,8 +632,8 @@ func TestJiraNotify(t *testing.T) {
|
||||
{
|
||||
title: "create new issue with custom field and too long summary",
|
||||
cfg: &config.JiraConfig{
|
||||
Summary: strings.Repeat("A", maxSummaryLenRunes+10),
|
||||
Description: `{{ template "jira.default.description" . }}`,
|
||||
Summary: config.JiraFieldConfig{Template: strings.Repeat("A", maxSummaryLenRunes+10)},
|
||||
Description: config.JiraFieldConfig{Template: `{{ template "jira.default.description" . }}`},
|
||||
IssueType: "Incident",
|
||||
Project: "OPS",
|
||||
Priority: `{{ template "jira.default.priority" . }}`,
|
||||
@@ -591,8 +670,8 @@ func TestJiraNotify(t *testing.T) {
|
||||
issue: issue{
|
||||
Key: "",
|
||||
Fields: &issueFields{
|
||||
Summary: strings.Repeat("A", maxSummaryLenRunes-1) + "…",
|
||||
Description: "\n\n# Alerts Firing:\n\nLabels:\n - alertname = test\n - instance = vm1\n\nAnnotations:\n\nSource: \n\n\n\n\n",
|
||||
Summary: stringPtr(strings.Repeat("A", maxSummaryLenRunes-1) + "…"),
|
||||
Description: stringPtr("\n\n# Alerts Firing:\n\nLabels:\n - alertname = test\n - instance = vm1\n\nAnnotations:\n\nSource: \n\n\n\n\n"),
|
||||
Issuetype: &idNameValue{Name: "Incident"},
|
||||
Labels: []string{"ALERT{6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b}", "alertmanager", "test"},
|
||||
Project: &issueProject{Key: "OPS"},
|
||||
@@ -613,8 +692,8 @@ func TestJiraNotify(t *testing.T) {
|
||||
{
|
||||
title: "reopen issue",
|
||||
cfg: &config.JiraConfig{
|
||||
Summary: `{{ template "jira.default.summary" . }}`,
|
||||
Description: `{{ template "jira.default.description" . }}`,
|
||||
Summary: config.JiraFieldConfig{Template: `{{ template "jira.default.summary" . }}`},
|
||||
Description: config.JiraFieldConfig{Template: `{{ template "jira.default.description" . }}`},
|
||||
IssueType: "Incident",
|
||||
Project: "OPS",
|
||||
Priority: `{{ template "jira.default.priority" . }}`,
|
||||
@@ -654,8 +733,8 @@ func TestJiraNotify(t *testing.T) {
|
||||
issue: issue{
|
||||
Key: "",
|
||||
Fields: &issueFields{
|
||||
Summary: "[FIRING:1] test (vm1)",
|
||||
Description: "\n\n# Alerts Firing:\n\nLabels:\n - alertname = test\n - instance = vm1\n\nAnnotations:\n\nSource: \n\n\n\n\n",
|
||||
Summary: stringPtr("[FIRING:1] test (vm1)"),
|
||||
Description: stringPtr("\n\n# Alerts Firing:\n\nLabels:\n - alertname = test\n - instance = vm1\n\nAnnotations:\n\nSource: \n\n\n\n\n"),
|
||||
Issuetype: &idNameValue{Name: "Incident"},
|
||||
Labels: []string{"ALERT{6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b}", "alertmanager", "test"},
|
||||
Project: &issueProject{Key: "OPS"},
|
||||
@@ -668,8 +747,8 @@ func TestJiraNotify(t *testing.T) {
|
||||
{
|
||||
title: "error resolve transition not found",
|
||||
cfg: &config.JiraConfig{
|
||||
Summary: `{{ template "jira.default.summary" . }}`,
|
||||
Description: `{{ template "jira.default.description" . }}`,
|
||||
Summary: config.JiraFieldConfig{Template: `{{ template "jira.default.summary" . }}`},
|
||||
Description: config.JiraFieldConfig{Template: `{{ template "jira.default.description" . }}`},
|
||||
IssueType: "Incident",
|
||||
Project: "OPS",
|
||||
Priority: `{{ template "jira.default.priority" . }}`,
|
||||
@@ -709,8 +788,8 @@ func TestJiraNotify(t *testing.T) {
|
||||
issue: issue{
|
||||
Key: "",
|
||||
Fields: &issueFields{
|
||||
Summary: "[RESOLVED] test (vm1)",
|
||||
Description: "\n\n\n# Alerts Resolved:\n\nLabels:\n - alertname = test\n - instance = vm1\n\nAnnotations:\n\nSource: \n\n\n\n",
|
||||
Summary: stringPtr("[RESOLVED] test (vm1)"),
|
||||
Description: stringPtr("\n\n\n# Alerts Resolved:\n\nLabels:\n - alertname = test\n - instance = vm1\n\nAnnotations:\n\nSource: \n\n\n\n"),
|
||||
Issuetype: &idNameValue{Name: "Incident"},
|
||||
Labels: []string{"ALERT{6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b}", "alertmanager", "test"},
|
||||
Project: &issueProject{Key: "OPS"},
|
||||
@@ -722,8 +801,8 @@ func TestJiraNotify(t *testing.T) {
|
||||
{
|
||||
title: "error reopen transition not found",
|
||||
cfg: &config.JiraConfig{
|
||||
Summary: `{{ template "jira.default.summary" . }}`,
|
||||
Description: `{{ template "jira.default.description" . }}`,
|
||||
Summary: config.JiraFieldConfig{Template: `{{ template "jira.default.summary" . }}`},
|
||||
Description: config.JiraFieldConfig{Template: `{{ template "jira.default.description" . }}`},
|
||||
IssueType: "Incident",
|
||||
Project: "OPS",
|
||||
Priority: `{{ template "jira.default.priority" . }}`,
|
||||
@@ -763,8 +842,8 @@ func TestJiraNotify(t *testing.T) {
|
||||
issue: issue{
|
||||
Key: "",
|
||||
Fields: &issueFields{
|
||||
Summary: "[FIRING:1] test (vm1)",
|
||||
Description: "\n\n# Alerts Firing:\n\nLabels:\n - alertname = test\n - instance = vm1\n\nAnnotations:\n\nSource: \n\n\n\n\n",
|
||||
Summary: stringPtr("[FIRING:1] test (vm1)"),
|
||||
Description: stringPtr("\n\n# Alerts Firing:\n\nLabels:\n - alertname = test\n - instance = vm1\n\nAnnotations:\n\nSource: \n\n\n\n\n"),
|
||||
Issuetype: &idNameValue{Name: "Incident"},
|
||||
Labels: []string{"ALERT{6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b}", "alertmanager", "test"},
|
||||
Project: &issueProject{Key: "OPS"},
|
||||
@@ -861,10 +940,28 @@ func TestJiraNotify(t *testing.T) {
|
||||
t.Fatalf("unexpected method %s", r.Method)
|
||||
}
|
||||
|
||||
return
|
||||
case "/issue/MONITORING-1":
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var raw map[string]any
|
||||
if err := json.Unmarshal(body, &raw); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if fields, ok := raw["fields"].(map[string]any); ok {
|
||||
tc.customFieldAssetFn(t, fields)
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return
|
||||
case "/issue/OPS-1":
|
||||
case "/issue/OPS-2":
|
||||
case "/issue/OPS-3":
|
||||
case "/issue/OPS-4":
|
||||
fallthrough
|
||||
case "/issue":
|
||||
body, err := io.ReadAll(r.Body)
|
||||
|
||||
@@ -25,13 +25,13 @@ type issue struct {
|
||||
}
|
||||
|
||||
type issueFields struct {
|
||||
Description any `json:"description"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
Issuetype *idNameValue `json:"issuetype,omitempty"`
|
||||
Labels []string `json:"labels,omitempty"`
|
||||
Priority *idNameValue `json:"priority,omitempty"`
|
||||
Project *issueProject `json:"project,omitempty"`
|
||||
Resolution *idNameValue `json:"resolution,omitempty"`
|
||||
Summary string `json:"summary"`
|
||||
Summary *string `json:"summary,omitempty"`
|
||||
Status *issueStatus `json:"status,omitempty"`
|
||||
|
||||
Fields map[string]any `json:"-"`
|
||||
@@ -69,11 +69,15 @@ type issueTransitions struct {
|
||||
|
||||
// MarshalJSON merges the struct issueFields and issueFields.CustomField together.
|
||||
func (i issueFields) MarshalJSON() ([]byte, error) {
|
||||
jsonFields := map[string]any{
|
||||
"description": i.Description,
|
||||
"summary": i.Summary,
|
||||
jsonFields := map[string]any{}
|
||||
|
||||
if i.Summary != nil {
|
||||
jsonFields["summary"] = *i.Summary
|
||||
}
|
||||
|
||||
if i.Description != nil {
|
||||
jsonFields["description"] = *i.Description
|
||||
}
|
||||
if i.Issuetype != nil {
|
||||
jsonFields["issuetype"] = i.Issuetype
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user