-
Notifications
You must be signed in to change notification settings - Fork 42
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Alerting: Add MQTT notifications receiver
- Loading branch information
1 parent
f2ab7c7
commit 92d508e
Showing
10 changed files
with
575 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package mqtt | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"net" | ||
"net/url" | ||
"strconv" | ||
"strings" | ||
|
||
"github.com/grafana/alerting/receivers" | ||
"github.com/grafana/alerting/templates" | ||
) | ||
|
||
type Config struct { | ||
BrokerURL string `json:"brokerUrl,omitempty" yaml:"brokerUrl,omitempty"` | ||
ClientID string `json:"clientId,omitempty" yaml:"clientId,omitempty"` | ||
Topic string `json:"topic,omitempty" yaml:"topic,omitempty"` | ||
Message string `json:"message,omitempty" yaml:"message,omitempty"` | ||
Username string `json:"username,omitempty" yaml:"username,omitempty"` | ||
Password string `json:"password,omitempty" yaml:"password,omitempty"` | ||
InsecureSkipVerify bool `json:"insecureSkipVerify,omitempty" yaml:"insecureSkipVerify,omitempty"` | ||
} | ||
|
||
func NewConfig(jsonData json.RawMessage, decryptFn receivers.DecryptFunc) (Config, error) { | ||
var settings Config | ||
err := json.Unmarshal(jsonData, &settings) | ||
if err != nil { | ||
return Config{}, fmt.Errorf("failed to unmarshal settings: %w", err) | ||
} | ||
|
||
if settings.BrokerURL == "" { | ||
return Config{}, errors.New("MQTT broker URL must be specified") | ||
} | ||
if _, err := isValidMqttURL(settings.BrokerURL); err != nil { | ||
return Config{}, fmt.Errorf("Invalid MQTT broker URL: %w", err) | ||
} | ||
|
||
if settings.Topic == "" { | ||
return Config{}, errors.New("MQTT topic must be specified") | ||
} | ||
|
||
if settings.ClientID == "" { | ||
settings.ClientID = "Grafana" | ||
} | ||
|
||
if settings.Message == "" { | ||
settings.Message = templates.DefaultMessageEmbed | ||
} | ||
|
||
password := decryptFn("password", settings.Password) | ||
settings.Password = password | ||
|
||
return settings, nil | ||
} | ||
|
||
func isValidMqttURL(mqttURL string) (bool, error) { | ||
parsedURL, err := url.Parse(mqttURL) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
if parsedURL.Scheme != "tcp" && parsedURL.Scheme != "ssl" { | ||
return false, errors.New("Invalid scheme, must be 'tcp' or 'ssl'") | ||
} | ||
|
||
host := parsedURL.Host | ||
if !strings.Contains(host, ":") { | ||
return false, errors.New("Port must be specified") | ||
} | ||
|
||
_, port, err := net.SplitHostPort(host) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
if portNum, err := strconv.ParseInt(port, 10, 32); err != nil || portNum > 65535 || portNum < 1 { | ||
return false, errors.New("Port must be a valid number between 1 and 65535") | ||
} | ||
|
||
return true, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
package mqtt | ||
|
||
import ( | ||
"encoding/json" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
|
||
receiversTesting "github.com/grafana/alerting/receivers/testing" | ||
"github.com/grafana/alerting/templates" | ||
) | ||
|
||
func TestNewConfig(t *testing.T) { | ||
cases := []struct { | ||
name string | ||
settings string | ||
secureSettings map[string][]byte | ||
expectedConfig Config | ||
expectedInitError string | ||
}{ | ||
{ | ||
name: "Error if empty", | ||
settings: "", | ||
expectedInitError: `failed to unmarshal settings`, | ||
}, | ||
{ | ||
name: "Error if broker URL is missing", | ||
settings: `{}`, | ||
expectedInitError: `MQTT broker URL must be specified`, | ||
}, | ||
{ | ||
name: "Error if topic is missing", | ||
settings: `{ "brokerUrl" : "tcp://localhost:1883" }`, | ||
expectedInitError: `MQTT topic must be specified`, | ||
}, | ||
{ | ||
name: "Error if the broker URL does not have the scheme", | ||
settings: `{ "brokerUrl" : "localhost" }`, | ||
expectedInitError: `Invalid MQTT broker URL: Invalid scheme, must be 'tcp' or 'ssl'`, | ||
}, | ||
{ | ||
name: "Error if the broker URL has invalid scheme", | ||
settings: `{ "brokerUrl" : "http://localhost" }`, | ||
expectedInitError: `Invalid MQTT broker URL: Invalid scheme, must be 'tcp' or 'ssl'`, | ||
}, | ||
{ | ||
name: "Error if the broker URL does not have the port", | ||
settings: `{ "brokerUrl" : "tcp://localhost" }`, | ||
expectedInitError: `Invalid MQTT broker URL: Port must be specified`, | ||
}, | ||
{ | ||
name: "Error if the broker URL port is invalid", | ||
settings: `{ "brokerUrl" : "tcp://localhost:100000" }`, | ||
expectedInitError: `Invalid MQTT broker URL: Port must be a valid number between 1 and 65535`, | ||
}, | ||
{ | ||
name: "Minimal valid configuration", | ||
settings: `{ "brokerUrl" : "tcp://localhost:1883", "topic": "grafana/alerts"}`, | ||
expectedConfig: Config{ | ||
Message: templates.DefaultMessageEmbed, | ||
BrokerURL: "tcp://localhost:1883", | ||
Topic: "grafana/alerts", | ||
ClientID: "Grafana", | ||
}, | ||
}, | ||
{ | ||
name: "Configuration with insecureSkipVerify", | ||
settings: `{ "brokerUrl" : "tcp://localhost:1883", "topic": "grafana/alerts", "insecureSkipVerify": true}`, | ||
expectedConfig: Config{ | ||
Message: templates.DefaultMessageEmbed, | ||
BrokerURL: "tcp://localhost:1883", | ||
Topic: "grafana/alerts", | ||
ClientID: "Grafana", | ||
InsecureSkipVerify: true, | ||
}, | ||
}, | ||
{ | ||
name: "Minimal valid configuration with secrets", | ||
settings: `{ "brokerUrl" : "tcp://localhost:1883", "topic": "grafana/alerts", "username": "grafana"}`, | ||
secureSettings: map[string][]byte{ | ||
"password": []byte("testpasswd"), | ||
}, | ||
expectedConfig: Config{ | ||
Message: templates.DefaultMessageEmbed, | ||
BrokerURL: "tcp://localhost:1883", | ||
Topic: "grafana/alerts", | ||
ClientID: "Grafana", | ||
Username: "grafana", | ||
Password: "testpasswd", | ||
}, | ||
}, | ||
} | ||
|
||
for _, c := range cases { | ||
t.Run(c.name, func(t *testing.T) { | ||
actual, err := NewConfig(json.RawMessage(c.settings), receiversTesting.DecryptForTesting(c.secureSettings)) | ||
|
||
if c.expectedInitError != "" { | ||
require.ErrorContains(t, err, c.expectedInitError) | ||
return | ||
} | ||
require.NoError(t, err) | ||
require.Equal(t, c.expectedConfig, actual) | ||
}) | ||
} | ||
} |
Oops, something went wrong.