feat: Set minimum interval for endpoints with [DOMAIN_EXPIRATION] to 5m

This commit is contained in:
TwiN 2022-09-15 21:23:14 -04:00
parent 33ce0e99b5
commit 38054f57e5
3 changed files with 75 additions and 47 deletions

View File

@ -1356,8 +1356,8 @@ endpoints:
**NOTE**: The usage of the `[DOMAIN_EXPIRATION]` placeholder requires Gatus to send a request to the official IANA WHOIS service [through a library](https://github.com/TwiN/whois)
and in some cases, a secondary request to a TLD-specific WHOIS server (e.g. `whois.nic.sh`).
You are also responsible for sending requests at a reasonable rate, as the WHOIS service may throttle your IP address if you send too many requests.
The duration taken by the WHOIS request(s) is excluded from the request's response time.
To prevent the WHOIS service from throttling your IP address if you send too many requests, Gatus will prevent you from
using the `[DOMAIN_EXPIRATION]` placeholder on an endpoint with an interval of less than `5m`.
### disable-monitoring-lock

View File

@ -58,6 +58,12 @@ var (
// ErrUnknownEndpointType is the error with which Gatus will panic if an endpoint has an unknown type
ErrUnknownEndpointType = errors.New("unknown endpoint type")
// ErrInvalidEndpointIntervalForDomainExpirationPlaceholder is the error with which Gatus will panic if an endpoint
// has both an interval smaller than 5 minutes and a condition with DomainExpirationPlaceholder.
// This is because the free whois service we are using should not be abused, especially considering the fact that
// the data takes a while to be updated.
ErrInvalidEndpointIntervalForDomainExpirationPlaceholder = errors.New("the minimum interval for an endpoint with a condition using the " + DomainExpirationPlaceholder + " placeholder is 300s (5m)")
)
// Endpoint is the configuration of a monitored
@ -191,6 +197,13 @@ func (endpoint *Endpoint) ValidateAndSetDefaults() error {
if len(endpoint.Conditions) == 0 {
return ErrEndpointWithNoCondition
}
if endpoint.Interval < 5*time.Minute {
for _, condition := range endpoint.Conditions {
if condition.hasDomainExpirationPlaceholder() {
return ErrInvalidEndpointIntervalForDomainExpirationPlaceholder
}
}
}
if endpoint.DNS != nil {
return endpoint.DNS.validateAndSetDefault()
}

View File

@ -372,11 +372,10 @@ func TestEndpoint_ValidateAndSetDefaults(t *testing.T) {
}
func TestEndpoint_ValidateAndSetDefaultsWithClientConfig(t *testing.T) {
condition := Condition("[STATUS] == 200")
endpoint := Endpoint{
Name: "website-health",
URL: "https://twin.sh/health",
Conditions: []Condition{condition},
Conditions: []Condition{Condition("[STATUS] == 200")},
ClientConfig: &client.Config{
Insecure: true,
IgnoreRedirect: true,
@ -399,51 +398,10 @@ func TestEndpoint_ValidateAndSetDefaultsWithClientConfig(t *testing.T) {
}
}
func TestEndpoint_ValidateAndSetDefaultsWithNoName(t *testing.T) {
defer func() { recover() }()
condition := Condition("[STATUS] == 200")
endpoint := &Endpoint{
Name: "",
URL: "http://example.com",
Conditions: []Condition{condition},
}
err := endpoint.ValidateAndSetDefaults()
if err == nil {
t.Fatal("Should've returned an error because endpoint didn't have a name, which is a mandatory field")
}
}
func TestEndpoint_ValidateAndSetDefaultsWithNoUrl(t *testing.T) {
defer func() { recover() }()
condition := Condition("[STATUS] == 200")
endpoint := &Endpoint{
Name: "example",
URL: "",
Conditions: []Condition{condition},
}
err := endpoint.ValidateAndSetDefaults()
if err == nil {
t.Fatal("Should've returned an error because endpoint didn't have an url, which is a mandatory field")
}
}
func TestEndpoint_ValidateAndSetDefaultsWithNoConditions(t *testing.T) {
defer func() { recover() }()
endpoint := &Endpoint{
Name: "example",
URL: "http://example.com",
Conditions: nil,
}
err := endpoint.ValidateAndSetDefaults()
if err == nil {
t.Fatal("Should've returned an error because endpoint didn't have at least 1 condition")
}
}
func TestEndpoint_ValidateAndSetDefaultsWithDNS(t *testing.T) {
endpoint := &Endpoint{
Name: "dns-test",
URL: "http://example.com",
URL: "https://example.com",
DNS: &DNS{
QueryType: "A",
QueryName: "example.com",
@ -452,13 +410,70 @@ func TestEndpoint_ValidateAndSetDefaultsWithDNS(t *testing.T) {
}
err := endpoint.ValidateAndSetDefaults()
if err != nil {
t.Error("did not expect an error, got", err)
}
if endpoint.DNS.QueryName != "example.com." {
t.Error("Endpoint.dns.query-name should be formatted with . suffix")
}
}
func TestEndpoint_ValidateAndSetDefaultsWithSimpleErrors(t *testing.T) {
scenarios := []struct {
endpoint *Endpoint
expectedErr error
}{
{
endpoint: &Endpoint{
Name: "",
URL: "https://example.com",
Conditions: []Condition{Condition("[STATUS] == 200")},
},
expectedErr: ErrEndpointWithNoName,
},
{
endpoint: &Endpoint{
Name: "endpoint-with-no-url",
URL: "",
Conditions: []Condition{Condition("[STATUS] == 200")},
},
expectedErr: ErrEndpointWithNoURL,
},
{
endpoint: &Endpoint{
Name: "endpoint-with-no-conditions",
URL: "https://example.com",
Conditions: nil,
},
expectedErr: ErrEndpointWithNoCondition,
},
{
endpoint: &Endpoint{
Name: "domain-expiration-with-bad-interval",
URL: "https://example.com",
Interval: time.Minute,
Conditions: []Condition{Condition("[DOMAIN_EXPIRATION] > 720h")},
},
expectedErr: ErrInvalidEndpointIntervalForDomainExpirationPlaceholder,
},
{
endpoint: &Endpoint{
Name: "domain-expiration-with-good-interval",
URL: "https://example.com",
Interval: 5 * time.Minute,
Conditions: []Condition{Condition("[DOMAIN_EXPIRATION] > 720h")},
},
expectedErr: nil,
},
}
for _, scenario := range scenarios {
t.Run(scenario.endpoint.Name, func(t *testing.T) {
if err := scenario.endpoint.ValidateAndSetDefaults(); err != scenario.expectedErr {
t.Errorf("Expected error %v, got %v", scenario.expectedErr, err)
}
})
}
}
func TestEndpoint_buildHTTPRequest(t *testing.T) {
condition := Condition("[STATUS] == 200")
endpoint := Endpoint{