Open didier-segura opened 3 months ago
Hi @didier-segura Pull requests are welcomed, feel free to add one
Hi @SheryarButt ,
I will try to PR something, but I can't check if it will works correctly.
It seems I can't create a PR.
pkg/monitor/uptime-monitor.go
package uptimerobot
import (
"encoding/json"
"errors"
"fmt"
Http "net/http"
"net/url"
"reflect"
"strconv"
"strings"
endpointmonitorv1alpha1 "github.com/stakater/IngressMonitorController/v2/api/v1alpha1"
"github.com/stakater/IngressMonitorController/v2/pkg/config"
"github.com/stakater/IngressMonitorController/v2/pkg/http"
"github.com/stakater/IngressMonitorController/v2/pkg/models"
)
type UpTimeMonitorService struct {
apiKey string
url string
alertContacts string
statusPageService UpTimeStatusPageService
}
// Default Interval for status checking
const DefaultInterval = 300
func (monitor *UpTimeMonitorService) Equal(oldMonitor models.Monitor, newMonitor models.Monitor) bool {
if !(reflect.DeepEqual(monitor.processProviderConfig(oldMonitor, false), monitor.processProviderConfig(newMonitor, false))) {
log.Info(fmt.Sprintf("There are some new changes in %s monitor", newMonitor.Name))
return false
}
return true
}
func (monitor *UpTimeMonitorService) Setup(p config.Provider) {
monitor.apiKey = p.ApiKey
monitor.url = p.ApiURL
monitor.alertContacts = p.AlertContacts
monitor.statusPageService = UpTimeStatusPageService{}
monitor.statusPageService.Setup(p)
}
func (monitor *UpTimeMonitorService) GetByName(name string) (*models.Monitor, error) {
action := "getMonitors"
client := http.CreateHttpClient(monitor.url + action)
body := "api_key=" + monitor.apiKey + "&format=json&logs=1&alert_contacts=1&search=" + name
response := client.PostUrlEncodedFormBody(body)
if response.StatusCode == Http.StatusOK {
var f UptimeMonitorGetMonitorsResponse
err := json.Unmarshal(response.Bytes, &f)
if err != nil {
log.Error(err, "Unable to unmarshal JSON")
}
if f.Monitors != nil {
for _, monitor := range f.Monitors {
if monitor.FriendlyName == name {
return UptimeMonitorMonitorToBaseMonitorMapper(monitor), nil
}
}
}
return nil, nil
}
errorString := "GetByName Request failed for name: " + name + ". Status Code: " + strconv.Itoa(response.StatusCode)
log.Info(errorString)
return nil, errors.New(errorString)
}
func (monitor *UpTimeMonitorService) GetAllByName(name string) ([]models.Monitor, error) {
action := "getMonitors"
client := http.CreateHttpClient(monitor.url + action)
body := "api_key=" + monitor.apiKey + "&format=json&logs=1" + "&search=" + name
response := client.PostUrlEncodedFormBody(body)
if response.StatusCode == 200 {
var f UptimeMonitorGetMonitorsResponse
err := json.Unmarshal(response.Bytes, &f)
if err != nil {
log.Error(err, "Unable to unmarshal JSON")
}
if len(f.Monitors) > 0 {
return UptimeMonitorMonitorsToBaseMonitorsMapper(f.Monitors), nil
}
return nil, nil
}
errorString := "GetAllByName Request failed for name: " + name + ". Status Code: " + strconv.Itoa(response.StatusCode)
log.Info(errorString)
return nil, errors.New(errorString)
}
func (monitor *UpTimeMonitorService) GetAll() []models.Monitor {
action := "getMonitors"
client := http.CreateHttpClient(monitor.url + action)
body := "api_key=" + monitor.apiKey + "&format=json&logs=1"
response := client.PostUrlEncodedFormBody(body)
if response.StatusCode == Http.StatusOK {
var f UptimeMonitorGetMonitorsResponse
err := json.Unmarshal(response.Bytes, &f)
if err != nil {
log.Error(err, "Unable to unmarshal list monitors response")
}
return UptimeMonitorMonitorsToBaseMonitorsMapper(f.Monitors)
}
log.Info("GetAllMonitors Request for UptimeRobot failed. Status Code: " + strconv.Itoa(response.StatusCode))
return nil
}
func (monitor *UpTimeMonitorService) Add(m models.Monitor) {
action := "newMonitor"
client := http.CreateHttpClient(monitor.url + action)
body := monitor.processProviderConfig(m, true)
response := client.PostUrlEncodedFormBody(body)
if response.StatusCode == Http.StatusOK {
var f UptimeMonitorNewMonitorResponse
err := json.Unmarshal(response.Bytes, &f)
if err != nil {
log.Error(err, "Monitor couldn't be added: "+m.Name)
}
if f.Stat == "ok" {
log.Info("Monitor Added: " + m.Name)
monitor.handleStatusPagesConfig(m, strconv.Itoa(f.Monitor.ID))
} else {
log.Info("Monitor couldn't be added: " + m.Name + ". Error: " + f.Error.Message)
}
} else {
log.Info("AddMonitor Request failed. Status Code: " + strconv.Itoa(response.StatusCode))
}
}
func (monitor *UpTimeMonitorService) Update(m models.Monitor) {
action := "editMonitor"
client := http.CreateHttpClient(monitor.url + action)
body := monitor.processProviderConfig(m, false)
response := client.PostUrlEncodedFormBody(body)
if response.StatusCode == Http.StatusOK {
var f UptimeMonitorStatusMonitorResponse
err := json.Unmarshal(response.Bytes, &f)
if err != nil {
log.Error(err, "Monitor couldn't be updated: "+m.Name)
}
if f.Stat == "ok" {
log.Info("Monitor Updated: " + m.Name)
monitor.handleStatusPagesConfig(m, strconv.Itoa(f.Monitor.ID))
} else {
log.Info("Monitor couldn't be updated: " + m.Name + ". Error: " + f.Error.Message)
}
} else {
log.Info("UpdateMonitor Request failed. Status Code: " + strconv.Itoa(response.StatusCode))
}
}
func (monitor *UpTimeMonitorService) processProviderConfig(m models.Monitor, createMonitorRequest bool) string {
var body string
// if createFunction is true, generate query for create else for update
if createMonitorRequest {
body = "api_key=" + monitor.apiKey + "&format=json&url=" + url.QueryEscape(m.URL) + "&friendly_name=" + url.QueryEscape(m.Name)
} else {
body = "api_key=" + monitor.apiKey + "&format=json&id=" + m.ID + "&friendly_name=" + m.Name + "&url=" + m.URL
}
// Retrieve provider configuration
providerConfig, _ := m.Config.(*endpointmonitorv1alpha1.UptimeRobotConfig)
// Type (Required)
if providerConfig != nil && len(providerConfig.MonitorType) != 0 {
if strings.Contains(strings.ToLower(providerConfig.MonitorType), "http") {
body += "&type=1"
} else if strings.Contains(strings.ToLower(providerConfig.MonitorType), "keyword") {
body += "&type=2"
if providerConfig != nil && 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"
}
// Keyword Value (Required for keyword monitoring)
if len(providerConfig.KeywordValue) != 0 {
body += "&keyword_value=" + url.QueryEscape(providerConfig.KeywordValue)
} else {
body += "&keyword_type=1" // By default 1 (check if keyword exists)
}
if providerConfig != nil && len(providerConfig.KeywordValue) != 0 {
body += "&keyword_value=" + 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)
}
// Interval (Optional, in seconds)
if providerConfig != nil && providerConfig.Interval > 0 {
body += "&interval=" + strconv.Itoa(providerConfig.Interval)
} else {
body += "&interval=" + strconv.Itoa(DefaultInterval)
}
// 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 && len(providerConfig.HTTPMethod) != 0 {
body += "&http_method=" + url.QueryEscape(providerConfig.HTTPMethod)
}
// Post Content Type (Optional)
if providerConfig != nil && len(providerConfig.PostContentType) != 0 {
body += "&post_content_type=" + url.QueryEscape(providerConfig.PostContentType)
}
// Alert Contacts (Optional)
if providerConfig != nil && len(providerConfig.AlertContacts) != 0 {
body += "&alert_contacts=" + url.QueryEscape(providerConfig.AlertContacts)
} else {
body += "&alert_contacts=" + url.QueryEscape(monitor.alertContacts)
}
// 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
}
func (monitor *UpTimeMonitorService) Remove(m models.Monitor) {
action := "deleteMonitor"
client := http.CreateHttpClient(monitor.url + action)
log.Info(m.ID)
body := "api_key=" + monitor.apiKey + "&format=json&id=" + m.ID
response := client.PostUrlEncodedFormBody(body)
if response.StatusCode == Http.StatusOK {
var f UptimeMonitorStatusMonitorResponse
err := json.Unmarshal(response.Bytes, &f)
if err != nil {
log.Error(err, "Monitor couldn't be removed: "+m.Name)
}
if f.Stat == "ok" {
log.Info("Monitor Removed: " + m.Name)
} else {
log.Info("Monitor couldn't be removed: " + m.Name + ". Error: " + f.Error.Message)
log.Info(string(body))
}
} else {
log.Info("RemoveMonitor Request failed. Status Code: " + strconv.Itoa(response.StatusCode))
}
}
func (monitor *UpTimeMonitorService) handleStatusPagesConfig(monitorToAdd models.Monitor, monitorId string) {
// Retrieve provider configuration
providerConfig, _ := monitorToAdd.Config.(*endpointmonitorv1alpha1.UptimeRobotConfig)
if providerConfig != nil && len(providerConfig.StatusPages) != 0 {
IDs := strings.Split(providerConfig.StatusPages, "-")
for i := range IDs {
monitor.updateStatusPages(IDs[i], models.Monitor{ID: monitorId})
}
}
}
func (monitor *UpTimeMonitorService) updateStatusPages(statusPages string, monitorToAdd models.Monitor) {
statusPage := UpTimeStatusPage{ID: statusPages}
_, err := monitor.statusPageService.AddMonitorToStatusPage(statusPage, monitorToAdd)
if err != nil {
log.Info("Monitor couldn't be added to status page: " + err.Error())
}
}
pkg/monitor/uptime-responses.go
package uptimerobot
type UptimeMonitorGetMonitorsResponse struct {
Stat string `json:"stat"`
Pagination UptimeMonitorPagination `json:"pagination"`
Monitors []UptimeMonitorMonitor `json:"monitors"`
}
type UptimeMonitorPagination struct {
Offset int `json:"offset"`
Limit int `json:"limit"`
Total int `json:"total"`
}
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"`
SSL int `json:"ssl"`
Timeout int `json:"timeout"`
CustomHTTPStatuses string `json:"custom_http_statuses"`
CustomHeader string `json:"custom_header"`
}
type UptimeMonitorAlertContacts struct {
ID string `json:"id"`
Threshold int `json:"threshold"`
Recurrence int `json:"recurrence"`
}
type UptimeMonitorLogs struct {
Type int `json:"type"`
Datetime int `json:"datetime"`
Duration int `json:"duration"`
}
type UptimeMonitorNewMonitorResponse struct {
Stat string `json:"stat"`
Monitor UptimeMonitorMonitorStatus `json:"monitor"`
Error UptimeMonitorError `json:"error"`
}
type UptimeMonitorError struct {
Type string `json:"type"`
Message string `json:"message"`
}
type UptimeMonitorMonitorStatus struct {
ID int `json:"id"`
Status int `json:"status"`
}
type UptimeMonitorStatusMonitorResponse struct {
Stat string `json:"stat"`
Error UptimeMonitorError `json:"error"`
Monitor struct {
ID int `json:"id"`
} `json:"monitor"`
}
type UptimePublicStatusPage struct {
ID int `json:"id"`
FriendlyName string `json:"friendly_name"`
Monitors []int `json:"monitors"`
CustomDomain string `json:"custom_domain"`
Password string `json:"password"`
Sort int `json:"sort"`
Status int `json:"status"`
}
type UptimeStatusPageResponse struct {
Stat string `json:"stat"`
UptimePublicStatusPage struct {
ID int `json:"id"`
} `json:"psp"`
}
type UptimeStatusPagesResponse struct {
Stat string `json:"stat"`
Pagination struct {
Offset int `json:"offset"`
Limit int `json:"limit"`
Total int `json:"total"`
} `json:"pagination"`
StatusPages []UptimePublicStatusPage `json:"psps"`
}
I can't test this but these are some options needed to be added.
@dsegurakaliop have u tried making a fork on your side of this project, and pushing those changes to a branch, then opening a PR to upstream master branch? You should be able to do that.
+1
Can it be possible to add all missing features in a monitor creation ?
Best regards !