-
-
Notifications
You must be signed in to change notification settings - Fork 451
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
Mqtt support #950
base: master
Are you sure you want to change the base?
Mqtt support #950
Changes from 4 commits
c4d3c13
072a872
9f51400
31e4e09
f368f3e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ import ( | |
"crypto/tls" | ||
"crypto/x509" | ||
"errors" | ||
"github.com/TwiN/gatus/v5/config/endpoint/mqtt" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be separate from the standard library dependencies, namely, at the bottom. See other go files for example. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let me know/ping me when you resolved it 👀 |
||
"io" | ||
"net/http" | ||
"strings" | ||
|
@@ -280,9 +281,10 @@ func TestEndpoint_IsEnabled(t *testing.T) { | |
|
||
func TestEndpoint_Type(t *testing.T) { | ||
type args struct { | ||
URL string | ||
DNS *dns.Config | ||
SSH *ssh.Config | ||
URL string | ||
DNS *dns.Config | ||
SSH *ssh.Config | ||
MQTT *mqtt.Config | ||
} | ||
tests := []struct { | ||
args args | ||
|
@@ -298,6 +300,15 @@ func TestEndpoint_Type(t *testing.T) { | |
}, | ||
want: TypeDNS, | ||
}, | ||
{ | ||
args: args{ | ||
URL: "wss://example.com/mqtt", | ||
MQTT: &mqtt.Config{ | ||
Topic: "my_topic", | ||
}, | ||
}, | ||
want: TypeMQTT, | ||
}, | ||
{ | ||
args: args{ | ||
URL: "tcp://127.0.0.1:6379", | ||
|
@@ -540,6 +551,57 @@ func TestEndpoint_ValidateAndSetDefaultsWithSSH(t *testing.T) { | |
} | ||
} | ||
|
||
func TestEndpoint_ValidateAndSetDefaultsWithMQTT(t *testing.T) { | ||
scenarios := []struct { | ||
name string | ||
topic string | ||
username string | ||
password string | ||
expectedErr error | ||
}{ | ||
{ | ||
name: "fail when has no topic", | ||
topic: "", | ||
username: "", | ||
password: "", | ||
expectedErr: mqtt.ErrEndpointWithoutMQTTTopic, | ||
}, | ||
{ | ||
name: "success when only topic is set", | ||
topic: "my_topic", | ||
username: "", | ||
password: "", | ||
expectedErr: nil, | ||
}, | ||
{ | ||
name: "success when all fields are set", | ||
topic: "my_topic", | ||
username: "username", | ||
password: "password", | ||
expectedErr: nil, | ||
}, | ||
} | ||
|
||
for _, scenario := range scenarios { | ||
t.Run(scenario.name, func(t *testing.T) { | ||
endpoint := &Endpoint{ | ||
Name: "mqtt-test", | ||
URL: "https://example.com", | ||
MQTTConfig: &mqtt.Config{ | ||
Topic: scenario.topic, | ||
Username: scenario.username, | ||
Password: scenario.password, | ||
}, | ||
Conditions: []Condition{Condition("[STATUS] == 0")}, | ||
} | ||
err := endpoint.ValidateAndSetDefaults() | ||
if !errors.Is(err, scenario.expectedErr) { | ||
t.Errorf("expected error %v, got %v", scenario.expectedErr, err) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestEndpoint_ValidateAndSetDefaultsWithSimpleErrors(t *testing.T) { | ||
scenarios := []struct { | ||
endpoint *Endpoint | ||
|
@@ -787,6 +849,42 @@ func TestIntegrationEvaluateHealthForDNS(t *testing.T) { | |
} | ||
} | ||
|
||
func TestIntegrationEvaluateHealthForMQTT(t *testing.T) { | ||
scenarios := []struct { | ||
name string | ||
endpoint Endpoint | ||
conditions []Condition | ||
success bool | ||
}{ | ||
{ | ||
name: "mqtt-failure", | ||
endpoint: Endpoint{ | ||
Name: "mqtt-failure", | ||
URL: "wss://example.com/mqtt", | ||
MQTTConfig: &mqtt.Config{ | ||
Topic: "my_topic", | ||
Username: "gatus", | ||
Password: "", | ||
}, | ||
Body: "This is a test: {{ uuidv4 }}", | ||
}, | ||
conditions: []Condition{Condition("[CONNECTED] == true")}, | ||
success: false, | ||
}, | ||
} | ||
|
||
for _, scenario := range scenarios { | ||
t.Run(scenario.name, func(t *testing.T) { | ||
scenario.endpoint.ValidateAndSetDefaults() | ||
scenario.endpoint.Conditions = scenario.conditions | ||
result := scenario.endpoint.EvaluateHealth() | ||
if result.Success != scenario.success { | ||
t.Errorf("Expected success to be %v, but was %v", scenario.success, result.Success) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestIntegrationEvaluateHealthForSSH(t *testing.T) { | ||
scenarios := []struct { | ||
name string | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package mqtt | ||
|
||
import ( | ||
"errors" | ||
) | ||
|
||
var ( | ||
// ErrEndpointWithoutMQTTTopic is the error with which Gatus will panic if an endpoint with MQTT monitoring is configured without a topic. | ||
ErrEndpointWithoutMQTTTopic = errors.New("you must specify a topic for each MQTT endpoint") | ||
) | ||
|
||
type Config struct { | ||
Topic string `yaml:"topic,omitempty"` | ||
Username string `yaml:"username,omitempty"` | ||
Password string `yaml:"password,omitempty"` | ||
} | ||
|
||
// Validate the SSH configuration | ||
func (cfg *Config) Validate() error { | ||
if len(cfg.Topic) == 0 { | ||
return ErrEndpointWithoutMQTTTopic | ||
} | ||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package mqtt | ||
|
||
import ( | ||
"errors" | ||
"testing" | ||
) | ||
|
||
func TestMQTT_validate(t *testing.T) { | ||
cfg := &Config{} | ||
if err := cfg.Validate(); err == nil { | ||
t.Error("expected an error") | ||
} else if !errors.Is(err, ErrEndpointWithoutMQTTTopic) { | ||
t.Errorf("expected error to be '%v', got '%v'", ErrEndpointWithoutMQTTTopic, err) | ||
} | ||
cfg.Username = "username" | ||
if err := cfg.Validate(); err != nil { | ||
t.Errorf("expected no error, got '%v'", err) | ||
} | ||
cfg.Password = "password" | ||
if err := cfg.Validate(); err != nil { | ||
t.Errorf("expected no error, got '%v'", err) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only think I'm unsure about is the fact that this uses a templating engine, while everything else leverages placeholder and functions. Maybe this should be exposed as a placeholder instead? I feel supporting
[UUID4]
globally (and not just for MQTT) would be more beneficial, albeit more complicated.The UUID4 placeholder should also be unique per endpoint, meaning that if it's in the url and the body, it should be the same. Let me know if that doesn't make sense, I can give you some examples
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense. I will look around and see if I can just figure it out.