Skip to content

Commit

Permalink
Alerting: Add MQTT notifications receiver
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-akhmetov committed Nov 16, 2024
1 parent 85e393d commit 764f0f8
Show file tree
Hide file tree
Showing 6 changed files with 287 additions and 0 deletions.
37 changes: 37 additions & 0 deletions docs/resources/contact_point.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ resource "grafana_contact_point" "my_contact_point" {
- `googlechat` (Block Set) A contact point that sends notifications to Google Chat. (see [below for nested schema](#nestedblock--googlechat))
- `kafka` (Block Set) A contact point that publishes notifications to Apache Kafka topics. (see [below for nested schema](#nestedblock--kafka))
- `line` (Block Set) A contact point that sends notifications to LINE.me. (see [below for nested schema](#nestedblock--line))
- `mqtt` (Block Set) A contact point that sends notifications to an MQTT broker. (see [below for nested schema](#nestedblock--mqtt))
- `oncall` (Block Set) A contact point that sends notifications to Grafana On-Call. (see [below for nested schema](#nestedblock--oncall))
- `opsgenie` (Block Set) A contact point that sends notifications to OpsGenie. (see [below for nested schema](#nestedblock--opsgenie))
- `org_id` (String) The Organization ID. If not set, the Org ID defined in the provider block will be used.
Expand Down Expand Up @@ -212,6 +213,42 @@ Read-Only:
- `uid` (String) The UID of the contact point.


<a id="nestedblock--mqtt"></a>
### Nested Schema for `mqtt`

Required:

- `broker_url` (String) The URL of the MQTT broker.
- `topic` (String) The topic to publish messages to.

Optional:

- `client_id` (String) The client ID to use when connecting to the broker.
- `disable_resolve_message` (Boolean) Whether to disable sending resolve messages. Defaults to `false`.
- `message_format` (String) The format of the message to send. Supported values are `json` and `text`.
- `password` (String, Sensitive) The password to use when connecting to the broker.
- `qos` (Number) The quality of service to use when sending messages. Supported values are 0, 1, and 2. Defaults to `0`.
- `retain` (Boolean) Whether to retain messages on the broker. Defaults to `false`.
- `settings` (Map of String, Sensitive) Additional custom properties to attach to the notifier. Defaults to `map[]`.
- `tls_config` (Block Set) TLS configuration for the connection. (see [below for nested schema](#nestedblock--mqtt--tls_config))
- `username` (String) The username to use when connecting to the broker.

Read-Only:

- `uid` (String) The UID of the contact point.

<a id="nestedblock--mqtt--tls_config"></a>
### Nested Schema for `mqtt.tls_config`

Optional:

- `ca_certificate` (String, Sensitive) The CA certificate to use when verifying the server's certificate.
- `client_certificate` (String, Sensitive) The client certificate to use when connecting to the server.
- `client_key` (String, Sensitive) The client key to use when connecting to the server.
- `insecure_skip_verify` (Boolean) Whether to skip verification of the server's certificate chain and host name. Defaults to `false`.



<a id="nestedblock--oncall"></a>
### Nested Schema for `oncall`

Expand Down
17 changes: 17 additions & 0 deletions examples/resources/grafana_contact_point/_acc_receiver_types.tf
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,23 @@ resource "grafana_contact_point" "receiver_types" {
description = "description"
}

mqtt {
broker_url = "tcp://localhost:1883"
client_id = "client_id"
topic = "grafana/alerts"
message_format = "json"
username = "username"
password = "password"
qos = 1
retain = true
tls_config {
insecure_skip_verify = true
ca_certificate = "ca_cert"
client_certificate = "client_cert"
client_key = "client"
}
}

opsgenie {
url = "http://opsgenie-api"
api_key = "token"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
resource "grafana_contact_point" "receiver_types" {
name = "Receiver Types since v11.3"

mqtt {
broker_url = "tcp://localhost:1883"
client_id = "grafana"
topic = "grafana/alerts"
message_format = "json"
username = "user"
password = "password123"
qos = 1
retain = true
tls_config {
insecure_skip_verify = true
ca_certificate = "ca_cert"
client_certificate = "client_cert"
client_key = "client_key"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ var (
googleChatNotifier{},
kafkaNotifier{},
lineNotifier{},
mqttNotifier{},
oncallNotifier{},
opsGenieNotifier{},
pagerDutyNotifier{},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,184 @@ func (o lineNotifier) unpack(raw interface{}, name string) *models.EmbeddedConta
}
}

type mqttNotifier struct{}

var _ notifier = (*mqttNotifier)(nil)

func (o mqttNotifier) meta() notifierMeta {
return notifierMeta{
field: "mqtt",
typeStr: "mqtt",
desc: "A contact point that sends notifications to an MQTT broker.",
secureFields: []string{"password"},
}
}

func (o mqttNotifier) schema() *schema.Resource {
r := commonNotifierResource()
r.Schema["broker_url"] = &schema.Schema{
Type: schema.TypeString,
Required: true,
Description: "The URL of the MQTT broker.",
}
r.Schema["topic"] = &schema.Schema{
Type: schema.TypeString,
Required: true,
Description: "The topic to publish messages to.",
}
r.Schema["client_id"] = &schema.Schema{
Type: schema.TypeString,
Optional: true,
Description: "The client ID to use when connecting to the broker.",
}
r.Schema["message_format"] = &schema.Schema{
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice([]string{"json", "text"}, false),
Description: "The format of the message to send. Supported values are `json` and `text`.",
}
r.Schema["username"] = &schema.Schema{
Type: schema.TypeString,
Optional: true,
Description: "The username to use when connecting to the broker.",
}
r.Schema["password"] = &schema.Schema{
Type: schema.TypeString,
Optional: true,
Sensitive: true,
Description: "The password to use when connecting to the broker.",
}
r.Schema["qos"] = &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Default: 0,
ValidateFunc: validation.IntBetween(0, 2),
Description: "The quality of service to use when sending messages. Supported values are 0, 1, and 2.",
}
r.Schema["retain"] = &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "Whether to retain messages on the broker.",
}

r.Schema["tls_config"] = &schema.Schema{
Type: schema.TypeSet,
Optional: true,
Description: "TLS configuration for the connection.",
Elem: tlsConfig{}.schema(),
}

return r
}

func (o mqttNotifier) pack(p *models.EmbeddedContactPoint, data *schema.ResourceData) (interface{}, error) {
notifier := packCommonNotifierFields(p)
settings := p.Settings.(map[string]interface{})

packNotifierStringField(&settings, &notifier, "brokerUrl", "broker_url")
packNotifierStringField(&settings, &notifier, "topic", "topic")
packNotifierStringField(&settings, &notifier, "clientId", "client_id")
packNotifierStringField(&settings, &notifier, "messageFormat", "message_format")
packNotifierStringField(&settings, &notifier, "username", "username")
if v, ok := settings["insecureSkipVerify"]; ok && v != nil {
notifier["insecure_skip_verify"] = v.(bool)
delete(settings, "insecureSkipVerify")
}

packSecureFields(notifier, getNotifierConfigFromStateWithUID(data, o, p.UID), o.meta().secureFields)

notifier["settings"] = packSettings(p)
return notifier, nil
}

func (o mqttNotifier) unpack(raw interface{}, name string) *models.EmbeddedContactPoint {
json := raw.(map[string]interface{})
uid, disableResolve, settings := unpackCommonNotifierFields(json)

unpackNotifierStringField(&json, &settings, "broker_url", "brokerUrl")
unpackNotifierStringField(&json, &settings, "topic", "topic")
unpackNotifierStringField(&json, &settings, "client_id", "clientId")
unpackNotifierStringField(&json, &settings, "message_format", "messageFormat")
unpackNotifierStringField(&json, &settings, "username", "username")
unpackNotifierStringField(&json, &settings, "password", "password")
if v, ok := json["insecure_skip_verify"]; ok && v != nil {
settings["insecureSkipVerify"] = v.(bool)
}

return &models.EmbeddedContactPoint{
UID: uid,
Name: name,
Type: common.Ref(o.meta().typeStr),
DisableResolveMessage: disableResolve,
Settings: settings,
}
}

type tlsConfig struct{}

func (t tlsConfig) schema() *schema.Resource {
r := &schema.Resource{
Schema: make(map[string]*schema.Schema),
}

r.Schema["insecure_skip_verify"] = &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "Whether to skip verification of the server's certificate chain and host name.",
}
r.Schema["ca_certificate"] = &schema.Schema{
Type: schema.TypeString,
Optional: true,
Sensitive: true,
Description: "The CA certificate to use when verifying the server's certificate.",
}
r.Schema["client_certificate"] = &schema.Schema{
Type: schema.TypeString,
Optional: true,
Sensitive: true,
Description: "The client certificate to use when connecting to the server.",
}
r.Schema["client_key"] = &schema.Schema{
Type: schema.TypeString,
Optional: true,
Sensitive: true,
Description: "The client key to use when connecting to the server.",
}

return r
}

func (t tlsConfig) pack(p *models.EmbeddedContactPoint, data *schema.ResourceData) (interface{}, error) {
settings := p.Settings.(map[string]interface{})
tls := make(map[string]interface{})

if v, ok := settings["insecureSkipVerify"]; ok && v != nil {
tls["insecure_skip_verify"] = v.(bool)
delete(settings, "insecureSkipVerify")
}
packNotifierStringField(&settings, &tls, "caCertificate", "ca_certificate")
packNotifierStringField(&settings, &tls, "clientCertificate", "client_certificate")
packNotifierStringField(&settings, &tls, "clientKey", "client_key")

return tls, nil
}

func (t tlsConfig) unpack(raw interface{}) map[string]interface{} {
json := raw.(map[string]interface{})
tls := make(map[string]interface{})

if v, ok := json["insecure_skip_verify"]; ok && v != nil {
tls["insecureSkipVerify"] = v.(bool)
}
unpackNotifierStringField(&json, &tls, "ca_certificate", "caCertificate")
unpackNotifierStringField(&json, &tls, "client_certificate", "clientCertificate")
unpackNotifierStringField(&json, &tls, "client_key", "clientKey")

return tls
}

type oncallNotifier struct {
}

Expand Down
34 changes: 34 additions & 0 deletions internal/resources/grafana/resource_alerting_contact_point_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,40 @@ func TestAccContactPoint_notifiers10_3(t *testing.T) {
})
}

func TestAccContactPoint_notifiers11_3(t *testing.T) {
testutils.CheckOSSTestsEnabled(t, ">=11.3.0")

var points models.ContactPoints

resource.ParallelTest(t, resource.TestCase{
ProtoV5ProviderFactories: testutils.ProtoV5ProviderFactories,
// Implicitly tests deletion.
CheckDestroy: alertingContactPointCheckExists.destroyed(&points, nil),
Steps: []resource.TestStep{
// Test creation.
{
Config: testutils.TestAccExample(t, "resources/grafana_contact_point/_acc_receiver_types_11_3.tf"),
Check: resource.ComposeTestCheckFunc(
checkAlertingContactPointExistsWithLength("grafana_contact_point.receiver_types", &points, 1),
// mqtt
resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "mqtt.#", "1"),
resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "mqtt.0.broker_url", "tcp://localhost:1883"),
resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "mqtt.0.topic", "grafana/alerts"),
resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "mqtt.0.client_id", "grafana"),
resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "mqtt.0.message_format", "json"),
resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "mqtt.0.username", "user"),
resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "mqtt.0.password", "password123"),
resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "mqtt.0.qos", "1"),
resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "mqtt.0.tls_config.insecure_skip_verify", "true"),
resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "mqtt.0.tls_config.ca_certificate", "ca_cer"),
resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "mqtt.0.tls_config.client_certificate", "client_cert"),
resource.TestCheckResourceAttr("grafana_contact_point.receiver_types", "mqtt.0.tls_config.client_key", "client_key"),
),
},
},
})
}

func TestAccContactPoint_sensitiveData(t *testing.T) {
testutils.CheckOSSTestsEnabled(t, ">=9.1.0")

Expand Down

0 comments on commit 764f0f8

Please sign in to comment.