Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions api/v1alpha1/endpointmonitor_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,45 @@ type UptimeRobotConfig struct {
// Defines which http status codes are treated as up or down
// For ex: 200:0_401:1_503:1 (to accept 200 as down and 401 and 503 as up)
CustomHTTPStatuses string `json:"customHTTPStatuses,omitempty"`

// Optional sub-type for HTTP monitors (e.g. "HTTPS", "ping", etc.)
SubType string `json:"subType,omitempty"`

// Optional TCP/UDP port to use when applicable (e.g. 443 for HTTPS)
Port int `json:"port,omitempty"`

// Timeout in seconds before considering the monitored site unresponsive
Timeout int `json:"timeout,omitempty"`

// Username for basic HTTP authentication if required by the target URL
HTTPAuthUsername string `json:"httpAuthUsername,omitempty"`

// Password for basic HTTP authentication
HTTPAuthPassword string `json:"httpAuthPassword,omitempty"`

// Authentication type: 1 = Basic Auth, 2 = Digest Auth
HTTPAuthType int `json:"httpAuthType,omitempty"`

// Type of HTTP POST request: e.g., "application/x-www-form-urlencoded"
PostType string `json:"postType,omitempty"`

// POST body value to be submitted with the request
PostValue string `json:"postValue,omitempty"`

// HTTP method used for the check: 1 = HEAD, 2 = GET, 3 = POST, 4 = PUT, 5 = PATCH, 6 = DELETE, 7 = OPTIONS
HTTPMethod int `json:"httpMethod,omitempty"`

// Content-Type of the HTTP POST request (e.g. "application/json")
PostContentType string `json:"postContentType,omitempty"`

// Set of custom HTTP headers in JSON format (e.g. {"Authorization": "Bearer TOKEN"})
CustomHTTPHeaders string `json:"customHttpHeaders,omitempty"`

// If set to 1, SSL errors will be ignored for HTTPS monitors
IgnoreSSLErrors int `json:"ignoreSSLErrors,omitempty"`

// If set to 1, disables expiration notifications for domains
DisableDomainExpireNotifications int `json:"disableDomainExpireNotifications,omitempty"`
}

// UptimeConfig defines the configuration for Uptime Monitor Provider
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,33 @@ spec:
or down For ex: 200:0_401:1_503:1 (to accept 200 as down and
401 and 503 as up)'
type: string
customHttpHeaders:
description: 'Set of custom HTTP headers in JSON format (e.g.
{"Authorization": "Bearer TOKEN"})'
type: string
disableDomainExpireNotifications:
description: If set to 1, disables expiration notifications for
domains
type: integer
httpAuthPassword:
description: Password for basic HTTP authentication
type: string
httpAuthType:
description: 'Authentication type: 1 = Basic Auth, 2 = Digest
Auth'
type: integer
httpAuthUsername:
description: Username for basic HTTP authentication if required
by the target URL
type: string
httpMethod:
description: 'HTTP method used for the check: 1 = HEAD, 2 = GET,
3 = POST, 4 = PUT, 5 = PATCH, 6 = DELETE, 7 = OPTIONS'
type: integer
ignoreSSLErrors:
description: If set to 1, SSL errors will be ignored for HTTPS
monitors
type: integer
interval:
description: The uptimerobot check interval in seconds
minimum: 60
Expand All @@ -390,10 +417,31 @@ spec:
- http
- keyword
type: string
port:
description: Optional TCP/UDP port to use when applicable (e.g.
443 for HTTPS)
type: integer
postContentType:
description: Content-Type of the HTTP POST request (e.g. "application/json")
type: string
postType:
description: 'Type of HTTP POST request: e.g., "application/x-www-form-urlencoded"'
type: string
postValue:
description: POST body value to be submitted with the request
type: string
statusPages:
description: The uptimerobot public status page ID to add this
monitor to
type: string
subType:
description: Optional sub-type for HTTP monitors (e.g. "HTTPS",
"ping", etc.)
type: string
timeout:
description: Timeout in seconds before considering the monitored
site unresponsive
type: integer
type: object
url:
description: URL to monitor
Expand Down
2 changes: 2 additions & 0 deletions pkg/monitors/uptimerobot/uptime-mappers.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ func UptimeMonitorMonitorToBaseMonitorMapper(uptimeMonitor UptimeMonitorMonitor)
providerConfig.AlertContacts = strings.Join(alertContacts, "-")
}

providerConfig.HTTPMethod = uptimeMonitor.HTTPMethod

m.Config = &providerConfig

return &m
Expand Down
100 changes: 86 additions & 14 deletions pkg/monitors/uptimerobot/uptime-monitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,12 @@ func (monitor *UpTimeMonitorService) GetByName(name string) (*models.Monitor, er

client := http.CreateHttpClient(monitor.url + action)

body := "api_key=" + monitor.apiKey + "&format=json&logs=1&alert_contacts=1&search=" + name
body := "api_key=" + monitor.apiKey + "&format=json&logs=1&alert_contacts=1&http_request_details=true&search=" + name

response := client.PostUrlEncodedFormBody(body)

if response.StatusCode == Http.StatusOK {
switch response.StatusCode {
case Http.StatusOK:
var f UptimeMonitorGetMonitorsResponse
err := json.Unmarshal(response.Bytes, &f)
if err != nil {
Expand All @@ -68,7 +69,7 @@ func (monitor *UpTimeMonitorService) GetByName(name string) (*models.Monitor, er
}

return nil, nil
} else if response.StatusCode == Http.StatusTooManyRequests {
case Http.StatusTooManyRequests:
log.Info("Too many requests, Monitor waiting for timeout: " + name)
retryAfter := response.Header.Get("Retry-After")
if retryAfter != "" {
Expand Down Expand Up @@ -151,7 +152,8 @@ func (monitor *UpTimeMonitorService) Add(m models.Monitor) {

response := client.PostUrlEncodedFormBody(body)

if response.StatusCode == Http.StatusOK {
switch response.StatusCode {
case Http.StatusOK:
var f UptimeMonitorNewMonitorResponse
err := json.Unmarshal(response.Bytes, &f)
if err != nil {
Expand All @@ -164,7 +166,7 @@ func (monitor *UpTimeMonitorService) Add(m models.Monitor) {
} else {
log.Info("Monitor couldn't be added: " + m.Name + ". Error: " + f.Error.Message)
}
} else if response.StatusCode == Http.StatusTooManyRequests {
case Http.StatusTooManyRequests:
log.Info("Too many requests, Monitor waiting for timeout: " + m.Name)
retryAfter := response.Header.Get("Retry-After")
if retryAfter != "" {
Expand All @@ -174,7 +176,7 @@ func (monitor *UpTimeMonitorService) Add(m models.Monitor) {
monitor.Add(m) // Retry after the specified delay
}
}
} else {
default:
log.Info("AddMonitor Request failed. Status Code: " + strconv.Itoa(response.StatusCode))
}
}
Expand All @@ -188,7 +190,8 @@ func (monitor *UpTimeMonitorService) Update(m models.Monitor) {

response := client.PostUrlEncodedFormBody(body)

if response.StatusCode == Http.StatusOK {
switch response.StatusCode {
case Http.StatusOK:
var f UptimeMonitorStatusMonitorResponse
err := json.Unmarshal(response.Bytes, &f)
if err != nil {
Expand All @@ -200,7 +203,7 @@ func (monitor *UpTimeMonitorService) Update(m models.Monitor) {
} else {
log.Info("Monitor couldn't be updated: " + m.Name + ". Error: " + f.Error.Message)
}
} else if response.StatusCode == Http.StatusTooManyRequests {
case Http.StatusTooManyRequests:
log.Info("Too many requests, Monitor waiting for timeout: " + m.Name)
retryAfter := response.Header.Get("Retry-After")
if retryAfter != "" {
Expand All @@ -210,7 +213,7 @@ func (monitor *UpTimeMonitorService) Update(m models.Monitor) {
monitor.Update(m) // Retry after the specified delay
}
}
} else {
default:
log.Info("UpdateMonitor Request failed. Status Code: " + strconv.Itoa(response.StatusCode))
}
}
Expand Down Expand Up @@ -255,27 +258,96 @@ func (monitor *UpTimeMonitorService) processProviderConfig(m models.Monitor, cre
} else if strings.Contains(strings.ToLower(providerConfig.MonitorType), "keyword") {
body += "&type=2"

if providerConfig != nil && len(providerConfig.KeywordExists) != 0 {

if len(providerConfig.KeywordExists) != 0 {
if strings.Contains(strings.ToLower(providerConfig.KeywordExists), "yes") {
body += "&keyword_type=1"
} else if strings.Contains(strings.ToLower(providerConfig.KeywordExists), "no") {
body += "&keyword_type=2"
}

} else {
body += "&keyword_type=1" // By default 1 (check if keyword exists)
}

if providerConfig != nil && len(providerConfig.KeywordValue) != 0 {
body += "&keyword_value=" + providerConfig.KeywordValue
// Keyword Value (Required for keyword monitoring)
if len(providerConfig.KeywordValue) != 0 {
body += "&keyword_value=" + url.QueryEscape(providerConfig.KeywordValue)
} else {
log.Error(nil, "Monitor is of type Keyword but the `keyword-value` is missing")
}
}
} else {
body += "&type=1" // By default monitor is of type HTTP
}

// SubType (Optional for certain types)
if providerConfig != nil && len(providerConfig.SubType) != 0 {
body += "&sub_type=" + url.QueryEscape(providerConfig.SubType)
}

// Port (Optional for certain types)
if providerConfig != nil && providerConfig.Port > 0 {
body += "&port=" + strconv.Itoa(providerConfig.Port)
}

// Timeout (Optional, in seconds)
if providerConfig != nil && providerConfig.Timeout > 0 {
body += "&timeout=" + strconv.Itoa(providerConfig.Timeout)
}

// HTTP Auth (Optional)
if providerConfig != nil && len(providerConfig.HTTPAuthUsername) != 0 && len(providerConfig.HTTPAuthPassword) != 0 {
body += "&http_username=" + url.QueryEscape(providerConfig.HTTPAuthUsername)
body += "&http_password=" + url.QueryEscape(providerConfig.HTTPAuthPassword)
if providerConfig.HTTPAuthType > 0 {
body += "&http_auth_type=" + strconv.Itoa(providerConfig.HTTPAuthType)
}
}

// Post Type (Optional)
if providerConfig != nil && len(providerConfig.PostType) != 0 {
body += "&post_type=" + url.QueryEscape(providerConfig.PostType)
}

// Post Value (Optional)
if providerConfig != nil && len(providerConfig.PostValue) != 0 {
body += "&post_value=" + url.QueryEscape(providerConfig.PostValue)
}

// HTTP Method (Optional)
if providerConfig != nil && providerConfig.HTTPMethod != 0 {
body += "&http_method=" + strconv.Itoa(providerConfig.HTTPMethod)
}

// Post Content Type (Optional)
if providerConfig != nil && len(providerConfig.PostContentType) != 0 {
body += "&post_content_type=" + url.QueryEscape(providerConfig.PostContentType)
}

// Maintenance Windows (Optional)
if providerConfig != nil && len(providerConfig.MaintenanceWindows) != 0 {
body += "&mwindows=" + url.QueryEscape(providerConfig.MaintenanceWindows)
}

// Custom HTTP Headers (Optional, must be sent as a JSON object)
if providerConfig != nil && len(providerConfig.CustomHTTPHeaders) != 0 {
body += "&custom_http_headers=" + url.QueryEscape(providerConfig.CustomHTTPHeaders)
}

// Custom HTTP Statuses (Optional, must be sent in specific format)
if providerConfig != nil && len(providerConfig.CustomHTTPStatuses) != 0 {
body += "&custom_http_statuses=" + url.QueryEscape(providerConfig.CustomHTTPStatuses)
}

// Ignore SSL Errors (Optional)
if providerConfig != nil && providerConfig.IgnoreSSLErrors > 0 {
body += "&ignore_ssl_errors=" + strconv.Itoa(providerConfig.IgnoreSSLErrors)
}

// Disable Domain Expire Notifications (Optional)
if providerConfig != nil && providerConfig.DisableDomainExpireNotifications > 0 {
body += "&disable_domain_expire_notifications=" + strconv.Itoa(providerConfig.DisableDomainExpireNotifications)
}

return body
}

Expand Down
29 changes: 29 additions & 0 deletions pkg/monitors/uptimerobot/uptime-monitor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,35 @@ func TestAddMonitorWithMonitorType(t *testing.T) {
service.Remove(*mRes)
}

func TestAddMonitorWithMonitorHTTPMethod(t *testing.T) {
config := config.GetControllerConfigTest()

service := UpTimeMonitorService{}
provider := util.GetProviderWithName(config, "UptimeRobot")
if provider == nil {
return
}
service.Setup(*provider)

configKeyword := &endpointmonitorv1alpha1.UptimeRobotConfig{
MonitorType: "http",
HTTPMethod: 2, // GET
}

m := models.Monitor{Name: "google-test", URL: "https://google.com", Config: configKeyword}
service.Add(m)

time.Sleep(time.Second * 30)
mRes, err := service.GetByName("google-test")

if err != nil {
t.Error("Error: " + err.Error())
}
if mRes.Name != m.Name {
t.Error("The name is incorrect, expected: " + m.Name + ", but was: " + mRes.Name)
}
}

func TestAddMonitorWithIncorrectValues(t *testing.T) {
config := config.GetControllerConfigTest()

Expand Down
35 changes: 20 additions & 15 deletions pkg/monitors/uptimerobot/uptime-responses.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,26 @@ type UptimeMonitorPagination struct {
}

type UptimeMonitorMonitor struct {
ID int `json:"id"`
FriendlyName string `json:"friendly_name"`
URL string `json:"url"`
Type int `json:"type"`
SubType string `json:"sub_type"`
KeywordType int `json:"keyword_type"`
KeywordValue string `json:"keyword_value"`
HTTPUsername string `json:"http_username"`
HTTPPassword string `json:"http_password"`
Port string `json:"port"`
Interval int `json:"interval"`
Status int `json:"status"`
CreateDatetime int `json:"create_datetime"`
Logs []UptimeMonitorLogs `json:"logs"`
AlertContacts []UptimeMonitorAlertContacts `json:"alert_contacts"`
ID int `json:"id"`
FriendlyName string `json:"friendly_name"`
URL string `json:"url"`
Type int `json:"type"`
SubType string `json:"sub_type"`
KeywordType int `json:"keyword_type"`
KeywordValue string `json:"keyword_value"`
HTTPUsername string `json:"http_username"`
HTTPPassword string `json:"http_password"`
Port string `json:"port"`
Interval int `json:"interval"`
Status int `json:"status"`
CreateDatetime int `json:"create_datetime"`
Logs []UptimeMonitorLogs `json:"logs"`
AlertContacts []UptimeMonitorAlertContacts `json:"alert_contacts"`
SSL int `json:"ssl"`
Timeout int `json:"timeout"`
CustomHTTPStatuses string `json:"custom_http_statuses"`
CustomHeader string `json:"custom_header"`
HTTPMethod int `json:"http_method"`
}

type UptimeMonitorAlertContacts struct {
Expand Down
2 changes: 1 addition & 1 deletion pkg/monitors/uptimerobot/uptime-status-page.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ func (statusPageService *UpTimeStatusPageService) RemoveMonitorFromStatusPage(st

body := "api_key=" + statusPageService.apiKey + "&format=json&id=" + statusPage.ID

if existingStatusPage.Monitors != nil && len(existingStatusPage.Monitors) > 0 {
if len(existingStatusPage.Monitors) > 0 {
monitors := strings.Join(existingStatusPage.Monitors, "-")
body += "&monitors=" + monitors
} else {
Expand Down
Loading