Skip to content
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

feat(Pingdom): add requestHeadersEnvVar field #599

Merged
merged 6 commits into from
Jul 11, 2024
Merged
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
9 changes: 7 additions & 2 deletions api/v1alpha1/endpointmonitor_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,10 +244,15 @@ type PingdomConfig struct {
// +optional
NotifyWhenBackUp bool `json:"notifyWhenBackUp,omitempty"`

// Custom pingdom request headers
// Custom request headers
// +optional
RequestHeaders string `json:"requestHeaders,omitempty"`

// Custom request headers that should be read from an environment variable as it possibly contains sensitive data.
// An example would be an API token.
// +optional
RequestHeadersEnvVar string `json:"requestHeadersEnvVar,omitempty"`

// Required for basic-authentication
// +optional
BasicAuthUser string `json:"basicAuthUser,omitempty"`
Expand Down Expand Up @@ -287,7 +292,7 @@ type PingdomConfig struct {

// Data that should be posted to the web page, for example submission data for a sign-up or login form.
// The data needs to be formatted in the same way as a web browser would send it to the web server.
// Because post data contains sensitive secret this field is only reference to a environment variable.
// Because post data contains sensitive secret this field is only a reference to an environment variable.
// +optional
PostDataEnvVar string `json:"postDataEnvVar,omitempty"`
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,15 @@ spec:
submission data for a sign-up or login form. The data needs
to be formatted in the same way as a web browser would send
it to the web server. Because post data contains sensitive secret
this field is only reference to a environment variable.
this field is only a reference to an environment variable.
type: string
requestHeaders:
description: Custom pingdom request headers
description: Custom request headers
type: string
requestHeadersEnvVar:
description: Custom request headers that should be read from an
environment variable as it possibly contains sensitive data.
An example would be an API token.
type: string
resolution:
description: The pingdom check interval in minutes
Expand Down
41 changes: 23 additions & 18 deletions docs/pingdom-configuration.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# Pingdom Configuration

## Note

Currently we do not have access to Pingdom account that's why Tests are not verified. Community members having Pingdom account are welcome to contribute in Test Cases.

## Basic

The following properties need to be configured for Pingdom, in addition to the general properties listed
in the [Configuration section of the README](../README.md#configuration):

Expand All @@ -12,31 +14,35 @@ in the [Configuration section of the README](../README.md#configuration):
| apiToken | Pingdom API Token generated inside My Pingdom |

## Optional

The following optional properties can be included if you want to declare some default options, without re-declaring them for each EndpointMonitor.
You are able to override any of them via EndpointMonitor specific options.

| Key | Description |
|-------------------|----------------------------------------------------------|
| alertIntegrations | `-` separated list of integration ids |
| teamAlertContacts | `-` separated list of teams ids |
| alertContacts | `-` separated list of alert contacts ids |
| alertIntegrations | `-` separated list of integration ids |
| teamAlertContacts | `-` separated list of teams ids |
| alertContacts | `-` separated list of alert contacts ids |

## Advanced

Currently additional pingdom configurations can be added through these fields:

| Fields | Description |
|:--------------------------------------------------------:|:------------------------------------------------:|
| Resolution | The pingdom check interval in minutes |
| SendNotificationWhenDown | How many failed check attempts before notifying |
| Paused | Set to "true" to pause checks |
| NotifyWhenBackUp | Set to "false" to disable recovery notifications |
| RequestHeaders | Custom pingdom request headers (e.g. {"Accept"="application/json"}) |
| Fields | Description |
|---------------------------|--------------------------------------------------|
| Resolution | The pingdom check interval in minutes |
| SendNotificationWhenDown | How many failed check attempts before notifying |
| Paused | Set to "true" to pause checks |
| NotifyWhenBackUp | Set to "false" to disable recovery notifications |
| PostDataEnvVar | Send post data. - [see below](#post-data-checks) |
| RequestHeaders | Custom request headers (e.g. {"Accept"="application/json"}) |
| RequestHeadersEnvVar | Custom request headers that should be stored in an environemnt variable, e.g., authentication headers. Behaves the same as PostDataEnvVar - [see below](#post-data-checks) |
| BasicAuthUser | Required for basic-authentication checks - [see below](#basic-auth-checks) |
| ShouldContain | Set to text string that has to be present in the HTML code of the page (configures "Should contain") |
| Tags | Comma separated set of tags to apply to check (e.g. "testing,aws") |
| AlertIntegrations | `-` separated set list of integrations ids (e.g. "91166-12168") |
| AlertContacts | `-` separated contact id's (e.g. "1234567_8_9-9876543_2_1") to override the [default alertContacts](https://github.com/stakater/IngressMonitorController/blob/master/README.md#usage)|
| TeamAlertContacts | Teams to alert. `-` separated set list of teams ids (e.g. "1234567_8_9-9876543_2_1)|
| ShouldContain | Set to text string that has to be present in the HTML code of the page (configures "Should contain") |
| Tags | Comma separated set of tags to apply to check (e.g. "testing,aws") |
| AlertIntegrations | `-` separated set list of integrations ids (e.g. "91166-12168") |
| AlertContacts | `-` separated contact id's (e.g. "1234567_8_9-9876543_2_1") to override the [default alertContacts](https://github.com/stakater/IngressMonitorController/blob/master/README.md#usage)|
| TeamAlertContacts | Teams to alert. `-` separated set list of teams ids (e.g. "1234567_8_9-9876543_2_1)|

### Basic Auth checks

Expand Down Expand Up @@ -71,9 +77,9 @@ envFrom:
name: stakater-post-data
```

If you set postData the request method will be automatically POST.
If you set postDataEnvVar the request method will be automatically POST.

## Example:
## Example

```yaml
apiVersion: endpointmonitor.stakater.com/v1alpha1
Expand All @@ -97,4 +103,3 @@ spec:
teamAlertContacts: "1234567_8_9-9876543_2_1,1234567_8_9-9876543_2_2"
postDataEnvVar: "monitor-user"
```

2 changes: 1 addition & 1 deletion pkg/controllers/endpointmonitor_deleted.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func (r *EndpointMonitorReconciler) removeMonitorIfExists(monitorService *monito
// Monitor Exists
if monitor != nil {
// Monitor Exists, remove the monitor
log.Info("Removing monitor: " + monitorName + " from provider provider: " + monitorService.GetType())
log.Info("Removing monitor " + monitorName + " from provider: " + monitorService.GetType())
monitorService.Remove(*monitor)
} else {
log.Info("Cannot find monitor with name: " + monitorName + " for provider: " + monitorService.GetType())
Expand Down
56 changes: 38 additions & 18 deletions pkg/monitors/pingdom/pingdom-monitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ type PingdomMonitorService struct {
client *pingdom.Client
}

func (monitor *PingdomMonitorService) Equal(oldMonitor models.Monitor, newMonitor models.Monitor) bool {
func (service *PingdomMonitorService) Equal(oldMonitor models.Monitor, newMonitor models.Monitor) bool {
// TODO: Retrieve oldMonitor config and compare it here
return false
}
Expand All @@ -48,7 +48,7 @@ func (service *PingdomMonitorService) Setup(p config.Provider) {
BaseURL: service.url,
})
if err != nil {
log.Info("Error Seting Up Monitor Service: " + err.Error())
log.Info("Error setting up Monitor Service", "error", err)
}
}

Expand All @@ -62,15 +62,15 @@ func (service *PingdomMonitorService) GetByName(name string) (*models.Monitor, e
}
}

return match, fmt.Errorf("Unable to locate monitor with name %v", name)
return match, fmt.Errorf("Unable to locate monitor with name '%v'", name)
}

func (service *PingdomMonitorService) GetAll() []models.Monitor {
var monitors []models.Monitor

checks, err := service.client.Checks.List()
if err != nil {
log.Info("Error received while listing checks: " + err.Error())
log.Info("Error received while listing checks", "error", err)
return nil
}
for _, mon := range checks {
Expand All @@ -90,9 +90,9 @@ func (service *PingdomMonitorService) Add(m models.Monitor) {

_, err := service.client.Checks.Create(&httpCheck)
if err != nil {
log.Info(fmt.Sprintf("Error Adding Monitor %s %v", m.Name, err.Error()))
log.Info(fmt.Sprintf("Error adding Monitor '%s': %v", m.Name, err.Error()))
} else {
log.Info("Added monitor for: " + m.Name)
log.Info("Successfully added Monitor " + m.Name)
}
}

Expand All @@ -102,9 +102,9 @@ func (service *PingdomMonitorService) Update(m models.Monitor) {

resp, err := service.client.Checks.Update(monitorID, &httpCheck)
if err != nil {
log.Info(fmt.Sprintf("Error updating Monitor %s %v", m.Name, err.Error()))
log.Info(fmt.Sprintf("Error updating Monitor '%s': %v", m.Name, err.Error()))
} else {
log.Info("Sucessfully updated Monitor "+m.Name, "Response", resp.Message)
log.Info("Successfully updated Monitor "+m.Name, "response", resp.Message)
}
}

Expand All @@ -113,18 +113,17 @@ func (service *PingdomMonitorService) Remove(m models.Monitor) {

resp, err := service.client.Checks.Delete(monitorID)
if err != nil {
log.Info(fmt.Sprintf("Error deleting Monitor %s %v", m.Name, err.Error()))
log.Info(fmt.Sprintf("Error deleting Monitor '%s': %v", m.Name, err.Error()))
} else {
log.Info("Sucessfully deleted Monitor "+m.Name, "Response", resp.Message)
log.Info("Successfully deleted Monitor "+m.Name, "response", resp.Message)
}
}

func (service *PingdomMonitorService) createHttpCheck(monitor models.Monitor) pingdom.HttpCheck {
httpCheck := pingdom.HttpCheck{}
url, err := url.Parse(monitor.URL)
if err != nil {
log.Info(fmt.Sprintf("Error parsing url '%s' of monitor %s", monitor.Name, service.url))
log.Info("Unable to parse the URL: " + service.url)
log.Info(fmt.Sprintf("Error parsing url '%s' of monitor %s", service.url, monitor.Name))
}

if url.Scheme == "https" {
Expand Down Expand Up @@ -184,7 +183,7 @@ func (service *PingdomMonitorService) addConfigToHttpCheck(httpCheck *pingdom.Ht
userIdsStringArray := strings.Split(providerConfig.AlertContacts, "-")

if userIds, err := util.SliceAtoi(userIdsStringArray); err != nil {
log.Info("Error decoding user alert contact IDs from config" + err.Error())
log.Info("Error decoding user alert contact IDs from config", "error", err)
} else {
httpCheck.UserIds = userIds
}
Expand All @@ -194,7 +193,7 @@ func (service *PingdomMonitorService) addConfigToHttpCheck(httpCheck *pingdom.Ht
integrationIdsStringArray := strings.Split(providerConfig.AlertIntegrations, "-")

if integrationIds, err := util.SliceAtoi(integrationIdsStringArray); err != nil {
log.Info("Error decoding integration ids into integers" + err.Error())
log.Info("Error decoding integration ids into integers", "error", err)
} else {
httpCheck.IntegrationIds = integrationIds
}
Expand All @@ -204,7 +203,7 @@ func (service *PingdomMonitorService) addConfigToHttpCheck(httpCheck *pingdom.Ht
integrationTeamIdsStringArray := strings.Split(providerConfig.TeamAlertContacts, "-")

if integrationTeamIdsStringArray, err := util.SliceAtoi(integrationTeamIdsStringArray); err != nil {
log.Info("Error decoding integration ids into integers" + err.Error())
log.Info("Error decoding integration ids into integers", "error", err)
} else {
httpCheck.TeamIds = integrationTeamIdsStringArray
}
Expand All @@ -226,7 +225,28 @@ func (service *PingdomMonitorService) addConfigToHttpCheck(httpCheck *pingdom.Ht
httpCheck.RequestHeaders = make(map[string]string)
err := json.Unmarshal([]byte(providerConfig.RequestHeaders), &httpCheck.RequestHeaders)
if err != nil {
log.Info("Error Converting from string to JSON object")
log.Info("Error converting request headers from string to JSON", "value", providerConfig.RequestHeaders, "error", err)
}
}
if providerConfig != nil && len(providerConfig.RequestHeadersEnvVar) > 0 {
requestHeaderEnvValue := os.Getenv(providerConfig.RequestHeadersEnvVar)
if requestHeaderEnvValue != "" {
requestHeadersValue := make(map[string]string)
err := json.Unmarshal([]byte(requestHeaderEnvValue), &requestHeadersValue)
if err != nil {
log.Info("Error converting request headers from environment from string to JSON", "envVar", providerConfig.RequestHeadersEnvVar, "error", err)
}

if httpCheck.RequestHeaders != nil {
for key, value := range requestHeadersValue {
httpCheck.RequestHeaders[key] = value
}
} else {
httpCheck.RequestHeaders = requestHeadersValue
}

} else {
log.Error(errors.New("error reading request headers from environment variable"), "Environment Variable does not exist", "envVar", providerConfig.RequestHeadersEnvVar)
}
}

Expand All @@ -240,7 +260,7 @@ func (service *PingdomMonitorService) addConfigToHttpCheck(httpCheck *pingdom.Ht
httpCheck.Username = providerConfig.BasicAuthUser
httpCheck.Password = passwordValue
} else {
log.Info("Error reading basic auth password from environment variable")
log.Error(errors.New("error reading basic auth password from environment variable"), "Environment Variable does not exist", "envVar", providerConfig.BasicAuthUser)
}
}

Expand All @@ -266,7 +286,7 @@ func (service *PingdomMonitorService) addConfigToHttpCheck(httpCheck *pingdom.Ht
if postDataValue != "" {
httpCheck.PostData = postDataValue
} else {
log.Error(errors.New("error reading post data from environment variable"), "Environment Variable %s does not exist", providerConfig.PostDataEnvVar)
log.Error(errors.New("error reading post data from environment variable"), "Environment Variable does not exist", "envVar", providerConfig.PostDataEnvVar)
}
}
}
Expand Down
24 changes: 12 additions & 12 deletions pkg/monitors/pingdomtransaction/pingdom-transaction-monitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ type PingdomTransactionMonitorService struct {
namespace string
}

func (monitor *PingdomTransactionMonitorService) Equal(oldMonitor models.Monitor, newMonitor models.Monitor) bool {
func (service *PingdomTransactionMonitorService) Equal(oldMonitor models.Monitor, newMonitor models.Monitor) bool {
// TODO: Retrieve oldMonitor config and compare it here
return false
}
Expand Down Expand Up @@ -105,7 +105,7 @@ func (service *PingdomTransactionMonitorService) GetAll() []models.Monitor {
func (service *PingdomTransactionMonitorService) GetUrlFromSteps(id int64) string {
check, _, err := service.client.TMSChecksAPI.GetCheck(service.context, id).Execute()
if err != nil {
log.Error(err, "Error getting transaction check")
log.Error(err, "Error getting transaction check", "id", id)
return ""
}
if check == nil {
Expand All @@ -126,7 +126,7 @@ func (service *PingdomTransactionMonitorService) Add(m models.Monitor) {
}
_, resp, err := service.client.TMSChecksAPI.AddCheck(service.context).CheckWithoutID(*transactionCheck).Execute()
if err != nil {
log.Error(err, "Error Adding Pingdom Transaction Monitor "+m.Name, "Response", parseResponseBody(resp))
log.Error(err, "Error adding Pingdom Transaction Monitor "+m.Name, "response", parseResponseBody(resp))
} else {
log.Info("Successfully added Pingdom Transaction Monitor " + m.Name)
}
Expand All @@ -140,18 +140,18 @@ func (service *PingdomTransactionMonitorService) Update(m models.Monitor) {
monitorID := util.StrToInt64(m.ID)
_, resp, err := service.client.TMSChecksAPI.ModifyCheck(service.context, monitorID).CheckWithoutIDPUT(*transactionCheck.AsPut()).Execute()
if err != nil {
log.Error(err, "Error Updating Pingdom Transaction Monitor", "Response", parseResponseBody(resp))
log.Error(err, "Error updating Pingdom Transaction Monitor", "response", parseResponseBody(resp))
return
}
log.Info("Updated Pingdom Transaction Monitor Monitor " + m.Name)
log.Info("Successfully updated Pingdom Transaction Monitor " + m.Name)
}

func (service *PingdomTransactionMonitorService) Remove(m models.Monitor) {
_, resp, err := service.client.TMSChecksAPI.DeleteCheck(service.context, util.StrToInt64(m.ID)).Execute()
if err != nil {
log.Error(err, "Error Deleting Pingdom Transaction Monitor", "Response", parseResponseBody(resp))
log.Error(err, "Error deleting Pingdom Transaction Monitor", "response", parseResponseBody(resp))
} else {
log.Info("Deleted Pingdom Transaction Monitor Monitor " + m.Name)
log.Info("Successfully deleted Pingdom Transaction Monitor " + m.Name)
}
}

Expand All @@ -177,19 +177,19 @@ func (service *PingdomTransactionMonitorService) createTransactionCheck(monitor
if teamAlertContacts != nil {
transactionCheck.TeamIds = teamAlertContacts
}
service.addConfigToTranscationCheck(transactionCheck, monitor)
service.addConfigToTransactionCheck(transactionCheck, monitor)

return transactionCheck
}

func (service *PingdomTransactionMonitorService) addConfigToTranscationCheck(transactionCheck *pingdomNew.CheckWithoutID, monitor models.Monitor) {
func (service *PingdomTransactionMonitorService) addConfigToTransactionCheck(transactionCheck *pingdomNew.CheckWithoutID, monitor models.Monitor) {

// Retrieve provider configuration
config := monitor.Config
providerConfig, _ := config.(*endpointmonitorv1alpha1.PingdomTransactionConfig)

if providerConfig == nil {
// providerConfig is not set, we create a go_to transaction by default from url because its required by API
// providerConfig is not set, we create a go_to transaction by default from url because it's required by API
transactionCheck.Steps = append(transactionCheck.Steps, pingdomNew.Step{
Args: &pingdomNew.StepArgs{
Url: ptr.String(monitor.URL),
Expand Down Expand Up @@ -233,7 +233,7 @@ func (service *PingdomTransactionMonitorService) addConfigToTranscationCheck(tra
transactionCheck.SeverityLevel = ptr.String(providerConfig.SeverityLevel)
}
if providerConfig.Interval > 0 {
transactionCheck.Interval = ptr.Int64((int64(providerConfig.Interval)))
transactionCheck.Interval = ptr.Int64(int64(providerConfig.Interval))
}
for _, step := range providerConfig.Steps {
args := service.NewStepArgsByMap(step.Args)
Expand Down Expand Up @@ -369,7 +369,7 @@ func parseResponseBody(resp *http.Response) string {
// Attempt to unmarshal the response body into a map
var responseBodyMap map[string]map[string]interface{}
if err := json.Unmarshal(bodyBytes, &responseBodyMap); err != nil {
// If unmarshaling fails, return the whole body as a string.
// If unmarshalling fails, return the whole body as a string.
return string(bodyBytes)
}
// Check if "error" key exists in the map
Expand Down
Loading