Skip to content

Commit a509c14

Browse files
jkroepkesimonpasquierSuperQ
authored
jira integration (#3590)
* Initial jira integration Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de> Signed-off-by: Jan-Otto Kröpke <github@jkroepke.de> Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de> Co-authored-by: Simon Pasquier <spasquie@redhat.com> Co-authored-by: Ben Kochie <superq@gmail.com>
1 parent cad5fa5 commit a509c14

File tree

12 files changed

+1472
-2
lines changed

12 files changed

+1472
-2
lines changed

asset/assets_vfsdata.go

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/config.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,9 @@ func resolveFilepaths(baseDir string, cfg *Config) {
263263
for _, cfg := range receiver.MSTeamsConfigs {
264264
cfg.HTTPConfig.SetDirectory(baseDir)
265265
}
266+
for _, cfg := range receiver.JiraConfigs {
267+
cfg.HTTPConfig.SetDirectory(baseDir)
268+
}
266269
}
267270
}
268271

@@ -548,6 +551,17 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
548551
return fmt.Errorf("no msteams webhook URL or URLFile provided")
549552
}
550553
}
554+
for _, jira := range rcv.JiraConfigs {
555+
if jira.HTTPConfig == nil {
556+
jira.HTTPConfig = c.Global.HTTPConfig
557+
}
558+
if jira.APIURL == nil {
559+
if c.Global.JiraAPIURL == nil {
560+
return fmt.Errorf("no global Jira Cloud URL set")
561+
}
562+
jira.APIURL = c.Global.JiraAPIURL
563+
}
564+
}
551565

552566
names[rcv.Name] = struct{}{}
553567
}
@@ -752,6 +766,7 @@ type GlobalConfig struct {
752766

753767
HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`
754768

769+
JiraAPIURL *URL `yaml:"jira_api_url,omitempty" json:"jira_api_url,omitempty"`
755770
SMTPFrom string `yaml:"smtp_from,omitempty" json:"smtp_from,omitempty"`
756771
SMTPHello string `yaml:"smtp_hello,omitempty" json:"smtp_hello,omitempty"`
757772
SMTPSmarthost HostPort `yaml:"smtp_smarthost,omitempty" json:"smtp_smarthost,omitempty"`
@@ -920,6 +935,7 @@ type Receiver struct {
920935
TelegramConfigs []*TelegramConfig `yaml:"telegram_configs,omitempty" json:"telegram_configs,omitempty"`
921936
WebexConfigs []*WebexConfig `yaml:"webex_configs,omitempty" json:"webex_configs,omitempty"`
922937
MSTeamsConfigs []*MSTeamsConfig `yaml:"msteams_configs,omitempty" json:"msteams_configs,omitempty"`
938+
JiraConfigs []*JiraConfig `yaml:"jira_configs,omitempty" json:"jira_configs,omitempty"`
923939
}
924940

925941
// UnmarshalYAML implements the yaml.Unmarshaler interface for Receiver.

config/notifiers.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"time"
2323

2424
commoncfg "github.com/prometheus/common/config"
25+
"github.com/prometheus/common/model"
2526
"github.com/prometheus/common/sigv4"
2627
)
2728

@@ -172,6 +173,15 @@ var (
172173
Summary: `{{ template "msteams.default.summary" . }}`,
173174
Text: `{{ template "msteams.default.text" . }}`,
174175
}
176+
177+
DefaultJiraConfig = JiraConfig{
178+
NotifierConfig: NotifierConfig{
179+
VSendResolved: true,
180+
},
181+
Summary: `{{ template "jira.default.summary" . }}`,
182+
Description: `{{ template "jira.default.description" . }}`,
183+
Priority: `{{ template "jira.default.priority" . }}`,
184+
}
175185
)
176186

177187
// NotifierConfig contains base options common across all notifier configurations.
@@ -825,3 +835,41 @@ func (c *MSTeamsConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
825835

826836
return nil
827837
}
838+
839+
type JiraConfig struct {
840+
NotifierConfig `yaml:",inline" json:",inline"`
841+
HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`
842+
843+
APIURL *URL `yaml:"api_url,omitempty" json:"api_url,omitempty"`
844+
845+
Project string `yaml:"project,omitempty" json:"project,omitempty"`
846+
Summary string `yaml:"summary,omitempty" json:"summary,omitempty"`
847+
Description string `yaml:"description,omitempty" json:"description,omitempty"`
848+
Labels []string `yaml:"labels,omitempty" json:"labels,omitempty"`
849+
Priority string `yaml:"priority,omitempty" json:"priority,omitempty"`
850+
IssueType string `yaml:"issue_type,omitempty" json:"issue_type,omitempty"`
851+
852+
ReopenTransition string `yaml:"reopen_transition,omitempty" json:"reopen_transition,omitempty"`
853+
ResolveTransition string `yaml:"resolve_transition,omitempty" json:"resolve_transition,omitempty"`
854+
WontFixResolution string `yaml:"wont_fix_resolution,omitempty" json:"wont_fix_resolution,omitempty"`
855+
ReopenDuration model.Duration `yaml:"reopen_duration,omitempty" json:"reopen_duration,omitempty"`
856+
857+
Fields map[string]any `yaml:"fields,omitempty" json:"custom_fields,omitempty"`
858+
}
859+
860+
func (c *JiraConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
861+
*c = DefaultJiraConfig
862+
type plain JiraConfig
863+
if err := unmarshal((*plain)(c)); err != nil {
864+
return err
865+
}
866+
867+
if c.Project == "" {
868+
return fmt.Errorf("missing project in jira_config")
869+
}
870+
if c.IssueType == "" {
871+
return fmt.Errorf("missing issue_type in jira_config")
872+
}
873+
874+
return nil
875+
}

config/receiver/receiver.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"github.com/prometheus/alertmanager/notify"
2323
"github.com/prometheus/alertmanager/notify/discord"
2424
"github.com/prometheus/alertmanager/notify/email"
25+
"github.com/prometheus/alertmanager/notify/jira"
2526
"github.com/prometheus/alertmanager/notify/msteams"
2627
"github.com/prometheus/alertmanager/notify/opsgenie"
2728
"github.com/prometheus/alertmanager/notify/pagerduty"
@@ -92,6 +93,9 @@ func BuildReceiverIntegrations(nc config.Receiver, tmpl *template.Template, logg
9293
for i, c := range nc.MSTeamsConfigs {
9394
add("msteams", i, c, func(l log.Logger) (notify.Notifier, error) { return msteams.New(c, tmpl, l, httpOpts...) })
9495
}
96+
for i, c := range nc.JiraConfigs {
97+
add("jira", i, c, func(l log.Logger) (notify.Notifier, error) { return jira.New(c, tmpl, l, httpOpts...) })
98+
}
9599

96100
if errs.Len() > 0 {
97101
return nil, &errs

docs/configuration.md

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ global:
9797
# The default TLS configuration for SMTP receivers
9898
[ smtp_tls_config: <tls_config> ]
9999

100+
# Default settings for the JIRA integration.
101+
[ jira_api_url: <string> ]
102+
100103
# The API URL to use for Slack notifications.
101104
[ slack_api_url: <secret> ]
102105
[ slack_api_url_file: <filepath> ]
@@ -697,6 +700,8 @@ email_configs:
697700
[ - <email_config>, ... ]
698701
msteams_configs:
699702
[ - <msteams_config>, ... ]
703+
jira_configs:
704+
[ - <jira_config>, ... ]
700705
opsgenie_configs:
701706
[ - <opsgenie_config>, ... ]
702707
pagerduty_configs:
@@ -944,6 +949,97 @@ Microsoft Teams notifications are sent via the [Incoming Webhooks](https://learn
944949
[ http_config: <http_config> | default = global.http_config ]
945950
```
946951

952+
### `<jira_config>`
953+
954+
JIRA notifications are sent via [JIRA Rest API v2](https://developer.atlassian.com/cloud/jira/platform/rest/v2/intro/)
955+
or [JIRA REST API v3](https://developer.atlassian.com/cloud/jira/platform/rest/v3/intro/#version).
956+
957+
Note: This integration is only tested against a Jira Cloud instance.
958+
Jira Data Center (on premise instance) can work, but it's not guaranteed.
959+
960+
Both APIs have the same feature set. The difference is that V2 supports [Wiki Markup](https://jira.atlassian.com/secure/WikiRendererHelpAction.jspa?section=all)
961+
for the issue description and V3 supports [Atlassian Document Format (ADF)](https://developer.atlassian.com/cloud/jira/platform/apis/document/structure/).
962+
The default `jira.default.description` template only works with V2.
963+
964+
```yaml
965+
# Whether to notify about resolved alerts.
966+
[ send_resolved: <boolean> | default = true ]
967+
968+
# The URL to send API requests to. The full API path must be included.
969+
# Example: https://company.atlassian.net/rest/api/2/
970+
[ api_url: <string> | default = global.jira_api_url ]
971+
972+
# The project key where issues are created.
973+
project: <string>
974+
975+
# Issue summary template.
976+
[ summary: <tmpl_string> | default = '{{ template "jira.default.summary" . }}' ]
977+
978+
# Issue description template.
979+
[ description: <tmpl_string> | default = '{{ template "jira.default.description" . }}' ]
980+
981+
# Labels to be added to the issue.
982+
labels:
983+
[ - <tmpl_string> ... ]
984+
985+
# Priority of the issue.
986+
[ priority: <tmpl_string> | default = '{{ template "jira.default.priority" . }}' ]
987+
988+
# Type of the issue (e.g. Bug).
989+
[ issue_type: <string> ]
990+
991+
# Name of the workflow transition to resolve an issue. The target status must have the category "done".
992+
# NOTE: The name of the transition can be localized and depends on the language setting of the service account.
993+
[ resolve_transition: <string> ]
994+
995+
# Name of the workflow transition to reopen an issue. The target status should not have the category "done".
996+
# NOTE: The name of the transition can be localized and depends on the language setting of the service account.
997+
[ reopen_transition: <string> ]
998+
999+
# If reopen_transition is defined, ignore issues with that resolution.
1000+
[ wont_fix_resolution: <string> ]
1001+
1002+
# If reopen_transition is defined, reopen the issue when it is not older than this value (rounded down to the nearest minute).
1003+
# The resolutiondate field is used to determine the age of the issue.
1004+
[ reopen_duration: <duration> ]
1005+
1006+
# Other issue and custom fields.
1007+
fields:
1008+
[ <string>: <jira_field> ... ]
1009+
1010+
1011+
# The HTTP client's configuration. You must use this configuration to supply the personal access token (PAT) as part of the HTTP `Authorization` header.
1012+
# For Jira Cloud, use basic_auth with the email address as the username and the PAT as the password.
1013+
# For Jira Data Center, use the 'authorization' field with 'credentials: <PAT value>'.
1014+
[ http_config: <http_config> | default = global.http_config ]
1015+
```
1016+
1017+
The `labels` field is a list of labels added to the issue. Template expressions are supported. For example:
1018+
1019+
```yaml
1020+
labels:
1021+
- 'alertmanager'
1022+
- '{{ .CommonLabels.severity }}'
1023+
```
1024+
1025+
#### `<jira_field>`
1026+
1027+
Jira issue field can have multiple types.
1028+
Depends on the field type, the values must be provided differently.
1029+
See https://developer.atlassian.com/server/jira/platform/jira-rest-api-examples/#setting-custom-field-data-for-other-field-types for further examples.
1030+
1031+
```yaml
1032+
fields:
1033+
# Components
1034+
components: { name: "Monitoring" }
1035+
# Custom Field TextField
1036+
customfield_10001: "Random text"
1037+
# Custom Field SelectList
1038+
customfield_10002: {"value": "red"}
1039+
# Custom Field MultiSelect
1040+
customfield_10003: [{"value": "red"}, {"value": "blue"}, {"value": "green"}]
1041+
```
1042+
9471043
### `<opsgenie_config>`
9481044

9491045
OpsGenie notifications are sent via the [OpsGenie API](https://docs.opsgenie.com/docs/alert-api).

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ require (
3838
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749
3939
github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546
4040
github.com/stretchr/testify v1.9.0
41+
github.com/trivago/tgo v1.0.7
4142
github.com/xlab/treeprint v1.2.0
4243
go.uber.org/atomic v1.11.0
4344
go.uber.org/automaxprocs v1.5.3

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
502502
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
503503
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
504504
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
505+
github.com/trivago/tgo v1.0.7 h1:uaWH/XIy9aWYWpjm2CU3RpcqZXmX2ysQ9/Go+d9gyrM=
506+
github.com/trivago/tgo v1.0.7/go.mod h1:w4dpD+3tzNIIiIfkWWa85w5/B77tlvdZckQ+6PkFnhc=
505507
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
506508
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
507509
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=

0 commit comments

Comments
 (0)