#126: Add client configuration
This commit is contained in:
		
							
								
								
									
										57
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										57
									
								
								README.md
									
									
									
									
									
								
							| @ -33,6 +33,8 @@ For more details, see [Usage](#usage) | ||||
|   - [Conditions](#conditions) | ||||
|     - [Placeholders](#placeholders) | ||||
|     - [Functions](#functions) | ||||
|   - [Storage](#storage) | ||||
|   - [Client configuration](#client-configuration) | ||||
|   - [Alerting](#alerting) | ||||
|     - [Configuring Slack alerts](#configuring-slack-alerts) | ||||
|     - [Configuring Discord alerts](#configuring-discord-alerts) | ||||
| @ -142,7 +144,6 @@ If you want to test it locally, see [Docker](#docker). | ||||
| | `services[].group`                       | Group name. Used to group multiple services together on the dashboard. See [Service groups](#service-groups). | `""`           | | ||||
| | `services[].url`                         | URL to send the request to.                                                   | Required `""`  | | ||||
| | `services[].method`                      | Request method.                                                               | `GET`          | | ||||
| | `services[].insecure`                    | Whether to skip verifying the server's certificate chain and host name.       | `false`        | | ||||
| | `services[].conditions`                  | Conditions used to determine the health of the service. See [Conditions](#conditions). | `[]`           | | ||||
| | `services[].interval`                    | Duration to wait between every status check.                                  | `60s`          | | ||||
| | `services[].graphql`                     | Whether to wrap the body in a query param (`{"query":"$body"}`).              | `false`        | | ||||
| @ -157,6 +158,7 @@ If you want to test it locally, see [Docker](#docker). | ||||
| | `services[].alerts[].success-threshold`  | Number of successes in a row before an ongoing incident is marked as resolved. | `2`            | | ||||
| | `services[].alerts[].send-on-resolved`   | Whether to send a notification once a triggered alert is marked as resolved.  | `false`        | | ||||
| | `services[].alerts[].description`        | Description of the alert. Will be included in the alert sent.                 | `""`           | | ||||
| | `services[].client`                      | Client configuration. See [Client configuration](#client-configuration).      | `{}`           | | ||||
| | `alerting`                               | Configuration for alerting. See [Alerting](#alerting).                        | `{}`           | | ||||
| | `security`                               | Security configuration.                                                       | `{}`           | | ||||
| | `security.basic`                         | Basic authentication security configuration.                                  | `{}`           | | ||||
| @ -238,6 +240,42 @@ storage: | ||||
| See [examples/docker-compose-sqlite-storage](examples/docker-compose-sqlite-storage) for an example. | ||||
|  | ||||
|  | ||||
| ### Client configuration | ||||
| In order to support a wide range of environments, each monitored service has a unique configuration for  | ||||
| the client used to send the request. | ||||
|  | ||||
| | Parameter                | Description                                                                   | Default        | | ||||
| |:-------------------------|:----------------------------------------------------------------------------- |:-------------- | | ||||
| | `client.insecure`        | Whether to skip verifying the server's certificate chain and host name.       | `false`        | | ||||
| | `client.ignore-follow`   | Whether to ignore redirects (true) or follow them (false, default).           | `false`        | | ||||
| | `client.timeout`         | Duration before timing out.                                                   | `10s`          | | ||||
|  | ||||
| Note that some of these parameters are ignored based on the type of service. For instance, there's no certificate involved | ||||
| in ICMP requests (ping), therefore, setting `client.insecure` to `true` for a service of that type will not do anything. | ||||
|  | ||||
| This default configuration is as follows: | ||||
| ```yaml | ||||
| client: | ||||
|   insecure: false | ||||
|   ignore-follow: false | ||||
|   timeout: 10s | ||||
| ``` | ||||
| Note that this configuration is only available under `services[]`, `alerting.mattermost` and `alerting.custom`. | ||||
|  | ||||
| Here's an example with the client configuration under `service[]`: | ||||
| ```yaml | ||||
| services: | ||||
|   - name: twinnation | ||||
|     url: "https://twinnation.org/health" | ||||
|     client: | ||||
|       insecure: false | ||||
|       ignore-follow: false | ||||
|       timeout: 10s | ||||
|     conditions: | ||||
|       - "[STATUS] == 200" | ||||
| ``` | ||||
|  | ||||
|  | ||||
| ### Alerting | ||||
| Gatus supports multiple alerting providers, such as Slack and PagerDuty, and supports different alerts for each | ||||
| individual services with configurable descriptions and thresholds. | ||||
| @ -260,7 +298,7 @@ ignored. | ||||
| | `alerting.twilio.to`                     | Number to send twilio alerts to                                               | Required `""`  | | ||||
| | `alerting.mattermost`                    | Configuration for alerts of type `mattermost`                                 | `{}`           | | ||||
| | `alerting.mattermost.webhook-url`        | Mattermost Webhook URL                                                        | Required `""`  | | ||||
| | `alerting.mattermost.insecure`           | Whether to skip verifying the server's certificate chain and host name        | `false`        | | ||||
| | `alerting.mattermost.client`             | Client configuration. See [Client configuration](#client-configuration).      | `{}`           | | ||||
| | `alerting.messagebird`                   | Settings for alerts of type `messagebird`                                     | `{}`           | | ||||
| | `alerting.messagebird.access-key`        | Messagebird access key                                                        | Required `""`  | | ||||
| | `alerting.messagebird.originator`        | The sender of the message                                                     | Required `""`  | | ||||
| @ -271,9 +309,9 @@ ignored. | ||||
| | `alerting.custom`                        | Configuration for custom actions on failure or alerts                         | `{}`           | | ||||
| | `alerting.custom.url`                    | Custom alerting request url                                                   | Required `""`  | | ||||
| | `alerting.custom.method`                 | Request method                                                                | `GET`          | | ||||
| | `alerting.custom.insecure`               | Whether to skip verifying the server's certificate chain and host name        | `false`        | | ||||
| | `alerting.custom.body`                   | Custom alerting request body.                                                 | `""`           | | ||||
| | `alerting.custom.headers`                | Custom alerting request headers                                               | `{}`           | | ||||
| | `alerting.custom.client`                 | Client configuration. See [Client configuration](#client-configuration).      | `{}`           | | ||||
| | `alerting.*.default-alert.enabled`            | Whether to enable the alert                                                   | N/A       | | ||||
| | `alerting.*.default-alert.failure-threshold`  | Number of failures in a row needed before triggering the alert                | N/A       | | ||||
| | `alerting.*.default-alert.success-threshold`  | Number of successes in a row before an ongoing incident is marked as resolved | N/A       | | ||||
| @ -394,7 +432,8 @@ services: | ||||
| alerting: | ||||
|   mattermost:  | ||||
|     webhook-url: "http://**********/hooks/**********" | ||||
|     insecure: true | ||||
|     client: | ||||
|       insecure: true | ||||
|  | ||||
| services: | ||||
|   - name: twinnation | ||||
| @ -490,7 +529,6 @@ alerting: | ||||
|   custom: | ||||
|     url: "https://hooks.slack.com/services/**********/**********/**********" | ||||
|     method: "POST" | ||||
|     insecure: true | ||||
|     body: | | ||||
|       { | ||||
|         "text": "[ALERT_TRIGGERED_OR_RESOLVED]: [SERVICE_NAME] - [ALERT_DESCRIPTION]" | ||||
| @ -739,7 +777,7 @@ such as 1ms. You'll notice that the response time does not fluctuate - that is b | ||||
| different goroutines, there's a global lock that prevents multiple services from running at the same time. | ||||
|  | ||||
| Unfortunately, there is a drawback. If you have a lot of services, including some that are very slow or prone to time out (the default | ||||
| time out is 10s for HTTP and 5s for TCP), then it means that for the entire duration of the request, no other services can be evaluated. | ||||
| timeout is 10s), then it means that for the entire duration of the request, no other services can be evaluated. | ||||
|  | ||||
| **This does mean that Gatus will be unable to evaluate the health of other services**.  | ||||
| The interval does not include the duration of the request itself, which means that if a service has an interval of 30s  | ||||
| @ -762,11 +800,13 @@ simple health checks used for alerting (PagerDuty/Twilio) to `30s`. | ||||
| | Protocol | Timeout | | ||||
| |:-------- |:------- | | ||||
| | HTTP     | 10s | ||||
| | TCP      | 5s | ||||
| | TCP      | 10s | ||||
| | ICMP     | 10s | ||||
|  | ||||
| To modify the timeout, see [Client configuration](#client-configuration). | ||||
|  | ||||
|  | ||||
| ### Monitoring a TCP service | ||||
|  | ||||
| By prefixing `services[].url` with `tcp:\\`, you can monitor TCP services at a very basic level: | ||||
|  | ||||
| ```yaml | ||||
| @ -1006,4 +1046,3 @@ No such header is required to query the API. | ||||
| You can find the full list of sponsors [here](https://github.com/sponsors/TwinProduction). | ||||
|  | ||||
| [<img src="https://github.com/math280h.png" width="35" />](https://github.com/math280h) | ||||
| [<img src="https://github.com/mateothegreat.png" width="35" />](https://github.com/mateothegreat) | ||||
|  | ||||
| @ -5,6 +5,7 @@ import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"log" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"strings" | ||||
| @ -19,18 +20,29 @@ import ( | ||||
| type AlertProvider struct { | ||||
| 	URL          string                       `yaml:"url"` | ||||
| 	Method       string                       `yaml:"method,omitempty"` | ||||
| 	Insecure     bool                         `yaml:"insecure,omitempty"` | ||||
| 	Insecure     bool                         `yaml:"insecure,omitempty"` // deprecated | ||||
| 	Body         string                       `yaml:"body,omitempty"` | ||||
| 	Headers      map[string]string            `yaml:"headers,omitempty"` | ||||
| 	Placeholders map[string]map[string]string `yaml:"placeholders,omitempty"` | ||||
|  | ||||
| 	// ClientConfig is the configuration of the client used to communicate with the provider's target | ||||
| 	ClientConfig *client.Config `yaml:"client"` | ||||
|  | ||||
| 	// DefaultAlert is the default alert configuration to use for services with an alert of the appropriate type | ||||
| 	DefaultAlert *alert.Alert `yaml:"default-alert"` | ||||
| } | ||||
|  | ||||
| // IsValid returns whether the provider's configuration is valid | ||||
| func (provider *AlertProvider) IsValid() bool { | ||||
| 	return len(provider.URL) > 0 | ||||
| 	if provider.ClientConfig == nil { | ||||
| 		provider.ClientConfig = client.GetDefaultConfig() | ||||
| 		// XXX: remove the next 3 lines in v3.0.0 | ||||
| 		if provider.Insecure { | ||||
| 			log.Println("WARNING: alerting.*.insecure has been deprecated and will be removed in v3.0.0 in favor of alerting.*.client.insecure") | ||||
| 			provider.ClientConfig.Insecure = true | ||||
| 		} | ||||
| 	} | ||||
| 	return len(provider.URL) > 0 && provider.ClientConfig != nil | ||||
| } | ||||
|  | ||||
| // ToCustomAlertProvider converts the provider into a custom.AlertProvider | ||||
| @ -103,7 +115,7 @@ func (provider *AlertProvider) Send(serviceName, alertDescription string, resolv | ||||
| 		return []byte("{}"), nil | ||||
| 	} | ||||
| 	request := provider.buildHTTPRequest(serviceName, alertDescription, resolved) | ||||
| 	response, err := client.GetHTTPClient(provider.Insecure).Do(request) | ||||
| 	response, err := client.GetHTTPClient(provider.ClientConfig).Do(request) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| @ -2,17 +2,22 @@ package mattermost | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 	"net/http" | ||||
|  | ||||
| 	"github.com/TwinProduction/gatus/alerting/alert" | ||||
| 	"github.com/TwinProduction/gatus/alerting/provider/custom" | ||||
| 	"github.com/TwinProduction/gatus/client" | ||||
| 	"github.com/TwinProduction/gatus/core" | ||||
| ) | ||||
|  | ||||
| // AlertProvider is the configuration necessary for sending an alert using Mattermost | ||||
| type AlertProvider struct { | ||||
| 	WebhookURL string `yaml:"webhook-url"` | ||||
| 	Insecure   bool   `yaml:"insecure,omitempty"` | ||||
| 	Insecure   bool   `yaml:"insecure,omitempty"` // deprecated | ||||
|  | ||||
| 	// ClientConfig is the configuration of the client used to communicate with the provider's target | ||||
| 	ClientConfig *client.Config `yaml:"client"` | ||||
|  | ||||
| 	// DefaultAlert is the default alert configuration to use for services with an alert of the appropriate type | ||||
| 	DefaultAlert *alert.Alert `yaml:"default-alert"` | ||||
| @ -20,6 +25,14 @@ type AlertProvider struct { | ||||
|  | ||||
| // IsValid returns whether the provider's configuration is valid | ||||
| func (provider *AlertProvider) IsValid() bool { | ||||
| 	if provider.ClientConfig == nil { | ||||
| 		provider.ClientConfig = client.GetDefaultConfig() | ||||
| 		// XXX: remove the next 3 lines in v3.0.0 | ||||
| 		if provider.Insecure { | ||||
| 			log.Println("WARNING: alerting.mattermost.insecure has been deprecated and will be removed in v3.0.0 in favor of alerting.mattermost.client.insecure") | ||||
| 			provider.ClientConfig.Insecure = true | ||||
| 		} | ||||
| 	} | ||||
| 	return len(provider.WebhookURL) > 0 | ||||
| } | ||||
|  | ||||
| @ -45,9 +58,9 @@ func (provider *AlertProvider) ToCustomAlertProvider(service *core.Service, aler | ||||
| 		results += fmt.Sprintf("%s - `%s`\\n", prefix, conditionResult.Condition) | ||||
| 	} | ||||
| 	return &custom.AlertProvider{ | ||||
| 		URL:      provider.WebhookURL, | ||||
| 		Method:   http.MethodPost, | ||||
| 		Insecure: provider.Insecure, | ||||
| 		URL:          provider.WebhookURL, | ||||
| 		Method:       http.MethodPost, | ||||
| 		ClientConfig: provider.ClientConfig, | ||||
| 		Body: fmt.Sprintf(`{ | ||||
|   "text": "", | ||||
|   "username": "gatus", | ||||
|  | ||||
| @ -14,58 +14,14 @@ import ( | ||||
| 	"github.com/go-ping/ping" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	secureHTTPClient   *http.Client | ||||
| 	insecureHTTPClient *http.Client | ||||
|  | ||||
| 	// pingTimeout is the timeout for the Ping function | ||||
| 	// This is mainly exposed for testing purposes | ||||
| 	pingTimeout = 5 * time.Second | ||||
|  | ||||
| 	// httpTimeout is the timeout for secureHTTPClient and insecureHTTPClient | ||||
| 	httpTimeout = 10 * time.Second | ||||
| ) | ||||
|  | ||||
| // GetHTTPClient returns the shared HTTP client | ||||
| func GetHTTPClient(insecure bool) *http.Client { | ||||
| 	if insecure { | ||||
| 		if insecureHTTPClient == nil { | ||||
| 			insecureHTTPClient = &http.Client{ | ||||
| 				Timeout: httpTimeout, | ||||
| 				Transport: &http.Transport{ | ||||
| 					MaxIdleConns:        100, | ||||
| 					MaxIdleConnsPerHost: 20, | ||||
| 					Proxy:               http.ProxyFromEnvironment, | ||||
| 					TLSClientConfig: &tls.Config{ | ||||
| 						InsecureSkipVerify: true, | ||||
| 					}, | ||||
| 				}, | ||||
| 				CheckRedirect: func(req *http.Request, via []*http.Request) error { | ||||
| 					return http.ErrUseLastResponse // Don't follow redirects | ||||
| 				}, | ||||
| 			} | ||||
| 		} | ||||
| 		return insecureHTTPClient | ||||
| 	} | ||||
| 	if secureHTTPClient == nil { | ||||
| 		secureHTTPClient = &http.Client{ | ||||
| 			Timeout: httpTimeout, | ||||
| 			Transport: &http.Transport{ | ||||
| 				MaxIdleConns:        100, | ||||
| 				MaxIdleConnsPerHost: 20, | ||||
| 				Proxy:               http.ProxyFromEnvironment, | ||||
| 			}, | ||||
| 			CheckRedirect: func(req *http.Request, via []*http.Request) error { | ||||
| 				return http.ErrUseLastResponse // Don't follow redirects | ||||
| 			}, | ||||
| 		} | ||||
| 	} | ||||
| 	return secureHTTPClient | ||||
| func GetHTTPClient(config *Config) *http.Client { | ||||
| 	return config.GetHTTPClient() | ||||
| } | ||||
|  | ||||
| // CanCreateTCPConnection checks whether a connection can be established with a TCP service | ||||
| func CanCreateTCPConnection(address string) bool { | ||||
| 	conn, err := net.DialTimeout("tcp", address, 5*time.Second) | ||||
| func CanCreateTCPConnection(address string, config *Config) bool { | ||||
| 	conn, err := net.DialTimeout("tcp", address, config.Timeout) | ||||
| 	if err != nil { | ||||
| 		return false | ||||
| 	} | ||||
| @ -74,7 +30,7 @@ func CanCreateTCPConnection(address string) bool { | ||||
| } | ||||
|  | ||||
| // CanPerformStartTLS checks whether a connection can be established to an address using the STARTTLS protocol | ||||
| func CanPerformStartTLS(address string, insecure bool) (connected bool, certificate *x509.Certificate, err error) { | ||||
| func CanPerformStartTLS(address string, config *Config) (connected bool, certificate *x509.Certificate, err error) { | ||||
| 	hostAndPort := strings.Split(address, ":") | ||||
| 	if len(hostAndPort) != 2 { | ||||
| 		return false, nil, errors.New("invalid address for starttls, format must be host:port") | ||||
| @ -84,7 +40,7 @@ func CanPerformStartTLS(address string, insecure bool) (connected bool, certific | ||||
| 		return | ||||
| 	} | ||||
| 	err = smtpClient.StartTLS(&tls.Config{ | ||||
| 		InsecureSkipVerify: insecure, | ||||
| 		InsecureSkipVerify: config.Insecure, | ||||
| 		ServerName:         hostAndPort[0], | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| @ -101,13 +57,13 @@ func CanPerformStartTLS(address string, insecure bool) (connected bool, certific | ||||
| // Ping checks if an address can be pinged and returns the round-trip time if the address can be pinged | ||||
| // | ||||
| // Note that this function takes at least 100ms, even if the address is 127.0.0.1 | ||||
| func Ping(address string) (bool, time.Duration) { | ||||
| func Ping(address string, config *Config) (bool, time.Duration) { | ||||
| 	pinger, err := ping.NewPinger(address) | ||||
| 	if err != nil { | ||||
| 		return false, 0 | ||||
| 	} | ||||
| 	pinger.Count = 1 | ||||
| 	pinger.Timeout = pingTimeout | ||||
| 	pinger.Timeout = config.Timeout | ||||
| 	// Set the pinger's privileged mode to true for every operating system except darwin | ||||
| 	// https://github.com/TwinProduction/gatus/issues/132 | ||||
| 	pinger.SetPrivileged(runtime.GOOS != "darwin") | ||||
|  | ||||
| @ -6,43 +6,28 @@ import ( | ||||
| ) | ||||
|  | ||||
| func TestGetHTTPClient(t *testing.T) { | ||||
| 	if secureHTTPClient != nil { | ||||
| 		t.Error("secureHTTPClient should've been nil since it hasn't been called a single time yet") | ||||
| 	} | ||||
| 	if insecureHTTPClient != nil { | ||||
| 		t.Error("insecureHTTPClient should've been nil since it hasn't been called a single time yet") | ||||
| 	} | ||||
| 	_ = GetHTTPClient(false) | ||||
| 	if secureHTTPClient == nil { | ||||
| 		t.Error("secureHTTPClient shouldn't have been nil, since it has been called once") | ||||
| 	} | ||||
| 	if insecureHTTPClient != nil { | ||||
| 		t.Error("insecureHTTPClient should've been nil since it hasn't been called a single time yet") | ||||
| 	} | ||||
| 	_ = GetHTTPClient(true) | ||||
| 	if secureHTTPClient == nil { | ||||
| 		t.Error("secureHTTPClient shouldn't have been nil, since it has been called once") | ||||
| 	} | ||||
| 	if insecureHTTPClient == nil { | ||||
| 		t.Error("insecureHTTPClient shouldn't have been nil, since it has been called once") | ||||
| 	} | ||||
| 	GetHTTPClient(&Config{ | ||||
| 		Insecure:       false, | ||||
| 		IgnoreRedirect: false, | ||||
| 		Timeout:        0, | ||||
| 		httpClient:     nil, | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func TestPing(t *testing.T) { | ||||
| 	pingTimeout = 500 * time.Millisecond | ||||
| 	if success, rtt := Ping("127.0.0.1"); !success { | ||||
| 	if success, rtt := Ping("127.0.0.1", &Config{Timeout: 500 * time.Millisecond}); !success { | ||||
| 		t.Error("expected true") | ||||
| 		if rtt == 0 { | ||||
| 			t.Error("Round-trip time returned on success should've higher than 0") | ||||
| 		} | ||||
| 	} | ||||
| 	if success, rtt := Ping("256.256.256.256"); success { | ||||
| 	if success, rtt := Ping("256.256.256.256", &Config{Timeout: 500 * time.Millisecond}); success { | ||||
| 		t.Error("expected false, because the IP is invalid") | ||||
| 		if rtt != 0 { | ||||
| 			t.Error("Round-trip time returned on failure should've been 0") | ||||
| 		} | ||||
| 	} | ||||
| 	if success, rtt := Ping("192.168.152.153"); success { | ||||
| 	if success, rtt := Ping("192.168.152.153", &Config{Timeout: 500 * time.Millisecond}); success { | ||||
| 		t.Error("expected false, because the IP is valid but the host should be unreachable") | ||||
| 		if rtt != 0 { | ||||
| 			t.Error("Round-trip time returned on failure should've been 0") | ||||
| @ -88,7 +73,7 @@ func TestCanPerformStartTLS(t *testing.T) { | ||||
| 	} | ||||
| 	for _, tt := range tests { | ||||
| 		t.Run(tt.name, func(t *testing.T) { | ||||
| 			connected, _, err := CanPerformStartTLS(tt.args.address, tt.args.insecure) | ||||
| 			connected, _, err := CanPerformStartTLS(tt.args.address, &Config{Insecure: tt.args.insecure, Timeout: 5 * time.Second}) | ||||
| 			if (err != nil) != tt.wantErr { | ||||
| 				t.Errorf("CanPerformStartTLS() err=%v, wantErr=%v", err, tt.wantErr) | ||||
| 				return | ||||
| @ -101,7 +86,7 @@ func TestCanPerformStartTLS(t *testing.T) { | ||||
| } | ||||
|  | ||||
| func TestCanCreateTCPConnection(t *testing.T) { | ||||
| 	if CanCreateTCPConnection("127.0.0.1") { | ||||
| 	if CanCreateTCPConnection("127.0.0.1", &Config{Timeout: 5 * time.Second}) { | ||||
| 		t.Error("should've failed, because there's no port in the address") | ||||
| 	} | ||||
| } | ||||
|  | ||||
							
								
								
									
										73
									
								
								client/config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								client/config.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,73 @@ | ||||
| package client | ||||
|  | ||||
| import ( | ||||
| 	"crypto/tls" | ||||
| 	"net/http" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	defaultHTTPTimeout = 10 * time.Second | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	// DefaultConfig is the default client configuration | ||||
| 	defaultConfig = Config{ | ||||
| 		Insecure:       false, | ||||
| 		IgnoreRedirect: false, | ||||
| 		Timeout:        defaultHTTPTimeout, | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| // GetDefaultConfig returns a copy of the default configuration | ||||
| func GetDefaultConfig() *Config { | ||||
| 	cfg := defaultConfig | ||||
| 	return &cfg | ||||
| } | ||||
|  | ||||
| // Config is the configuration for clients | ||||
| type Config struct { | ||||
| 	// Insecure determines whether to skip verifying the server's certificate chain and host name | ||||
| 	Insecure bool `yaml:"insecure"` | ||||
|  | ||||
| 	// IgnoreRedirect determines whether to ignore redirects (true) or follow them (false, default) | ||||
| 	IgnoreRedirect bool `yaml:"ignore-redirect"` | ||||
|  | ||||
| 	// Timeout for the client | ||||
| 	Timeout time.Duration `yaml:"timeout"` | ||||
|  | ||||
| 	httpClient *http.Client | ||||
| } | ||||
|  | ||||
| // ValidateAndSetDefaults validates the client configuration and sets the default values if necessary | ||||
| func (c *Config) ValidateAndSetDefaults() { | ||||
| 	if c.Timeout < time.Millisecond { | ||||
| 		c.Timeout = 10 * time.Second | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // GetHTTPClient return a HTTP client matching the Config's parameters. | ||||
| func (c *Config) GetHTTPClient() *http.Client { | ||||
| 	if c.httpClient == nil { | ||||
| 		c.httpClient = &http.Client{ | ||||
| 			Timeout: c.Timeout, | ||||
| 			Transport: &http.Transport{ | ||||
| 				MaxIdleConns:        100, | ||||
| 				MaxIdleConnsPerHost: 20, | ||||
| 				Proxy:               http.ProxyFromEnvironment, | ||||
| 				TLSClientConfig: &tls.Config{ | ||||
| 					InsecureSkipVerify: c.Insecure, | ||||
| 				}, | ||||
| 			}, | ||||
| 			CheckRedirect: func(req *http.Request, via []*http.Request) error { | ||||
| 				if c.IgnoreRedirect { | ||||
| 					// Don't follow redirects | ||||
| 					return http.ErrUseLastResponse | ||||
| 				} | ||||
| 				// Follow redirects | ||||
| 				return nil | ||||
| 			}, | ||||
| 		} | ||||
| 	} | ||||
| 	return c.httpClient | ||||
| } | ||||
							
								
								
									
										37
									
								
								client/config_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								client/config_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | ||||
| package client | ||||
|  | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"testing" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| func TestConfig_GetHTTPClient(t *testing.T) { | ||||
| 	insecureConfig := &Config{Insecure: true} | ||||
| 	insecureConfig.ValidateAndSetDefaults() | ||||
| 	insecureClient := insecureConfig.GetHTTPClient() | ||||
| 	if !(insecureClient.Transport).(*http.Transport).TLSClientConfig.InsecureSkipVerify { | ||||
| 		t.Error("expected Config.Insecure set to true to cause the HTTP client to skip certificate verification") | ||||
| 	} | ||||
| 	if insecureClient.Timeout != defaultHTTPTimeout { | ||||
| 		t.Error("expected Config.Timeout to default the HTTP client to a timeout of 10s") | ||||
| 	} | ||||
| 	request, _ := http.NewRequest("GET", "", nil) | ||||
| 	if err := insecureClient.CheckRedirect(request, nil); err != nil { | ||||
| 		t.Error("expected Config.IgnoreRedirect set to false to cause the HTTP client's CheckRedirect to return nil") | ||||
| 	} | ||||
|  | ||||
| 	secureConfig := &Config{IgnoreRedirect: true, Timeout: 5 * time.Second} | ||||
| 	secureConfig.ValidateAndSetDefaults() | ||||
| 	secureClient := secureConfig.GetHTTPClient() | ||||
| 	if (secureClient.Transport).(*http.Transport).TLSClientConfig.InsecureSkipVerify { | ||||
| 		t.Error("expected Config.Insecure set to false to cause the HTTP client to not skip certificate verification") | ||||
| 	} | ||||
| 	if secureClient.Timeout != 5*time.Second { | ||||
| 		t.Error("expected Config.Timeout to cause the HTTP client to have a timeout of 5s") | ||||
| 	} | ||||
| 	request, _ = http.NewRequest("GET", "", nil) | ||||
| 	if err := secureClient.CheckRedirect(request, nil); err != http.ErrUseLastResponse { | ||||
| 		t.Error("expected Config.IgnoreRedirect set to true to cause the HTTP client's CheckRedirect to return http.ErrUseLastResponse") | ||||
| 	} | ||||
| } | ||||
| @ -16,6 +16,7 @@ import ( | ||||
| 	"github.com/TwinProduction/gatus/alerting/provider/slack" | ||||
| 	"github.com/TwinProduction/gatus/alerting/provider/telegram" | ||||
| 	"github.com/TwinProduction/gatus/alerting/provider/twilio" | ||||
| 	"github.com/TwinProduction/gatus/client" | ||||
| 	"github.com/TwinProduction/gatus/core" | ||||
| 	"github.com/TwinProduction/gatus/k8stest" | ||||
| 	v1 "k8s.io/api/core/v1" | ||||
| @ -40,17 +41,31 @@ func TestParseAndValidateConfigBytes(t *testing.T) { | ||||
| 	config, err := parseAndValidateConfigBytes([]byte(fmt.Sprintf(` | ||||
| storage: | ||||
|   file: %s | ||||
|  | ||||
| services: | ||||
|   - name: twinnation | ||||
|     url: https://twinnation.org/health | ||||
|     interval: 15s | ||||
|     conditions: | ||||
|       - "[STATUS] == 200" | ||||
|  | ||||
|   - name: github | ||||
|     url: https://api.github.com/healthz | ||||
|     client: | ||||
|       insecure: true | ||||
|       ignore-redirect: true | ||||
|       timeout: 5s | ||||
|     conditions: | ||||
|       - "[STATUS] != 400" | ||||
|       - "[STATUS] != 500" | ||||
|  | ||||
|   - name: example | ||||
|     url: https://example.com/ | ||||
|     interval: 30m | ||||
|     client: | ||||
|       insecure: true | ||||
|     conditions: | ||||
|       - "[STATUS] == 200" | ||||
| `, file))) | ||||
| 	if err != nil { | ||||
| 		t.Error("expected no error, got", err.Error()) | ||||
| @ -58,33 +73,75 @@ services: | ||||
| 	if config == nil { | ||||
| 		t.Fatal("Config shouldn't have been nil") | ||||
| 	} | ||||
| 	if len(config.Services) != 2 { | ||||
| 	if len(config.Services) != 3 { | ||||
| 		t.Error("Should have returned two services") | ||||
| 	} | ||||
|  | ||||
| 	if config.Services[0].URL != "https://twinnation.org/health" { | ||||
| 		t.Errorf("URL should have been %s", "https://twinnation.org/health") | ||||
| 	} | ||||
| 	if config.Services[1].URL != "https://api.github.com/healthz" { | ||||
| 		t.Errorf("URL should have been %s", "https://api.github.com/healthz") | ||||
| 	} | ||||
| 	if config.Services[0].Method != "GET" { | ||||
| 		t.Errorf("Method should have been %s (default)", "GET") | ||||
| 	} | ||||
| 	if config.Services[1].Method != "GET" { | ||||
| 		t.Errorf("Method should have been %s (default)", "GET") | ||||
| 	} | ||||
| 	if config.Services[0].Interval != 15*time.Second { | ||||
| 		t.Errorf("Interval should have been %s", 15*time.Second) | ||||
| 	} | ||||
| 	if config.Services[1].Interval != 60*time.Second { | ||||
| 		t.Errorf("Interval should have been %s, because it is the default value", 60*time.Second) | ||||
| 	if config.Services[0].ClientConfig.Insecure != client.GetDefaultConfig().Insecure { | ||||
| 		t.Errorf("ClientConfig.Insecure should have been %v, got %v", true, config.Services[0].ClientConfig.Insecure) | ||||
| 	} | ||||
| 	if config.Services[0].ClientConfig.IgnoreRedirect != client.GetDefaultConfig().IgnoreRedirect { | ||||
| 		t.Errorf("ClientConfig.IgnoreRedirect should have been %v, got %v", true, config.Services[0].ClientConfig.IgnoreRedirect) | ||||
| 	} | ||||
| 	if config.Services[0].ClientConfig.Timeout != client.GetDefaultConfig().Timeout { | ||||
| 		t.Errorf("ClientConfig.Timeout should have been %v, got %v", client.GetDefaultConfig().Timeout, config.Services[0].ClientConfig.Timeout) | ||||
| 	} | ||||
| 	if len(config.Services[0].Conditions) != 1 { | ||||
| 		t.Errorf("There should have been %d conditions", 1) | ||||
| 	} | ||||
|  | ||||
| 	if config.Services[1].URL != "https://api.github.com/healthz" { | ||||
| 		t.Errorf("URL should have been %s", "https://api.github.com/healthz") | ||||
| 	} | ||||
| 	if config.Services[1].Method != "GET" { | ||||
| 		t.Errorf("Method should have been %s (default)", "GET") | ||||
| 	} | ||||
| 	if config.Services[1].Interval != 60*time.Second { | ||||
| 		t.Errorf("Interval should have been %s, because it is the default value", 60*time.Second) | ||||
| 	} | ||||
| 	if !config.Services[1].ClientConfig.Insecure { | ||||
| 		t.Errorf("ClientConfig.Insecure should have been %v, got %v", true, config.Services[1].ClientConfig.Insecure) | ||||
| 	} | ||||
| 	if !config.Services[1].ClientConfig.IgnoreRedirect { | ||||
| 		t.Errorf("ClientConfig.IgnoreRedirect should have been %v, got %v", true, config.Services[1].ClientConfig.IgnoreRedirect) | ||||
| 	} | ||||
| 	if config.Services[1].ClientConfig.Timeout != 5*time.Second { | ||||
| 		t.Errorf("ClientConfig.Timeout should have been %v, got %v", 5*time.Second, config.Services[1].ClientConfig.Timeout) | ||||
| 	} | ||||
| 	if len(config.Services[1].Conditions) != 2 { | ||||
| 		t.Errorf("There should have been %d conditions", 2) | ||||
| 	} | ||||
|  | ||||
| 	if config.Services[2].URL != "https://example.com/" { | ||||
| 		t.Errorf("URL should have been %s", "https://example.com/") | ||||
| 	} | ||||
| 	if config.Services[2].Method != "GET" { | ||||
| 		t.Errorf("Method should have been %s (default)", "GET") | ||||
| 	} | ||||
| 	if config.Services[2].Interval != 30*time.Minute { | ||||
| 		t.Errorf("Interval should have been %s, because it is the default value", 30*time.Minute) | ||||
| 	} | ||||
| 	if !config.Services[2].ClientConfig.Insecure { | ||||
| 		t.Errorf("ClientConfig.Insecure should have been %v, got %v", true, config.Services[2].ClientConfig.Insecure) | ||||
| 	} | ||||
| 	if config.Services[2].ClientConfig.IgnoreRedirect { | ||||
| 		t.Errorf("ClientConfig.IgnoreRedirect should have been %v by default, got %v", false, config.Services[2].ClientConfig.IgnoreRedirect) | ||||
| 	} | ||||
| 	if config.Services[2].ClientConfig.Timeout != 10*time.Second { | ||||
| 		t.Errorf("ClientConfig.Timeout should have been %v by default, got %v", 10*time.Second, config.Services[2].ClientConfig.Timeout) | ||||
| 	} | ||||
| 	if len(config.Services[2].Conditions) != 1 { | ||||
| 		t.Errorf("There should have been %d conditions", 1) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestParseAndValidateConfigBytesDefault(t *testing.T) { | ||||
| @ -104,17 +161,26 @@ services: | ||||
| 	if config.Metrics { | ||||
| 		t.Error("Metrics should've been false by default") | ||||
| 	} | ||||
| 	if config.Web.Address != DefaultAddress { | ||||
| 		t.Errorf("Bind address should have been %s, because it is the default value", DefaultAddress) | ||||
| 	} | ||||
| 	if config.Web.Port != DefaultPort { | ||||
| 		t.Errorf("Port should have been %d, because it is the default value", DefaultPort) | ||||
| 	} | ||||
| 	if config.Services[0].URL != "https://twinnation.org/health" { | ||||
| 		t.Errorf("URL should have been %s", "https://twinnation.org/health") | ||||
| 	} | ||||
| 	if config.Services[0].Interval != 60*time.Second { | ||||
| 		t.Errorf("Interval should have been %s, because it is the default value", 60*time.Second) | ||||
| 	} | ||||
| 	if config.Web.Address != DefaultAddress { | ||||
| 		t.Errorf("Bind address should have been %s, because it is the default value", DefaultAddress) | ||||
| 	if config.Services[0].ClientConfig.Insecure != client.GetDefaultConfig().Insecure { | ||||
| 		t.Errorf("ClientConfig.Insecure should have been %v by default, got %v", true, config.Services[0].ClientConfig.Insecure) | ||||
| 	} | ||||
| 	if config.Web.Port != DefaultPort { | ||||
| 		t.Errorf("Port should have been %d, because it is the default value", DefaultPort) | ||||
| 	if config.Services[0].ClientConfig.IgnoreRedirect != client.GetDefaultConfig().IgnoreRedirect { | ||||
| 		t.Errorf("ClientConfig.IgnoreRedirect should have been %v by default, got %v", true, config.Services[0].ClientConfig.IgnoreRedirect) | ||||
| 	} | ||||
| 	if config.Services[0].ClientConfig.Timeout != client.GetDefaultConfig().Timeout { | ||||
| 		t.Errorf("ClientConfig.Timeout should have been %v by default, got %v", client.GetDefaultConfig().Timeout, config.Services[0].ClientConfig.Timeout) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -143,11 +209,9 @@ services: | ||||
| 	if config.Services[0].Interval != 60*time.Second { | ||||
| 		t.Errorf("Interval should have been %s, because it is the default value", 60*time.Second) | ||||
| 	} | ||||
|  | ||||
| 	if config.Web.Address != "127.0.0.1" { | ||||
| 		t.Errorf("Bind address should have been %s, because it is specified in config", "127.0.0.1") | ||||
| 	} | ||||
|  | ||||
| 	if config.Web.Port != DefaultPort { | ||||
| 		t.Errorf("Port should have been %d, because it is the default value", DefaultPort) | ||||
| 	} | ||||
| @ -339,6 +403,8 @@ alerting: | ||||
|     integration-key: "00000000000000000000000000000000" | ||||
|   mattermost: | ||||
|     webhook-url: "http://example.com" | ||||
|     client: | ||||
|       insecure: true | ||||
|   messagebird: | ||||
|     access-key: "1" | ||||
|     originator: "31619191918" | ||||
| @ -895,6 +961,9 @@ services: | ||||
| 	if config.Alerting.Custom.Insecure { | ||||
| 		t.Fatal("config.Alerting.Custom.Insecure shouldn't have been true") | ||||
| 	} | ||||
| 	if config.Alerting.Custom.ClientConfig.Insecure { | ||||
| 		t.Errorf("ClientConfig.Insecure should have been %v, got %v", false, config.Alerting.Custom.ClientConfig.Insecure) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestParseAndValidateConfigBytesWithCustomAlertingConfigAndCustomPlaceholderValues(t *testing.T) { | ||||
|  | ||||
| @ -30,7 +30,6 @@ var ( | ||||
| 		Interval:                30 * time.Second, | ||||
| 		Conditions:              []*core.Condition{&firstCondition, &secondCondition, &thirdCondition}, | ||||
| 		Alerts:                  nil, | ||||
| 		Insecure:                false, | ||||
| 		NumberOfFailuresInARow:  0, | ||||
| 		NumberOfSuccessesInARow: 0, | ||||
| 	} | ||||
|  | ||||
| @ -6,6 +6,7 @@ import ( | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"io/ioutil" | ||||
| 	"log" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"net/url" | ||||
| @ -78,8 +79,13 @@ type Service struct { | ||||
| 	Alerts []*alert.Alert `yaml:"alerts"` | ||||
|  | ||||
| 	// Insecure is whether to skip verifying the server's certificate chain and host name | ||||
| 	// | ||||
| 	// deprecated | ||||
| 	Insecure bool `yaml:"insecure,omitempty"` | ||||
|  | ||||
| 	// ClientConfig is the configuration of the client used to communicate with the service's target | ||||
| 	ClientConfig *client.Config `yaml:"client"` | ||||
|  | ||||
| 	// NumberOfFailuresInARow is the number of unsuccessful evaluations in a row | ||||
| 	NumberOfFailuresInARow int | ||||
|  | ||||
| @ -90,6 +96,16 @@ type Service struct { | ||||
| // ValidateAndSetDefaults validates the service's configuration and sets the default value of fields that have one | ||||
| func (service *Service) ValidateAndSetDefaults() error { | ||||
| 	// Set default values | ||||
| 	if service.ClientConfig == nil { | ||||
| 		service.ClientConfig = client.GetDefaultConfig() | ||||
| 		// XXX: remove the next 3 lines in v3.0.0 | ||||
| 		if service.Insecure { | ||||
| 			log.Println("WARNING: services[].insecure has been deprecated and will be removed in v3.0.0 in favor of services[].client.insecure") | ||||
| 			service.ClientConfig.Insecure = true | ||||
| 		} | ||||
| 	} else { | ||||
| 		service.ClientConfig.ValidateAndSetDefaults() | ||||
| 	} | ||||
| 	if service.Interval == 0 { | ||||
| 		service.Interval = 1 * time.Minute | ||||
| 	} | ||||
| @ -199,7 +215,7 @@ func (service *Service) call(result *Result) { | ||||
| 		service.DNS.query(service.URL, result) | ||||
| 		result.Duration = time.Since(startTime) | ||||
| 	} else if isServiceStartTLS { | ||||
| 		result.Connected, certificate, err = client.CanPerformStartTLS(strings.TrimPrefix(service.URL, "starttls://"), service.Insecure) | ||||
| 		result.Connected, certificate, err = client.CanPerformStartTLS(strings.TrimPrefix(service.URL, "starttls://"), service.ClientConfig) | ||||
| 		if err != nil { | ||||
| 			result.AddError(err.Error()) | ||||
| 			return | ||||
| @ -207,12 +223,12 @@ func (service *Service) call(result *Result) { | ||||
| 		result.Duration = time.Since(startTime) | ||||
| 		result.CertificateExpiration = time.Until(certificate.NotAfter) | ||||
| 	} else if isServiceTCP { | ||||
| 		result.Connected = client.CanCreateTCPConnection(strings.TrimPrefix(service.URL, "tcp://")) | ||||
| 		result.Connected = client.CanCreateTCPConnection(strings.TrimPrefix(service.URL, "tcp://"), service.ClientConfig) | ||||
| 		result.Duration = time.Since(startTime) | ||||
| 	} else if isServiceICMP { | ||||
| 		result.Connected, result.Duration = client.Ping(strings.TrimPrefix(service.URL, "icmp://")) | ||||
| 		result.Connected, result.Duration = client.Ping(strings.TrimPrefix(service.URL, "icmp://"), service.ClientConfig) | ||||
| 	} else { | ||||
| 		response, err = client.GetHTTPClient(service.Insecure).Do(request) | ||||
| 		response, err = client.GetHTTPClient(service.ClientConfig).Do(request) | ||||
| 		result.Duration = time.Since(startTime) | ||||
| 		if err != nil { | ||||
| 			result.AddError(err.Error()) | ||||
|  | ||||
| @ -7,6 +7,7 @@ import ( | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/TwinProduction/gatus/alerting/alert" | ||||
| 	"github.com/TwinProduction/gatus/client" | ||||
| ) | ||||
|  | ||||
| func TestService_ValidateAndSetDefaults(t *testing.T) { | ||||
| @ -18,6 +19,19 @@ func TestService_ValidateAndSetDefaults(t *testing.T) { | ||||
| 		Alerts:     []*alert.Alert{{Type: alert.TypePagerDuty}}, | ||||
| 	} | ||||
| 	service.ValidateAndSetDefaults() | ||||
| 	if service.ClientConfig == nil { | ||||
| 		t.Error("client configuration should've been set to the default configuration") | ||||
| 	} else { | ||||
| 		if service.ClientConfig.Insecure != client.GetDefaultConfig().Insecure { | ||||
| 			t.Errorf("Default client configuration should've set Insecure to %v, got %v", client.GetDefaultConfig().Insecure, service.ClientConfig.Insecure) | ||||
| 		} | ||||
| 		if service.ClientConfig.IgnoreRedirect != client.GetDefaultConfig().IgnoreRedirect { | ||||
| 			t.Errorf("Default client configuration should've set IgnoreRedirect to %v, got %v", client.GetDefaultConfig().IgnoreRedirect, service.ClientConfig.IgnoreRedirect) | ||||
| 		} | ||||
| 		if service.ClientConfig.Timeout != client.GetDefaultConfig().Timeout { | ||||
| 			t.Errorf("Default client configuration should've set Timeout to %v, got %v", client.GetDefaultConfig().Timeout, service.ClientConfig.Timeout) | ||||
| 		} | ||||
| 	} | ||||
| 	if service.Method != "GET" { | ||||
| 		t.Error("Service method should've defaulted to GET") | ||||
| 	} | ||||
| @ -41,6 +55,34 @@ func TestService_ValidateAndSetDefaults(t *testing.T) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestService_ValidateAndSetDefaultsWithClientConfig(t *testing.T) { | ||||
| 	condition := Condition("[STATUS] == 200") | ||||
| 	service := Service{ | ||||
| 		Name:       "twinnation-health", | ||||
| 		URL:        "https://twinnation.org/health", | ||||
| 		Conditions: []*Condition{&condition}, | ||||
| 		ClientConfig: &client.Config{ | ||||
| 			Insecure:       true, | ||||
| 			IgnoreRedirect: true, | ||||
| 			Timeout:        0, | ||||
| 		}, | ||||
| 	} | ||||
| 	service.ValidateAndSetDefaults() | ||||
| 	if service.ClientConfig == nil { | ||||
| 		t.Error("client configuration should've been set to the default configuration") | ||||
| 	} else { | ||||
| 		if !service.ClientConfig.Insecure { | ||||
| 			t.Error("service.ClientConfig.Insecure should've been set to true") | ||||
| 		} | ||||
| 		if !service.ClientConfig.IgnoreRedirect { | ||||
| 			t.Error("service.ClientConfig.IgnoreRedirect should've been set to true") | ||||
| 		} | ||||
| 		if service.ClientConfig.Timeout != client.GetDefaultConfig().Timeout { | ||||
| 			t.Error("service.ClientConfig.Timeout should've been set to 10s, because the timeout value entered is not set or invalid") | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestService_ValidateAndSetDefaultsWithNoName(t *testing.T) { | ||||
| 	defer func() { recover() }() | ||||
| 	condition := Condition("[STATUS] == 200") | ||||
| @ -205,6 +247,7 @@ func TestIntegrationEvaluateHealth(t *testing.T) { | ||||
| 		URL:        "https://twinnation.org/health", | ||||
| 		Conditions: []*Condition{&condition, &bodyCondition}, | ||||
| 	} | ||||
| 	service.ValidateAndSetDefaults() | ||||
| 	result := service.EvaluateHealth() | ||||
| 	if !result.ConditionResults[0].Success { | ||||
| 		t.Errorf("Condition '%s' should have been a success", condition) | ||||
| @ -224,6 +267,7 @@ func TestIntegrationEvaluateHealthWithFailure(t *testing.T) { | ||||
| 		URL:        "https://twinnation.org/health", | ||||
| 		Conditions: []*Condition{&condition}, | ||||
| 	} | ||||
| 	service.ValidateAndSetDefaults() | ||||
| 	result := service.EvaluateHealth() | ||||
| 	if result.ConditionResults[0].Success { | ||||
| 		t.Errorf("Condition '%s' should have been a failure", condition) | ||||
| @ -248,6 +292,7 @@ func TestIntegrationEvaluateHealthForDNS(t *testing.T) { | ||||
| 		}, | ||||
| 		Conditions: []*Condition{&conditionSuccess, &conditionBody}, | ||||
| 	} | ||||
| 	service.ValidateAndSetDefaults() | ||||
| 	result := service.EvaluateHealth() | ||||
| 	if !result.ConditionResults[0].Success { | ||||
| 		t.Errorf("Conditions '%s' and %s should have been a success", conditionSuccess, conditionBody) | ||||
| @ -267,6 +312,7 @@ func TestIntegrationEvaluateHealthForICMP(t *testing.T) { | ||||
| 		URL:        "icmp://127.0.0.1", | ||||
| 		Conditions: []*Condition{&conditionSuccess}, | ||||
| 	} | ||||
| 	service.ValidateAndSetDefaults() | ||||
| 	result := service.EvaluateHealth() | ||||
| 	if !result.ConditionResults[0].Success { | ||||
| 		t.Errorf("Conditions '%s' should have been a success", conditionSuccess) | ||||
|  | ||||
		Reference in New Issue
	
	Block a user