Compare commits

...

24 Commits

Author SHA1 Message Date
TwinProduction
2d3fe9795f Add v3 to module path
Gatus wasn't intended to be used as a library, but I have a use case now.
2021-10-03 21:53:59 -04:00
TwinProduction
d19f564e4e Ensure that tested hour never goes negative 2021-10-03 21:50:58 -04:00
TwinProduction
babe7b0be9 Update Go to 1.17 2021-10-03 15:03:09 -04:00
TwinProduction
dee04945d0 Minor update 2021-10-03 15:03:09 -04:00
TwinProduction
bf455fb7cc Fix issue with under maintenance feature 2021-10-01 02:33:37 -04:00
TwinProduction
dfd2f7943f Fix issue with privileged call on linux 2021-10-01 02:33:16 -04:00
TwinProduction
fece11540b Remove unnecessary rows.Close() calls 2021-09-30 21:19:57 -04:00
TwinProduction
ac43ef4ab7 Refactor some code 2021-09-30 20:56:09 -04:00
TwinProduction
bc25fea1c0 Minor improvements 2021-09-30 20:45:47 -04:00
Carlotronics
30cb7b6ec8 Health check for SSL/TLS services (#177)
* protocol: starttls: add timeout support

Signed-off-by: Charles Decoux <charles@phowork.fr>

* protocol: add ssl support

Signed-off-by: Charles Decoux <charles@phowork.fr>
2021-09-30 16:15:17 -04:00
TwinProduction
289d834587 Change domain 2021-09-28 18:54:40 -04:00
TwinProduction
428e415616 Fix issue with under maintenance 2021-09-27 00:11:42 -04:00
TwinProduction
0d284c2494 Update front-end dependencies 2021-09-25 13:39:54 -04:00
TwinProduction
4a46a5ae9e Rename security.go and security_test.go to config.go and config_test.go 2021-09-22 00:53:13 -04:00
TwinProduction
df3a2016ff Move web and ui configurations in their own packages 2021-09-22 00:47:51 -04:00
TwinProduction
dda83761b5 Minor fix 2021-09-22 00:11:46 -04:00
TwinProduction
882444e0d5 Minor fix 2021-09-22 00:11:22 -04:00
TwinProduction
fa4736c672 Close #74: Add maintenance window 2021-09-22 00:04:51 -04:00
TwinProduction
dc173b29bc Fix indention 2021-09-18 21:43:19 -04:00
TwinProduction
c3a4ce1eb4 Minor update 2021-09-18 13:04:50 -04:00
TwinProduction
044f0454f8 Domain migration 2021-09-18 12:42:11 -04:00
newsr
9bd5c38a96 Add enabled parameter to service (#175)
* feat: Add enabled flag to service
* Add IsEnabled method

Co-authored-by: 1newsr <1newsr@users.noreply.github.com>
2021-09-18 11:52:11 -04:00
TwinProduction
d6b4c2394a Update frontend dependencies 2021-09-16 22:47:03 -04:00
TwinProduction
9fe4678193 Update line separator 2021-09-16 22:35:22 -04:00
126 changed files with 5399 additions and 8903 deletions

2
.gitattributes vendored
View File

@@ -1 +1 @@
* text=lf
* text=auto eol=lf

2302
README.md

File diff suppressed because it is too large Load Diff

View File

@@ -1,17 +1,17 @@
package alerting
import (
"github.com/TwinProduction/gatus/alerting/alert"
"github.com/TwinProduction/gatus/alerting/provider"
"github.com/TwinProduction/gatus/alerting/provider/custom"
"github.com/TwinProduction/gatus/alerting/provider/discord"
"github.com/TwinProduction/gatus/alerting/provider/mattermost"
"github.com/TwinProduction/gatus/alerting/provider/messagebird"
"github.com/TwinProduction/gatus/alerting/provider/pagerduty"
"github.com/TwinProduction/gatus/alerting/provider/slack"
"github.com/TwinProduction/gatus/alerting/provider/teams"
"github.com/TwinProduction/gatus/alerting/provider/telegram"
"github.com/TwinProduction/gatus/alerting/provider/twilio"
"github.com/TwinProduction/gatus/v3/alerting/alert"
"github.com/TwinProduction/gatus/v3/alerting/provider"
"github.com/TwinProduction/gatus/v3/alerting/provider/custom"
"github.com/TwinProduction/gatus/v3/alerting/provider/discord"
"github.com/TwinProduction/gatus/v3/alerting/provider/mattermost"
"github.com/TwinProduction/gatus/v3/alerting/provider/messagebird"
"github.com/TwinProduction/gatus/v3/alerting/provider/pagerduty"
"github.com/TwinProduction/gatus/v3/alerting/provider/slack"
"github.com/TwinProduction/gatus/v3/alerting/provider/teams"
"github.com/TwinProduction/gatus/v3/alerting/provider/telegram"
"github.com/TwinProduction/gatus/v3/alerting/provider/twilio"
)
// Config is the configuration for alerting providers

View File

@@ -9,9 +9,9 @@ import (
"os"
"strings"
"github.com/TwinProduction/gatus/alerting/alert"
"github.com/TwinProduction/gatus/client"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/v3/alerting/alert"
"github.com/TwinProduction/gatus/v3/client"
"github.com/TwinProduction/gatus/v3/core"
)
// AlertProvider is the configuration necessary for sending an alert using a custom HTTP request

View File

@@ -4,8 +4,8 @@ import (
"io/ioutil"
"testing"
"github.com/TwinProduction/gatus/alerting/alert"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/v3/alerting/alert"
"github.com/TwinProduction/gatus/v3/core"
)
func TestAlertProvider_IsValid(t *testing.T) {

View File

@@ -4,9 +4,9 @@ import (
"fmt"
"net/http"
"github.com/TwinProduction/gatus/alerting/alert"
"github.com/TwinProduction/gatus/alerting/provider/custom"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/v3/alerting/alert"
"github.com/TwinProduction/gatus/v3/alerting/provider/custom"
"github.com/TwinProduction/gatus/v3/core"
)
// AlertProvider is the configuration necessary for sending an alert using Discord

View File

@@ -6,8 +6,8 @@ import (
"strings"
"testing"
"github.com/TwinProduction/gatus/alerting/alert"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/v3/alerting/alert"
"github.com/TwinProduction/gatus/v3/core"
)
func TestAlertProvider_IsValid(t *testing.T) {

View File

@@ -4,10 +4,10 @@ import (
"fmt"
"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"
"github.com/TwinProduction/gatus/v3/alerting/alert"
"github.com/TwinProduction/gatus/v3/alerting/provider/custom"
"github.com/TwinProduction/gatus/v3/client"
"github.com/TwinProduction/gatus/v3/core"
)
// AlertProvider is the configuration necessary for sending an alert using Mattermost

View File

@@ -6,8 +6,8 @@ import (
"strings"
"testing"
"github.com/TwinProduction/gatus/alerting/alert"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/v3/alerting/alert"
"github.com/TwinProduction/gatus/v3/core"
)
func TestAlertProvider_IsValid(t *testing.T) {

View File

@@ -4,9 +4,9 @@ import (
"fmt"
"net/http"
"github.com/TwinProduction/gatus/alerting/alert"
"github.com/TwinProduction/gatus/alerting/provider/custom"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/v3/alerting/alert"
"github.com/TwinProduction/gatus/v3/alerting/provider/custom"
"github.com/TwinProduction/gatus/v3/core"
)
const (
@@ -37,7 +37,6 @@ func (provider *AlertProvider) ToCustomAlertProvider(service *core.Service, aler
} else {
message = fmt.Sprintf("TRIGGERED: %s - %s", service.Name, alert.GetDescription())
}
return &custom.AlertProvider{
URL: restAPIURL,
Method: http.MethodPost,

View File

@@ -6,8 +6,8 @@ import (
"strings"
"testing"
"github.com/TwinProduction/gatus/alerting/alert"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/v3/alerting/alert"
"github.com/TwinProduction/gatus/v3/core"
)
func TestMessagebirdAlertProvider_IsValid(t *testing.T) {

View File

@@ -4,9 +4,9 @@ import (
"fmt"
"net/http"
"github.com/TwinProduction/gatus/alerting/alert"
"github.com/TwinProduction/gatus/alerting/provider/custom"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/v3/alerting/alert"
"github.com/TwinProduction/gatus/v3/alerting/provider/custom"
"github.com/TwinProduction/gatus/v3/core"
)
const (

View File

@@ -6,8 +6,8 @@ import (
"strings"
"testing"
"github.com/TwinProduction/gatus/alerting/alert"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/v3/alerting/alert"
"github.com/TwinProduction/gatus/v3/core"
)
func TestAlertProvider_IsValid(t *testing.T) {

View File

@@ -1,17 +1,17 @@
package provider
import (
"github.com/TwinProduction/gatus/alerting/alert"
"github.com/TwinProduction/gatus/alerting/provider/custom"
"github.com/TwinProduction/gatus/alerting/provider/discord"
"github.com/TwinProduction/gatus/alerting/provider/mattermost"
"github.com/TwinProduction/gatus/alerting/provider/messagebird"
"github.com/TwinProduction/gatus/alerting/provider/pagerduty"
"github.com/TwinProduction/gatus/alerting/provider/slack"
"github.com/TwinProduction/gatus/alerting/provider/teams"
"github.com/TwinProduction/gatus/alerting/provider/telegram"
"github.com/TwinProduction/gatus/alerting/provider/twilio"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/v3/alerting/alert"
"github.com/TwinProduction/gatus/v3/alerting/provider/custom"
"github.com/TwinProduction/gatus/v3/alerting/provider/discord"
"github.com/TwinProduction/gatus/v3/alerting/provider/mattermost"
"github.com/TwinProduction/gatus/v3/alerting/provider/messagebird"
"github.com/TwinProduction/gatus/v3/alerting/provider/pagerduty"
"github.com/TwinProduction/gatus/v3/alerting/provider/slack"
"github.com/TwinProduction/gatus/v3/alerting/provider/teams"
"github.com/TwinProduction/gatus/v3/alerting/provider/telegram"
"github.com/TwinProduction/gatus/v3/alerting/provider/twilio"
"github.com/TwinProduction/gatus/v3/core"
)
// AlertProvider is the interface that each providers should implement

View File

@@ -3,7 +3,7 @@ package provider
import (
"testing"
"github.com/TwinProduction/gatus/alerting/alert"
"github.com/TwinProduction/gatus/v3/alerting/alert"
)
func TestParseWithDefaultAlert(t *testing.T) {

View File

@@ -4,9 +4,9 @@ import (
"fmt"
"net/http"
"github.com/TwinProduction/gatus/alerting/alert"
"github.com/TwinProduction/gatus/alerting/provider/custom"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/v3/alerting/alert"
"github.com/TwinProduction/gatus/v3/alerting/provider/custom"
"github.com/TwinProduction/gatus/v3/core"
)
// AlertProvider is the configuration necessary for sending an alert using Slack

View File

@@ -6,8 +6,8 @@ import (
"strings"
"testing"
"github.com/TwinProduction/gatus/alerting/alert"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/v3/alerting/alert"
"github.com/TwinProduction/gatus/v3/core"
)
func TestAlertProvider_IsValid(t *testing.T) {

View File

@@ -4,9 +4,9 @@ import (
"fmt"
"net/http"
"github.com/TwinProduction/gatus/alerting/alert"
"github.com/TwinProduction/gatus/alerting/provider/custom"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/v3/alerting/alert"
"github.com/TwinProduction/gatus/v3/alerting/provider/custom"
"github.com/TwinProduction/gatus/v3/core"
)
// AlertProvider is the configuration necessary for sending an alert using Teams

View File

@@ -6,8 +6,8 @@ import (
"strings"
"testing"
"github.com/TwinProduction/gatus/alerting/alert"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/v3/alerting/alert"
"github.com/TwinProduction/gatus/v3/core"
)
func TestAlertProvider_IsValid(t *testing.T) {

View File

@@ -4,9 +4,9 @@ import (
"fmt"
"net/http"
"github.com/TwinProduction/gatus/alerting/alert"
"github.com/TwinProduction/gatus/alerting/provider/custom"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/v3/alerting/alert"
"github.com/TwinProduction/gatus/v3/alerting/provider/custom"
"github.com/TwinProduction/gatus/v3/core"
)
// AlertProvider is the configuration necessary for sending an alert using Telegram

View File

@@ -7,8 +7,8 @@ import (
"strings"
"testing"
"github.com/TwinProduction/gatus/alerting/alert"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/v3/alerting/alert"
"github.com/TwinProduction/gatus/v3/core"
)
func TestAlertProvider_IsValid(t *testing.T) {

View File

@@ -6,9 +6,9 @@ import (
"net/http"
"net/url"
"github.com/TwinProduction/gatus/alerting/alert"
"github.com/TwinProduction/gatus/alerting/provider/custom"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/v3/alerting/alert"
"github.com/TwinProduction/gatus/v3/alerting/provider/custom"
"github.com/TwinProduction/gatus/v3/core"
)
// AlertProvider is the configuration necessary for sending an alert using Twilio

View File

@@ -5,8 +5,8 @@ import (
"strings"
"testing"
"github.com/TwinProduction/gatus/alerting/alert"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/v3/alerting/alert"
"github.com/TwinProduction/gatus/v3/core"
)
func TestTwilioAlertProvider_IsValid(t *testing.T) {

View File

@@ -38,7 +38,11 @@ func CanPerformStartTLS(address string, config *Config) (connected bool, certifi
if len(hostAndPort) != 2 {
return false, nil, errors.New("invalid address for starttls, format must be host:port")
}
smtpClient, err := smtp.Dial(address)
connection, err := net.DialTimeout("tcp", address, config.Timeout)
if err != nil {
return
}
smtpClient, err := smtp.NewClient(connection, hostAndPort[0])
if err != nil {
return
}
@@ -57,6 +61,20 @@ func CanPerformStartTLS(address string, config *Config) (connected bool, certifi
return true, certificate, nil
}
// CanPerformTLS checks whether a connection can be established to an address using the TLS protocol
func CanPerformTLS(address string, config *Config) (connected bool, certificate *x509.Certificate, err error) {
connection, err := tls.DialWithDialer(&net.Dialer{Timeout: config.Timeout}, "tcp", address, nil)
if err != nil {
return
}
defer connection.Close()
verifiedChains := connection.ConnectionState().VerifiedChains
if len(verifiedChains) == 0 || len(verifiedChains[0]) == 0 {
return
}
return true, verifiedChains[0][0], nil
}
// 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
@@ -67,9 +85,9 @@ func Ping(address string, config *Config) (bool, time.Duration) {
}
pinger.Count = 1
pinger.Timeout = config.Timeout
// Set the pinger's privileged mode to true for every operating system except darwin
// Set the pinger's privileged mode to true for windows
// https://github.com/TwinProduction/gatus/issues/132
pinger.SetPrivileged(runtime.GOOS != "darwin")
pinger.SetPrivileged(runtime.GOOS == "windows")
err = pinger.Run()
if err != nil {
return false, 0

View File

@@ -91,6 +91,56 @@ func TestCanPerformStartTLS(t *testing.T) {
}
}
func TestCanPerformTLS(t *testing.T) {
type args struct {
address string
insecure bool
}
tests := []struct {
name string
args args
wantConnected bool
wantErr bool
}{
{
name: "invalid address",
args: args{
address: "test",
},
wantConnected: false,
wantErr: true,
},
{
name: "error dial",
args: args{
address: "test:1234",
},
wantConnected: false,
wantErr: true,
},
{
name: "valid tls",
args: args{
address: "smtp.gmail.com:465",
},
wantConnected: true,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
connected, _, err := CanPerformTLS(tt.args.address, &Config{Insecure: tt.args.insecure, Timeout: 5 * time.Second})
if (err != nil) != tt.wantErr {
t.Errorf("CanPerformTLS() err=%v, wantErr=%v", err, tt.wantErr)
return
}
if connected != tt.wantConnected {
t.Errorf("CanPerformTLS() connected=%v, wantConnected=%v", connected, tt.wantConnected)
}
})
}
}
func TestCanCreateTCPConnection(t *testing.T) {
if CanCreateTCPConnection("127.0.0.1", &Config{Timeout: 5 * time.Second}) {
t.Error("should've failed, because there's no port in the address")

View File

@@ -1,7 +1,7 @@
services:
- name: front-end
group: core
url: "https://twinnation.org/health"
url: "https://twin.sh/health"
interval: 1m
conditions:
- "[STATUS] == 200"

View File

@@ -7,12 +7,15 @@ import (
"os"
"time"
"github.com/TwinProduction/gatus/alerting"
"github.com/TwinProduction/gatus/alerting/alert"
"github.com/TwinProduction/gatus/alerting/provider"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/security"
"github.com/TwinProduction/gatus/storage"
"github.com/TwinProduction/gatus/v3/alerting"
"github.com/TwinProduction/gatus/v3/alerting/alert"
"github.com/TwinProduction/gatus/v3/alerting/provider"
"github.com/TwinProduction/gatus/v3/config/maintenance"
"github.com/TwinProduction/gatus/v3/config/ui"
"github.com/TwinProduction/gatus/v3/config/web"
"github.com/TwinProduction/gatus/v3/core"
"github.com/TwinProduction/gatus/v3/security"
"github.com/TwinProduction/gatus/v3/storage"
"gopkg.in/yaml.v2"
)
@@ -24,12 +27,6 @@ const (
// DefaultFallbackConfigurationFilePath is the default fallback path that will be used to search for the
// configuration file if DefaultConfigurationFilePath didn't work
DefaultFallbackConfigurationFilePath = "config/config.yml"
// DefaultAddress is the default address the service will bind to
DefaultAddress = "0.0.0.0"
// DefaultPort is the default port the service will listen on
DefaultPort = 8080
)
var (
@@ -41,10 +38,6 @@ var (
// ErrInvalidSecurityConfig is an error returned when the security configuration is invalid
ErrInvalidSecurityConfig = errors.New("invalid security configuration")
// StaticFolder is the path to the location of the static folder from the root path of the project
// The only reason this is exposed is to allow running tests from a different path than the root path of the project
StaticFolder = "./web/static"
)
// Config is the main configuration structure
@@ -77,10 +70,13 @@ type Config struct {
Storage *storage.Config `yaml:"storage"`
// Web is the configuration for the web listener
Web *WebConfig `yaml:"web"`
Web *web.Config `yaml:"web"`
// UI is the configuration for the UI
UI *UIConfig `yaml:"ui"`
UI *ui.Config `yaml:"ui"`
// Maintenance is the configuration for creating a maintenance window in which no alerts are sent
Maintenance *maintenance.Config `yaml:"maintenance"`
filePath string // path to the file from which config was loaded from
lastFileModTime time.Time // last modification time
@@ -172,6 +168,9 @@ func parseAndValidateConfigBytes(yamlBytes []byte) (config *Config, err error) {
if err := validateUIConfig(config); err != nil {
return nil, err
}
if err := validateMaintenanceConfig(config); err != nil {
return nil, err
}
if err := validateStorageConfig(config); err != nil {
return nil, err
}
@@ -201,11 +200,22 @@ func validateStorageConfig(config *Config) error {
return nil
}
func validateMaintenanceConfig(config *Config) error {
if config.Maintenance == nil {
config.Maintenance = maintenance.GetDefaultConfig()
} else {
if err := config.Maintenance.ValidateAndSetDefaults(); err != nil {
return err
}
}
return nil
}
func validateUIConfig(config *Config) error {
if config.UI == nil {
config.UI = GetDefaultUIConfig()
config.UI = ui.GetDefaultConfig()
} else {
if err := config.UI.validateAndSetDefaults(); err != nil {
if err := config.UI.ValidateAndSetDefaults(); err != nil {
return err
}
}
@@ -214,9 +224,9 @@ func validateUIConfig(config *Config) error {
func validateWebConfig(config *Config) error {
if config.Web == nil {
config.Web = GetDefaultWebConfig()
config.Web = web.GetDefaultConfig()
} else {
return config.Web.validateAndSetDefaults()
return config.Web.ValidateAndSetDefaults()
}
return nil
}

View File

@@ -5,19 +5,21 @@ import (
"testing"
"time"
"github.com/TwinProduction/gatus/alerting"
"github.com/TwinProduction/gatus/alerting/alert"
"github.com/TwinProduction/gatus/alerting/provider/custom"
"github.com/TwinProduction/gatus/alerting/provider/discord"
"github.com/TwinProduction/gatus/alerting/provider/mattermost"
"github.com/TwinProduction/gatus/alerting/provider/messagebird"
"github.com/TwinProduction/gatus/alerting/provider/pagerduty"
"github.com/TwinProduction/gatus/alerting/provider/slack"
"github.com/TwinProduction/gatus/alerting/provider/teams"
"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/v3/alerting"
"github.com/TwinProduction/gatus/v3/alerting/alert"
"github.com/TwinProduction/gatus/v3/alerting/provider/custom"
"github.com/TwinProduction/gatus/v3/alerting/provider/discord"
"github.com/TwinProduction/gatus/v3/alerting/provider/mattermost"
"github.com/TwinProduction/gatus/v3/alerting/provider/messagebird"
"github.com/TwinProduction/gatus/v3/alerting/provider/pagerduty"
"github.com/TwinProduction/gatus/v3/alerting/provider/slack"
"github.com/TwinProduction/gatus/v3/alerting/provider/teams"
"github.com/TwinProduction/gatus/v3/alerting/provider/telegram"
"github.com/TwinProduction/gatus/v3/alerting/provider/twilio"
"github.com/TwinProduction/gatus/v3/client"
"github.com/TwinProduction/gatus/v3/config/ui"
"github.com/TwinProduction/gatus/v3/config/web"
"github.com/TwinProduction/gatus/v3/core"
)
func TestLoadFileThatDoesNotExist(t *testing.T) {
@@ -36,18 +38,23 @@ func TestLoadDefaultConfigurationFile(t *testing.T) {
func TestParseAndValidateConfigBytes(t *testing.T) {
file := t.TempDir() + "/test.db"
StaticFolder = "../web/static"
ui.StaticFolder = "../web/static"
defer func() {
StaticFolder = "./web/static"
ui.StaticFolder = "./web/static"
}()
config, err := parseAndValidateConfigBytes([]byte(fmt.Sprintf(`
storage:
file: %s
maintenance:
enabled: true
start: 00:00
duration: 4h
every: [Monday, Thursday]
ui:
title: Test
services:
- name: twinnation
url: https://twinnation.org/health
- name: website
url: https://twin.sh/health
interval: 15s
conditions:
- "[STATUS] == 200"
@@ -79,12 +86,15 @@ services:
if config.UI == nil || config.UI.Title != "Test" {
t.Error("Expected Config.UI.Title to be Test")
}
if mc := config.Maintenance; mc == nil || mc.Start != "00:00" || !mc.IsEnabled() || mc.Duration != 4*time.Hour || len(mc.Every) != 2 {
t.Error("Expected Config.Maintenance to be configured properly")
}
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[0].URL != "https://twin.sh/health" {
t.Errorf("URL should have been %s", "https://twin.sh/health")
}
if config.Services[0].Method != "GET" {
t.Errorf("Method should have been %s (default)", "GET")
@@ -153,8 +163,8 @@ services:
func TestParseAndValidateConfigBytesDefault(t *testing.T) {
config, err := parseAndValidateConfigBytes([]byte(`
services:
- name: twinnation
url: https://twinnation.org/health
- name: website
url: https://twin.sh/health
conditions:
- "[STATUS] == 200"
`))
@@ -167,14 +177,14 @@ 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.Address != web.DefaultAddress {
t.Errorf("Bind address should have been %s, because it is the default value", web.DefaultAddress)
}
if config.Web.Port != DefaultPort {
t.Errorf("Port should have been %d, because it is the default value", DefaultPort)
if config.Web.Port != web.DefaultPort {
t.Errorf("Port should have been %d, because it is the default value", web.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].URL != "https://twin.sh/health" {
t.Errorf("URL should have been %s", "https://twin.sh/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)
@@ -195,8 +205,8 @@ func TestParseAndValidateConfigBytesWithAddress(t *testing.T) {
web:
address: 127.0.0.1
services:
- name: twinnation
url: https://twinnation.org/actuator/health
- name: website
url: https://twin.sh/actuator/health
conditions:
- "[STATUS] == 200"
`))
@@ -209,8 +219,8 @@ services:
if config.Metrics {
t.Error("Metrics should've been false by default")
}
if config.Services[0].URL != "https://twinnation.org/actuator/health" {
t.Errorf("URL should have been %s", "https://twinnation.org/actuator/health")
if config.Services[0].URL != "https://twin.sh/actuator/health" {
t.Errorf("URL should have been %s", "https://twin.sh/actuator/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)
@@ -218,8 +228,8 @@ services:
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)
if config.Web.Port != web.DefaultPort {
t.Errorf("Port should have been %d, because it is the default value", web.DefaultPort)
}
}
@@ -228,8 +238,8 @@ func TestParseAndValidateConfigBytesWithPort(t *testing.T) {
web:
port: 12345
services:
- name: twinnation
url: https://twinnation.org/health
- name: website
url: https://twin.sh/health
conditions:
- "[STATUS] == 200"
`))
@@ -242,14 +252,14 @@ services:
if config.Metrics {
t.Error("Metrics should've been false by default")
}
if config.Services[0].URL != "https://twinnation.org/health" {
t.Errorf("URL should have been %s", "https://twinnation.org/health")
if config.Services[0].URL != "https://twin.sh/health" {
t.Errorf("URL should have been %s", "https://twin.sh/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.Web.Address != web.DefaultAddress {
t.Errorf("Bind address should have been %s, because it is the default value", web.DefaultAddress)
}
if config.Web.Port != 12345 {
t.Errorf("Port should have been %d, because it is specified in config", 12345)
@@ -262,8 +272,8 @@ web:
port: 12345
address: 127.0.0.1
services:
- name: twinnation
url: https://twinnation.org/health
- name: website
url: https://twin.sh/health
conditions:
- "[STATUS] == 200"
`))
@@ -276,8 +286,8 @@ services:
if config.Metrics {
t.Error("Metrics should've been false by default")
}
if config.Services[0].URL != "https://twinnation.org/health" {
t.Errorf("URL should have been %s", "https://twinnation.org/health")
if config.Services[0].URL != "https://twin.sh/health" {
t.Errorf("URL should have been %s", "https://twin.sh/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)
@@ -296,8 +306,8 @@ web:
port: 65536
address: 127.0.0.1
services:
- name: twinnation
url: https://twinnation.org/health
- name: website
url: https://twin.sh/health
conditions:
- "[STATUS] == 200"
`))
@@ -310,8 +320,8 @@ func TestParseAndValidateConfigBytesWithMetricsAndCustomUserAgentHeader(t *testi
config, err := parseAndValidateConfigBytes([]byte(`
metrics: true
services:
- name: twinnation
url: https://twinnation.org/health
- name: website
url: https://twin.sh/health
headers:
User-Agent: Test/2.0
conditions:
@@ -326,17 +336,17 @@ services:
if !config.Metrics {
t.Error("Metrics should have been true")
}
if config.Services[0].URL != "https://twinnation.org/health" {
t.Errorf("URL should have been %s", "https://twinnation.org/health")
if config.Services[0].URL != "https://twin.sh/health" {
t.Errorf("URL should have been %s", "https://twin.sh/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.Web.Address != web.DefaultAddress {
t.Errorf("Bind address should have been %s, because it is the default value", web.DefaultAddress)
}
if config.Web.Port != DefaultPort {
t.Errorf("Port should have been %d, because it is the default value", DefaultPort)
if config.Web.Port != web.DefaultPort {
t.Errorf("Port should have been %d, because it is the default value", web.DefaultPort)
}
if userAgent := config.Services[0].Headers["User-Agent"]; userAgent != "Test/2.0" {
t.Errorf("User-Agent should've been %s, got %s", "Test/2.0", userAgent)
@@ -350,8 +360,8 @@ web:
address: 192.168.0.1
port: 9090
services:
- name: twinnation
url: https://twinnation.org/health
- name: website
url: https://twin.sh/health
conditions:
- "[STATUS] == 200"
`))
@@ -370,8 +380,8 @@ services:
if config.Web.Port != 9090 {
t.Errorf("Port should have been %d, because it is specified in config", 9090)
}
if config.Services[0].URL != "https://twinnation.org/health" {
t.Errorf("URL should have been %s", "https://twinnation.org/health")
if config.Services[0].URL != "https://twin.sh/health" {
t.Errorf("URL should have been %s", "https://twin.sh/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)
@@ -427,8 +437,8 @@ alerting:
webhook-url: "http://example.com"
services:
- name: twinnation
url: https://twinnation.org/health
- name: website
url: https://twin.sh/health
alerts:
- type: slack
enabled: true
@@ -471,8 +481,8 @@ services:
if len(config.Services) != 1 {
t.Error("There should've been 1 service")
}
if config.Services[0].URL != "https://twinnation.org/health" {
t.Errorf("URL should have been %s", "https://twinnation.org/health")
if config.Services[0].URL != "https://twin.sh/health" {
t.Errorf("URL should have been %s", "https://twin.sh/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)
@@ -633,8 +643,8 @@ alerting:
enabled: true
services:
- name: twinnation
url: https://twinnation.org/health
- name: website
url: https://twin.sh/health
alerts:
- type: slack
- type: pagerduty
@@ -748,8 +758,8 @@ services:
if len(config.Services) != 1 {
t.Error("There should've been 1 service")
}
if config.Services[0].URL != "https://twinnation.org/health" {
t.Errorf("URL should have been %s", "https://twinnation.org/health")
if config.Services[0].URL != "https://twin.sh/health" {
t.Errorf("URL should have been %s", "https://twin.sh/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)
@@ -871,8 +881,8 @@ alerting:
description: "description"
services:
- name: twinnation
url: https://twinnation.org/health
- name: website
url: https://twin.sh/health
alerts:
- type: slack
failure-threshold: 10
@@ -946,8 +956,8 @@ alerting:
pagerduty:
integration-key: "INVALID_KEY"
services:
- name: twinnation
url: https://twinnation.org/health
- name: website
url: https://twin.sh/health
alerts:
- type: pagerduty
conditions:
@@ -980,8 +990,8 @@ alerting:
"text": "[ALERT_TRIGGERED_OR_RESOLVED]: [SERVICE_NAME] - [ALERT_DESCRIPTION]"
}
services:
- name: twinnation
url: https://twinnation.org/health
- name: website
url: https://twin.sh/health
alerts:
- type: custom
conditions:
@@ -1025,8 +1035,8 @@ alerting:
insecure: true
body: "[ALERT_TRIGGERED_OR_RESOLVED]: [SERVICE_NAME] - [ALERT_DESCRIPTION]"
services:
- name: twinnation
url: https://twinnation.org/health
- name: website
url: https://twin.sh/health
alerts:
- type: custom
conditions:
@@ -1065,8 +1075,8 @@ alerting:
url: "https://example.com"
body: "[ALERT_TRIGGERED_OR_RESOLVED]: [SERVICE_NAME] - [ALERT_DESCRIPTION]"
services:
- name: twinnation
url: https://twinnation.org/health
- name: website
url: https://twin.sh/health
alerts:
- type: custom
conditions:
@@ -1099,7 +1109,7 @@ func TestParseAndValidateConfigBytesWithInvalidServiceName(t *testing.T) {
_, err := parseAndValidateConfigBytes([]byte(`
services:
- name: ""
url: https://twinnation.org/health
url: https://twin.sh/health
conditions:
- "[STATUS] == 200"
`))
@@ -1145,8 +1155,8 @@ security:
username: "admin"
password-sha512: "invalid-sha512-hash"
services:
- name: twinnation
url: https://twinnation.org/health
- name: website
url: https://twin.sh/health
conditions:
- "[STATUS] == 200"
`))
@@ -1164,8 +1174,8 @@ security:
username: "%s"
password-sha512: "%s"
services:
- name: twinnation
url: https://twinnation.org/health
- name: website
url: https://twin.sh/health
conditions:
- "[STATUS] == 200"
`, expectedUsername, expectedPasswordHash)))

View File

@@ -0,0 +1,146 @@
package maintenance
import (
"errors"
"fmt"
"strconv"
"strings"
"time"
)
var (
errInvalidMaintenanceStartFormat = errors.New("invalid maintenance start format: must be hh:mm, between 00:00 and 23:59 inclusively (e.g. 23:00)")
errInvalidMaintenanceDuration = errors.New("invalid maintenance duration: must be bigger than 0 (e.g. 30m)")
errInvalidDayName = fmt.Errorf("invalid value specified for 'on'. supported values are %s", longDayNames)
longDayNames = []string{
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
}
)
// Config allows for the configuration of a maintenance period.
// During this maintenance period, no alerts will be sent.
//
// Uses UTC.
type Config struct {
Enabled *bool `yaml:"enabled"` // Whether the maintenance period is enabled. Enabled by default if nil.
Start string `yaml:"start"` // Time at which the maintenance period starts (e.g. 23:00)
Duration time.Duration `yaml:"duration"` // Duration of the maintenance period (e.g. 4h)
// Every is a list of days of the week during which maintenance period applies.
// See longDayNames for list of valid values.
// Every day if empty.
Every []string `yaml:"every"`
durationToStartFromMidnight time.Duration
timeLocation *time.Location
}
func GetDefaultConfig() *Config {
defaultValue := false
return &Config{
Enabled: &defaultValue,
}
}
// IsEnabled returns whether maintenance is enabled or not
func (c Config) IsEnabled() bool {
if c.Enabled == nil {
return true
}
return *c.Enabled
}
// ValidateAndSetDefaults validates the maintenance configuration and sets the default values if necessary.
//
// Must be called once in the application's lifecycle before IsUnderMaintenance is called, since it
// also sets durationToStartFromMidnight.
func (c *Config) ValidateAndSetDefaults() error {
if c == nil || !c.IsEnabled() {
// Don't waste time validating if maintenance is not enabled.
return nil
}
for _, day := range c.Every {
isDayValid := false
for _, longDayName := range longDayNames {
if day == longDayName {
isDayValid = true
break
}
}
if !isDayValid {
return errInvalidDayName
}
}
var err error
c.durationToStartFromMidnight, err = hhmmToDuration(c.Start)
if err != nil {
return err
}
if c.Duration <= 0 || c.Duration >= 24*time.Hour {
return errInvalidMaintenanceDuration
}
return nil
}
// IsUnderMaintenance checks whether the services that Gatus monitors are within the configured maintenance window
func (c Config) IsUnderMaintenance() bool {
if !c.IsEnabled() {
return false
}
now := time.Now().UTC()
var dayWhereMaintenancePeriodWouldStart time.Time
if now.Hour() >= int(c.durationToStartFromMidnight.Hours()) {
dayWhereMaintenancePeriodWouldStart = now.Truncate(24 * time.Hour)
} else {
dayWhereMaintenancePeriodWouldStart = now.Add(-c.Duration).Truncate(24 * time.Hour)
}
hasMaintenanceEveryDay := len(c.Every) == 0
hasMaintenancePeriodScheduledToStartOnThatWeekday := c.hasDay(dayWhereMaintenancePeriodWouldStart.Weekday().String())
if !hasMaintenanceEveryDay && !hasMaintenancePeriodScheduledToStartOnThatWeekday {
// The day when the maintenance period would start is not scheduled
// to have any maintenance, so we can just return false.
return false
}
startOfMaintenancePeriod := dayWhereMaintenancePeriodWouldStart.Add(c.durationToStartFromMidnight)
endOfMaintenancePeriod := startOfMaintenancePeriod.Add(c.Duration)
return now.After(startOfMaintenancePeriod) && now.Before(endOfMaintenancePeriod)
}
func (c Config) hasDay(day string) bool {
for _, d := range c.Every {
if d == day {
return true
}
}
return false
}
func hhmmToDuration(s string) (time.Duration, error) {
if len(s) != 5 {
return 0, errInvalidMaintenanceStartFormat
}
var hours, minutes int
var err error
if hours, err = extractNumericalValueFromPotentiallyZeroPaddedString(s[:2]); err != nil {
return 0, err
}
if minutes, err = extractNumericalValueFromPotentiallyZeroPaddedString(s[3:5]); err != nil {
return 0, err
}
duration := (time.Duration(hours) * time.Hour) + (time.Duration(minutes) * time.Minute)
if hours < 0 || hours > 23 || minutes < 0 || minutes > 59 || duration < 0 || duration >= 24*time.Hour {
return 0, errInvalidMaintenanceStartFormat
}
return duration, nil
}
func extractNumericalValueFromPotentiallyZeroPaddedString(s string) (int, error) {
return strconv.Atoi(strings.TrimPrefix(s, "0"))
}

View File

@@ -0,0 +1,226 @@
package maintenance
import (
"errors"
"fmt"
"strconv"
"testing"
"time"
)
func TestGetDefaultConfig(t *testing.T) {
if *GetDefaultConfig().Enabled {
t.Fatal("expected default config to be disabled by default")
}
}
func TestConfig_ValidateAndSetDefaults(t *testing.T) {
yes, no := true, false
scenarios := []struct {
name string
cfg *Config
expectedError error
}{
{
name: "nil",
cfg: nil,
expectedError: nil,
},
{
name: "disabled",
cfg: &Config{
Enabled: &no,
},
expectedError: nil,
},
{
name: "invalid-day",
cfg: &Config{
Every: []string{"invalid-day"},
},
expectedError: errInvalidDayName,
},
{
name: "invalid-day",
cfg: &Config{
Every: []string{"invalid-day"},
},
expectedError: errInvalidDayName,
},
{
name: "invalid-start-format",
cfg: &Config{
Start: "0000",
},
expectedError: errInvalidMaintenanceStartFormat,
},
{
name: "invalid-start-hours",
cfg: &Config{
Start: "25:00",
},
expectedError: errInvalidMaintenanceStartFormat,
},
{
name: "invalid-start-minutes",
cfg: &Config{
Start: "0:61",
},
expectedError: errInvalidMaintenanceStartFormat,
},
{
name: "invalid-start-minutes-non-numerical",
cfg: &Config{
Start: "00:zz",
},
expectedError: strconv.ErrSyntax,
},
{
name: "invalid-start-hours-non-numerical",
cfg: &Config{
Start: "zz:00",
},
expectedError: strconv.ErrSyntax,
},
{
name: "invalid-duration",
cfg: &Config{
Start: "23:00",
Duration: 0,
},
expectedError: errInvalidMaintenanceDuration,
},
{
name: "every-day-at-2300",
cfg: &Config{
Start: "23:00",
Duration: time.Hour,
},
expectedError: nil,
},
{
name: "every-monday-at-0000",
cfg: &Config{
Start: "00:00",
Duration: 30 * time.Minute,
Every: []string{"Monday"},
},
expectedError: nil,
},
{
name: "every-friday-and-sunday-at-0000-explicitly-enabled",
cfg: &Config{
Enabled: &yes,
Start: "08:00",
Duration: 8 * time.Hour,
Every: []string{"Friday", "Sunday"},
},
expectedError: nil,
},
}
for _, scenario := range scenarios {
t.Run(scenario.name, func(t *testing.T) {
err := scenario.cfg.ValidateAndSetDefaults()
if !errors.Is(err, scenario.expectedError) {
t.Errorf("expected %v, got %v", scenario.expectedError, err)
}
})
}
}
func TestConfig_IsUnderMaintenance(t *testing.T) {
yes, no := true, false
now := time.Now().UTC()
scenarios := []struct {
name string
cfg *Config
expected bool
}{
{
name: "disabled",
cfg: &Config{
Enabled: &no,
},
expected: false,
},
{
name: "under-maintenance-explicitly-enabled",
cfg: &Config{
Enabled: &yes,
Start: fmt.Sprintf("%02d:00", now.Hour()),
Duration: 2 * time.Hour,
},
expected: true,
},
{
name: "under-maintenance-starting-now-for-2h",
cfg: &Config{
Start: fmt.Sprintf("%02d:00", now.Hour()),
Duration: 2 * time.Hour,
},
expected: true,
},
{
name: "under-maintenance-starting-now-for-8h",
cfg: &Config{
Start: fmt.Sprintf("%02d:00", now.Hour()),
Duration: 8 * time.Hour,
},
expected: true,
},
{
name: "under-maintenance-starting-4h-ago-for-8h",
cfg: &Config{
Start: fmt.Sprintf("%02d:00", normalizeHour(now.Hour()-4)),
Duration: 8 * time.Hour,
},
expected: true,
},
{
name: "under-maintenance-starting-4h-ago-for-3h",
cfg: &Config{
Start: fmt.Sprintf("%02d:00", normalizeHour(now.Hour()-4)),
Duration: 3 * time.Hour,
},
expected: false,
},
{
name: "under-maintenance-starting-5h-ago-for-1h",
cfg: &Config{
Start: fmt.Sprintf("%02d:00", normalizeHour(now.Hour()-5)),
Duration: time.Hour,
},
expected: false,
},
{
name: "not-under-maintenance-today",
cfg: &Config{
Start: fmt.Sprintf("%02d:00", now.Hour()),
Duration: time.Hour,
Every: []string{now.Add(48 * time.Hour).Weekday().String()},
},
expected: false,
},
}
for _, scenario := range scenarios {
t.Run(scenario.name, func(t *testing.T) {
t.Log(scenario.cfg.Start)
t.Log(now)
if err := scenario.cfg.ValidateAndSetDefaults(); err != nil {
t.Fatal("validation shouldn't have returned an error, got", err)
}
isUnderMaintenance := scenario.cfg.IsUnderMaintenance()
if isUnderMaintenance != scenario.expected {
t.Errorf("expected %v, got %v", scenario.expected, isUnderMaintenance)
t.Logf("start=%v; duration=%v; now=%v", scenario.cfg.Start, scenario.cfg.Duration, time.Now().UTC())
}
})
}
}
func normalizeHour(hour int) int {
if hour < 0 {
return hour + 24
}
return hour
}

View File

@@ -1,41 +0,0 @@
package config
import (
"bytes"
"html/template"
)
const (
defaultTitle = "Health Dashboard | Gatus"
defaultLogo = ""
)
// UIConfig is the configuration for the UI of Gatus
type UIConfig struct {
Title string `yaml:"title"` // Title of the page
Logo string `yaml:"logo"` // Logo to display on the page
}
// GetDefaultUIConfig returns a UIConfig struct with the default values
func GetDefaultUIConfig() *UIConfig {
return &UIConfig{
Title: defaultTitle,
Logo: defaultLogo,
}
}
func (cfg *UIConfig) validateAndSetDefaults() error {
if len(cfg.Title) == 0 {
cfg.Title = defaultTitle
}
t, err := template.ParseFiles(StaticFolder + "/index.html")
if err != nil {
return err
}
var buffer bytes.Buffer
err = t.Execute(&buffer, cfg)
if err != nil {
return err
}
return nil
}

48
config/ui/ui.go Normal file
View File

@@ -0,0 +1,48 @@
package ui
import (
"bytes"
"html/template"
)
const (
defaultTitle = "Health Dashboard | Gatus"
defaultLogo = ""
)
var (
// StaticFolder is the path to the location of the static folder from the root path of the project
// The only reason this is exposed is to allow running tests from a different path than the root path of the project
StaticFolder = "./web/static"
)
// Config is the configuration for the UI of Gatus
type Config struct {
Title string `yaml:"title"` // Title of the page
Logo string `yaml:"logo"` // Logo to display on the page
}
// GetDefaultConfig returns a Config struct with the default values
func GetDefaultConfig() *Config {
return &Config{
Title: defaultTitle,
Logo: defaultLogo,
}
}
// ValidateAndSetDefaults validates the UI configuration and sets the default values if necessary.
func (cfg *Config) ValidateAndSetDefaults() error {
if len(cfg.Title) == 0 {
cfg.Title = defaultTitle
}
t, err := template.ParseFiles(StaticFolder + "/index.html")
if err != nil {
return err
}
var buffer bytes.Buffer
err = t.Execute(&buffer, cfg)
if err != nil {
return err
}
return nil
}

26
config/ui/ui_test.go Normal file
View File

@@ -0,0 +1,26 @@
package ui
import (
"testing"
)
func TestConfig_ValidateAndSetDefaults(t *testing.T) {
StaticFolder = "../../web/static"
defer func() {
StaticFolder = "./web/static"
}()
cfg := &Config{Title: ""}
if err := cfg.ValidateAndSetDefaults(); err != nil {
t.Error("expected no error, got", err.Error())
}
}
func TestGetDefaultConfig(t *testing.T) {
defaultConfig := GetDefaultConfig()
if defaultConfig.Title != defaultTitle {
t.Error("expected GetDefaultConfig() to return defaultTitle, got", defaultConfig.Title)
}
if defaultConfig.Logo != defaultLogo {
t.Error("expected GetDefaultConfig() to return defaultLogo, got", defaultConfig.Logo)
}
}

View File

@@ -1,24 +0,0 @@
package config
import "testing"
func TestUIConfig_validateAndSetDefaults(t *testing.T) {
StaticFolder = "../web/static"
defer func() {
StaticFolder = "./web/static"
}()
uiConfig := &UIConfig{Title: ""}
if err := uiConfig.validateAndSetDefaults(); err != nil {
t.Error("expected no error, got", err.Error())
}
}
func TestGetDefaultUIConfig(t *testing.T) {
defaultUIConfig := GetDefaultUIConfig()
if defaultUIConfig.Title != defaultTitle {
t.Error("expected GetDefaultUIConfig() to return defaultTitle, got", defaultUIConfig.Title)
}
if defaultUIConfig.Logo != defaultLogo {
t.Error("expected GetDefaultUIConfig() to return defaultLogo, got", defaultUIConfig.Logo)
}
}

View File

@@ -1,13 +1,21 @@
package config
package web
import (
"fmt"
"math"
)
// WebConfig is the structure which supports the configuration of the endpoint
const (
// DefaultAddress is the default address the service will bind to
DefaultAddress = "0.0.0.0"
// DefaultPort is the default port the service will listen on
DefaultPort = 8080
)
// Config is the structure which supports the configuration of the endpoint
// which provides access to the web frontend
type WebConfig struct {
type Config struct {
// Address to listen on (defaults to 0.0.0.0 specified by DefaultAddress)
Address string `yaml:"address"`
@@ -15,13 +23,13 @@ type WebConfig struct {
Port int `yaml:"port"`
}
// GetDefaultWebConfig returns a WebConfig struct with the default values
func GetDefaultWebConfig() *WebConfig {
return &WebConfig{Address: DefaultAddress, Port: DefaultPort}
// GetDefaultConfig returns a Config struct with the default values
func GetDefaultConfig() *Config {
return &Config{Address: DefaultAddress, Port: DefaultPort}
}
// validateAndSetDefaults checks and sets the default values for fields that are not set
func (web *WebConfig) validateAndSetDefaults() error {
// ValidateAndSetDefaults validates the web configuration and sets the default values if necessary.
func (web *Config) ValidateAndSetDefaults() error {
// Validate the Address
if len(web.Address) == 0 {
web.Address = DefaultAddress
@@ -36,6 +44,6 @@ func (web *WebConfig) validateAndSetDefaults() error {
}
// SocketAddress returns the combination of the Address and the Port
func (web *WebConfig) SocketAddress() string {
func (web *Config) SocketAddress() string {
return fmt.Sprintf("%s:%d", web.Address, web.Port)
}

65
config/web/web_test.go Normal file
View File

@@ -0,0 +1,65 @@
package web
import (
"testing"
)
func TestGetDefaultConfig(t *testing.T) {
defaultConfig := GetDefaultConfig()
if defaultConfig.Port != DefaultPort {
t.Error("expected default config to have the default port")
}
if defaultConfig.Address != DefaultAddress {
t.Error("expected default config to have the default address")
}
}
func TestConfig_ValidateAndSetDefaults(t *testing.T) {
scenarios := []struct {
name string
cfg *Config
expectedAddress string
expectedPort int
expectedErr bool
}{
{
name: "no-explicit-config",
cfg: &Config{},
expectedAddress: "0.0.0.0",
expectedPort: 8080,
expectedErr: false,
},
{
name: "invalid-port",
cfg: &Config{Port: 100000000},
expectedErr: true,
},
}
for _, scenario := range scenarios {
t.Run(scenario.name, func(t *testing.T) {
err := scenario.cfg.ValidateAndSetDefaults()
if (err != nil) != scenario.expectedErr {
t.Errorf("expected the existence of an error to be %v, got %v", scenario.expectedErr, err)
return
}
if !scenario.expectedErr {
if scenario.cfg.Port != scenario.expectedPort {
t.Errorf("expected port to be %d, got %d", scenario.expectedPort, scenario.cfg.Port)
}
if scenario.cfg.Address != scenario.expectedAddress {
t.Errorf("expected address to be %s, got %s", scenario.expectedAddress, scenario.cfg.Address)
}
}
})
}
}
func TestConfig_SocketAddress(t *testing.T) {
web := &Config{
Address: "0.0.0.0",
Port: 8081,
}
if web.SocketAddress() != "0.0.0.0:8081" {
t.Errorf("expected %s, got %s", "0.0.0.0:8081", web.SocketAddress())
}
}

View File

@@ -1,15 +0,0 @@
package config
import (
"testing"
)
func TestWebConfig_SocketAddress(t *testing.T) {
web := &WebConfig{
Address: "0.0.0.0",
Port: 8081,
}
if web.SocketAddress() != "0.0.0.0:8081" {
t.Errorf("expected %s, got %s", "0.0.0.0:8081", web.SocketAddress())
}
}

View File

@@ -8,9 +8,10 @@ import (
"os"
"time"
"github.com/TwinProduction/gatus/config"
"github.com/TwinProduction/gatus/controller/handler"
"github.com/TwinProduction/gatus/security"
"github.com/TwinProduction/gatus/v3/config/ui"
"github.com/TwinProduction/gatus/v3/config/web"
"github.com/TwinProduction/gatus/v3/controller/handler"
"github.com/TwinProduction/gatus/v3/security"
)
var (
@@ -20,8 +21,8 @@ var (
)
// Handle creates the router and starts the server
func Handle(securityConfig *security.Config, webConfig *config.WebConfig, uiConfig *config.UIConfig, enableMetrics bool) {
var router http.Handler = handler.CreateRouter(config.StaticFolder, securityConfig, uiConfig, enableMetrics)
func Handle(securityConfig *security.Config, webConfig *web.Config, uiConfig *ui.Config, enableMetrics bool) {
var router http.Handler = handler.CreateRouter(ui.StaticFolder, securityConfig, uiConfig, enableMetrics)
if os.Getenv("ENVIRONMENT") == "dev" {
router = handler.DevelopmentCORS(router)
}

View File

@@ -7,13 +7,14 @@ import (
"os"
"testing"
"github.com/TwinProduction/gatus/config"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/v3/config"
"github.com/TwinProduction/gatus/v3/config/web"
"github.com/TwinProduction/gatus/v3/core"
)
func TestHandle(t *testing.T) {
cfg := &config.Config{
Web: &config.WebConfig{
Web: &web.Config{
Address: "0.0.0.0",
Port: rand.Intn(65534),
},

View File

@@ -7,8 +7,8 @@ import (
"strings"
"time"
"github.com/TwinProduction/gatus/storage"
"github.com/TwinProduction/gatus/storage/store/common"
"github.com/TwinProduction/gatus/v3/storage"
"github.com/TwinProduction/gatus/v3/storage/store/common"
"github.com/gorilla/mux"
)

View File

@@ -7,10 +7,10 @@ import (
"testing"
"time"
"github.com/TwinProduction/gatus/config"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/storage"
"github.com/TwinProduction/gatus/watchdog"
"github.com/TwinProduction/gatus/v3/config"
"github.com/TwinProduction/gatus/v3/core"
"github.com/TwinProduction/gatus/v3/storage"
"github.com/TwinProduction/gatus/v3/watchdog"
)
func TestUptimeBadge(t *testing.T) {

View File

@@ -7,8 +7,8 @@ import (
"sort"
"time"
"github.com/TwinProduction/gatus/storage"
"github.com/TwinProduction/gatus/storage/store/common"
"github.com/TwinProduction/gatus/v3/storage"
"github.com/TwinProduction/gatus/v3/storage/store/common"
"github.com/gorilla/mux"
"github.com/wcharczuk/go-chart/v2"
"github.com/wcharczuk/go-chart/v2/drawing"

View File

@@ -6,10 +6,10 @@ import (
"testing"
"time"
"github.com/TwinProduction/gatus/config"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/storage"
"github.com/TwinProduction/gatus/watchdog"
"github.com/TwinProduction/gatus/v3/config"
"github.com/TwinProduction/gatus/v3/core"
"github.com/TwinProduction/gatus/v3/storage"
"github.com/TwinProduction/gatus/v3/watchdog"
)
func TestResponseTimeChart(t *testing.T) {

View File

@@ -3,14 +3,14 @@ package handler
import (
"net/http"
"github.com/TwinProduction/gatus/config"
"github.com/TwinProduction/gatus/security"
"github.com/TwinProduction/gatus/v3/config/ui"
"github.com/TwinProduction/gatus/v3/security"
"github.com/TwinProduction/health"
"github.com/gorilla/mux"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
func CreateRouter(staticFolder string, securityConfig *security.Config, uiConfig *config.UIConfig, enabledMetrics bool) *mux.Router {
func CreateRouter(staticFolder string, securityConfig *security.Config, uiConfig *ui.Config, enabledMetrics bool) *mux.Router {
router := mux.NewRouter()
if enabledMetrics {
router.Handle("/metrics", promhttp.Handler()).Methods("GET")

View File

@@ -10,9 +10,9 @@ import (
"strings"
"time"
"github.com/TwinProduction/gatus/storage"
"github.com/TwinProduction/gatus/storage/store/common"
"github.com/TwinProduction/gatus/storage/store/common/paging"
"github.com/TwinProduction/gatus/v3/storage"
"github.com/TwinProduction/gatus/v3/storage/store/common"
"github.com/TwinProduction/gatus/v3/storage/store/common/paging"
"github.com/TwinProduction/gocache"
"github.com/gorilla/mux"
)

View File

@@ -6,10 +6,10 @@ import (
"testing"
"time"
"github.com/TwinProduction/gatus/config"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/storage"
"github.com/TwinProduction/gatus/watchdog"
"github.com/TwinProduction/gatus/v3/config"
"github.com/TwinProduction/gatus/v3/core"
"github.com/TwinProduction/gatus/v3/storage"
"github.com/TwinProduction/gatus/v3/watchdog"
)
var (

View File

@@ -5,10 +5,10 @@ import (
"log"
"net/http"
"github.com/TwinProduction/gatus/config"
"github.com/TwinProduction/gatus/v3/config/ui"
)
func SinglePageApplication(staticFolder string, ui *config.UIConfig) http.HandlerFunc {
func SinglePageApplication(staticFolder string, ui *ui.Config) http.HandlerFunc {
return func(writer http.ResponseWriter, request *http.Request) {
t, err := template.ParseFiles(staticFolder + "/index.html")
if err != nil {

View File

@@ -6,10 +6,10 @@ import (
"testing"
"time"
"github.com/TwinProduction/gatus/config"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/storage"
"github.com/TwinProduction/gatus/watchdog"
"github.com/TwinProduction/gatus/v3/config"
"github.com/TwinProduction/gatus/v3/core"
"github.com/TwinProduction/gatus/v3/storage"
"github.com/TwinProduction/gatus/v3/watchdog"
)
func TestSinglePageApplication(t *testing.T) {

View File

@@ -4,7 +4,7 @@ import (
"net/http"
"strconv"
"github.com/TwinProduction/gatus/storage/store/common"
"github.com/TwinProduction/gatus/v3/storage/store/common"
)
const (

View File

@@ -6,8 +6,8 @@ import (
"strings"
"time"
"github.com/TwinProduction/gatus/jsonpath"
"github.com/TwinProduction/gatus/pattern"
"github.com/TwinProduction/gatus/v3/jsonpath"
"github.com/TwinProduction/gatus/v3/pattern"
)
const (

View File

@@ -7,15 +7,14 @@ import (
)
func TestCondition_evaluate(t *testing.T) {
type scenario struct {
scenarios := []struct {
Name string
Condition Condition
Result *Result
DontResolveFailedConditions bool
ExpectedSuccess bool
ExpectedOutput string
}
scenarios := []scenario{
}{
{
Name: "ip",
Condition: Condition("[IP] == 127.0.0.1"),

View File

@@ -3,7 +3,7 @@ package core
import (
"testing"
"github.com/TwinProduction/gatus/pattern"
"github.com/TwinProduction/gatus/v3/pattern"
)
func TestIntegrationQuery(t *testing.T) {

View File

@@ -12,10 +12,10 @@ import (
"strings"
"time"
"github.com/TwinProduction/gatus/alerting/alert"
"github.com/TwinProduction/gatus/client"
"github.com/TwinProduction/gatus/core/ui"
"github.com/TwinProduction/gatus/util"
"github.com/TwinProduction/gatus/v3/alerting/alert"
"github.com/TwinProduction/gatus/v3/client"
"github.com/TwinProduction/gatus/v3/core/ui"
"github.com/TwinProduction/gatus/v3/util"
)
const (
@@ -44,7 +44,11 @@ var (
)
// Service is the configuration of a monitored endpoint
// XXX: Rename this to Endpoint in v4.0.0?
type Service struct {
// Enabled defines whether to enable the service
Enabled *bool `yaml:"enabled,omitempty"`
// Name of the service. Can be anything.
Name string `yaml:"name"`
@@ -91,6 +95,14 @@ type Service struct {
NumberOfSuccessesInARow int
}
// IsEnabled returns whether the service is enabled or not
func (service Service) IsEnabled() bool {
if service.Enabled == nil {
return true
}
return *service.Enabled
}
// ValidateAndSetDefaults validates the service's configuration and sets the default value of fields that have one
func (service *Service) ValidateAndSetDefaults() error {
// Set default values
@@ -206,7 +218,8 @@ func (service *Service) call(result *Result) {
isServiceTCP := strings.HasPrefix(service.URL, "tcp://")
isServiceICMP := strings.HasPrefix(service.URL, "icmp://")
isServiceStartTLS := strings.HasPrefix(service.URL, "starttls://")
isServiceHTTP := !isServiceDNS && !isServiceTCP && !isServiceICMP && !isServiceStartTLS
isServiceTLS := strings.HasPrefix(service.URL, "tls://")
isServiceHTTP := !isServiceDNS && !isServiceTCP && !isServiceICMP && !isServiceStartTLS && !isServiceTLS
if isServiceHTTP {
request = service.buildHTTPRequest()
}
@@ -214,8 +227,12 @@ func (service *Service) call(result *Result) {
if isServiceDNS {
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.ClientConfig)
} else if isServiceStartTLS || isServiceTLS {
if isServiceStartTLS {
result.Connected, certificate, err = client.CanPerformStartTLS(strings.TrimPrefix(service.URL, "starttls://"), service.ClientConfig)
} else {
result.Connected, certificate, err = client.CanPerformStartTLS(strings.TrimPrefix(service.URL, "tls://"), service.ClientConfig)
}
if err != nil {
result.AddError(err.Error())
return

View File

@@ -6,15 +6,27 @@ import (
"testing"
"time"
"github.com/TwinProduction/gatus/alerting/alert"
"github.com/TwinProduction/gatus/client"
"github.com/TwinProduction/gatus/v3/alerting/alert"
"github.com/TwinProduction/gatus/v3/client"
)
func TestService_IsEnabled(t *testing.T) {
if !(Service{Enabled: nil}).IsEnabled() {
t.Error("service.IsEnabled() should've returned true, because Enabled was set to nil")
}
if value := false; (Service{Enabled: &value}).IsEnabled() {
t.Error("service.IsEnabled() should've returned false, because Enabled was set to false")
}
if value := true; !(Service{Enabled: &value}).IsEnabled() {
t.Error("Service.IsEnabled() should've returned true, because Enabled was set to true")
}
}
func TestService_ValidateAndSetDefaults(t *testing.T) {
condition := Condition("[STATUS] == 200")
service := Service{
Name: "twinnation-health",
URL: "https://twinnation.org/health",
Name: "website-health",
URL: "https://twin.sh/health",
Conditions: []*Condition{&condition},
Alerts: []*alert.Alert{{Type: alert.TypePagerDuty}},
}
@@ -58,8 +70,8 @@ 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",
Name: "website-health",
URL: "https://twin.sh/health",
Conditions: []*Condition{&condition},
ClientConfig: &client.Config{
Insecure: true,
@@ -147,8 +159,8 @@ func TestService_ValidateAndSetDefaultsWithDNS(t *testing.T) {
func TestService_buildHTTPRequest(t *testing.T) {
condition := Condition("[STATUS] == 200")
service := Service{
Name: "twinnation-health",
URL: "https://twinnation.org/health",
Name: "website-health",
URL: "https://twin.sh/health",
Conditions: []*Condition{&condition},
}
service.ValidateAndSetDefaults()
@@ -156,8 +168,8 @@ func TestService_buildHTTPRequest(t *testing.T) {
if request.Method != "GET" {
t.Error("request.Method should've been GET, but was", request.Method)
}
if request.Host != "twinnation.org" {
t.Error("request.Host should've been twinnation.org, but was", request.Host)
if request.Host != "twin.sh" {
t.Error("request.Host should've been twin.sh, but was", request.Host)
}
if userAgent := request.Header.Get("User-Agent"); userAgent != GatusUserAgent {
t.Errorf("request.Header.Get(User-Agent) should've been %s, but was %s", GatusUserAgent, userAgent)
@@ -167,8 +179,8 @@ func TestService_buildHTTPRequest(t *testing.T) {
func TestService_buildHTTPRequestWithCustomUserAgent(t *testing.T) {
condition := Condition("[STATUS] == 200")
service := Service{
Name: "twinnation-health",
URL: "https://twinnation.org/health",
Name: "website-health",
URL: "https://twin.sh/health",
Conditions: []*Condition{&condition},
Headers: map[string]string{
"User-Agent": "Test/2.0",
@@ -179,8 +191,8 @@ func TestService_buildHTTPRequestWithCustomUserAgent(t *testing.T) {
if request.Method != "GET" {
t.Error("request.Method should've been GET, but was", request.Method)
}
if request.Host != "twinnation.org" {
t.Error("request.Host should've been twinnation.org, but was", request.Host)
if request.Host != "twin.sh" {
t.Error("request.Host should've been twin.sh, but was", request.Host)
}
if userAgent := request.Header.Get("User-Agent"); userAgent != "Test/2.0" {
t.Errorf("request.Header.Get(User-Agent) should've been %s, but was %s", "Test/2.0", userAgent)
@@ -190,8 +202,8 @@ func TestService_buildHTTPRequestWithCustomUserAgent(t *testing.T) {
func TestService_buildHTTPRequestWithHostHeader(t *testing.T) {
condition := Condition("[STATUS] == 200")
service := Service{
Name: "twinnation-health",
URL: "https://twinnation.org/health",
Name: "website-health",
URL: "https://twin.sh/health",
Method: "POST",
Conditions: []*Condition{&condition},
Headers: map[string]string{
@@ -211,8 +223,8 @@ func TestService_buildHTTPRequestWithHostHeader(t *testing.T) {
func TestService_buildHTTPRequestWithGraphQLEnabled(t *testing.T) {
condition := Condition("[STATUS] == 200")
service := Service{
Name: "twinnation-graphql",
URL: "https://twinnation.org/graphql",
Name: "website-graphql",
URL: "https://twin.sh/graphql",
Method: "POST",
Conditions: []*Condition{&condition},
GraphQL: true,
@@ -243,8 +255,8 @@ func TestIntegrationEvaluateHealth(t *testing.T) {
condition := Condition("[STATUS] == 200")
bodyCondition := Condition("[BODY].status == UP")
service := Service{
Name: "twinnation-health",
URL: "https://twinnation.org/health",
Name: "website-health",
URL: "https://twin.sh/health",
Conditions: []*Condition{&condition, &bodyCondition},
}
service.ValidateAndSetDefaults()
@@ -263,8 +275,8 @@ func TestIntegrationEvaluateHealth(t *testing.T) {
func TestIntegrationEvaluateHealthWithFailure(t *testing.T) {
condition := Condition("[STATUS] == 500")
service := Service{
Name: "twinnation-health",
URL: "https://twinnation.org/health",
Name: "website-health",
URL: "https://twin.sh/health",
Conditions: []*Condition{&condition},
}
service.ValidateAndSetDefaults()

View File

@@ -39,9 +39,9 @@ alerting:
You can now add alerts of type `pagerduty` in the services you've defined, like so:
```yaml
services:
- name: twinnation
- name: website
interval: 30s
url: "https://twinnation.org/health"
url: "https://twin.sh/health"
alerts:
- type: pagerduty
enabled: true
@@ -56,7 +56,7 @@ services:
```
The sample above will do the following:
- Send a request to the `https://twinnation.org/health` (`services[].url`) specified every **30s** (`services[].interval`)
- Send a request to the `https://twin.sh/health` (`services[].url`) specified every **30s** (`services[].interval`)
- Evaluate the conditions to determine whether the service is "healthy" or not
- **If all conditions are not met 3 (`services[].alerts[].failure-threshold`) times in a row**: Gatus will create a new incident
- **If, after an incident has been triggered, all conditions are met 5 (`services[].alerts[].success-threshold`) times in a row _AND_ `services[].alerts[].send-on-resolved` is set to `true`**: Gatus will resolve the triggered incident

View File

@@ -1,16 +1,16 @@
metrics: true
services:
- name: TwiNNatioN
url: https://twinnation.org/health
- name: website
url: https://twin.sh/health
interval: 30s
conditions:
- "[STATUS] == 200"
- name: GitHub
- name: github
url: https://api.github.com/healthz
interval: 5m
conditions:
- "[STATUS] == 200"
- name: Example
- name: example
url: https://example.com/
conditions:
- "[STATUS] == 200"

View File

@@ -3,12 +3,12 @@ data:
config.yaml: |
metrics: true
services:
- name: TwiNNatioN
url: https://twinnation.org/health
- name: website
url: https://twin.sh/health
interval: 1m
conditions:
- "[STATUS] == 200"
- name: GitHub
- name: github
url: https://api.github.com/healthz
interval: 5m
conditions:
@@ -23,7 +23,7 @@ data:
- "[BODY].text == pat(*cat*)"
- "[STATUS] == pat(2*)"
- "[CONNECTED] == true"
- name: Example
- name: example
url: https://example.com/
conditions:
- "[STATUS] == 200"

27
go.mod
View File

@@ -1,23 +1,46 @@
module github.com/TwinProduction/gatus
module github.com/TwinProduction/gatus/v3
go 1.16
go 1.17
require (
github.com/TwinProduction/gocache v1.2.3
github.com/TwinProduction/health v1.0.0
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.1 // indirect
github.com/go-ping/ping v0.0.0-20201115131931-3300c582a663
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/golang/protobuf v1.4.3 // indirect
github.com/google/go-cmp v0.5.4 // indirect
github.com/gorilla/mux v1.8.0
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/lib/pq v1.10.3
github.com/mattn/go-isatty v0.0.12 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/miekg/dns v1.1.35
github.com/prometheus/client_golang v1.9.0
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.15.0 // indirect
github.com/prometheus/procfs v0.2.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
github.com/wcharczuk/go-chart/v2 v2.1.0
go.etcd.io/bbolt v1.3.5 // indirect
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d // indirect
golang.org/x/mod v0.4.0 // indirect
golang.org/x/net v0.0.0-20201224014010-6772e930b67b // indirect
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c // indirect
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/protobuf v1.25.0 // indirect
gopkg.in/yaml.v2 v2.4.0
lukechampine.com/uint128 v1.1.1 // indirect
modernc.org/cc/v3 v3.33.6 // indirect
modernc.org/ccgo/v3 v3.9.5 // indirect
modernc.org/libc v1.9.11 // indirect
modernc.org/mathutil v1.4.0 // indirect
modernc.org/memory v1.0.4 // indirect
modernc.org/opt v0.1.1 // indirect
modernc.org/sqlite v1.11.2
modernc.org/strutil v1.1.1 // indirect
modernc.org/token v1.0.0 // indirect
)

View File

@@ -7,10 +7,10 @@ import (
"syscall"
"time"
"github.com/TwinProduction/gatus/config"
"github.com/TwinProduction/gatus/controller"
"github.com/TwinProduction/gatus/storage"
"github.com/TwinProduction/gatus/watchdog"
"github.com/TwinProduction/gatus/v3/config"
"github.com/TwinProduction/gatus/v3/controller"
"github.com/TwinProduction/gatus/v3/storage"
"github.com/TwinProduction/gatus/v3/watchdog"
)
func main() {

View File

@@ -5,7 +5,7 @@ import (
"strconv"
"sync"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/v3/core"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)

View File

@@ -5,9 +5,9 @@ import (
"log"
"time"
"github.com/TwinProduction/gatus/storage/store"
"github.com/TwinProduction/gatus/storage/store/memory"
"github.com/TwinProduction/gatus/storage/store/sql"
"github.com/TwinProduction/gatus/v3/storage/store"
"github.com/TwinProduction/gatus/v3/storage/store/memory"
"github.com/TwinProduction/gatus/v3/storage/store/sql"
)
var (

View File

@@ -4,7 +4,7 @@ import (
"testing"
"time"
"github.com/TwinProduction/gatus/storage/store/sql"
"github.com/TwinProduction/gatus/v3/storage/store/sql"
)
func TestGet(t *testing.T) {

View File

@@ -6,10 +6,10 @@ import (
"sync"
"time"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/storage/store/common"
"github.com/TwinProduction/gatus/storage/store/common/paging"
"github.com/TwinProduction/gatus/util"
"github.com/TwinProduction/gatus/v3/core"
"github.com/TwinProduction/gatus/v3/storage/store/common"
"github.com/TwinProduction/gatus/v3/storage/store/common/paging"
"github.com/TwinProduction/gatus/v3/util"
"github.com/TwinProduction/gocache"
)

View File

@@ -4,8 +4,8 @@ import (
"testing"
"time"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/storage/store/common/paging"
"github.com/TwinProduction/gatus/v3/core"
"github.com/TwinProduction/gatus/v3/storage/store/common/paging"
)
var (

View File

@@ -3,7 +3,7 @@ package memory
import (
"time"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/v3/core"
)
const (

View File

@@ -4,7 +4,7 @@ import (
"testing"
"time"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/v3/core"
)
func BenchmarkProcessUptimeAfterResult(b *testing.B) {

View File

@@ -4,7 +4,7 @@ import (
"testing"
"time"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/v3/core"
)
func TestProcessUptimeAfterResult(t *testing.T) {

View File

@@ -1,9 +1,9 @@
package memory
import (
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/storage/store/common"
"github.com/TwinProduction/gatus/storage/store/common/paging"
"github.com/TwinProduction/gatus/v3/core"
"github.com/TwinProduction/gatus/v3/storage/store/common"
"github.com/TwinProduction/gatus/v3/storage/store/common/paging"
)
// ShallowCopyServiceStatus returns a shallow copy of a ServiceStatus with only the results

View File

@@ -3,9 +3,9 @@ package memory
import (
"testing"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/storage/store/common"
"github.com/TwinProduction/gatus/storage/store/common/paging"
"github.com/TwinProduction/gatus/v3/core"
"github.com/TwinProduction/gatus/v3/storage/store/common"
"github.com/TwinProduction/gatus/v3/storage/store/common/paging"
)
func BenchmarkShallowCopyServiceStatus(b *testing.B) {

View File

@@ -4,9 +4,9 @@ import (
"testing"
"time"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/storage/store/common"
"github.com/TwinProduction/gatus/storage/store/common/paging"
"github.com/TwinProduction/gatus/v3/core"
"github.com/TwinProduction/gatus/v3/storage/store/common"
"github.com/TwinProduction/gatus/v3/storage/store/common/paging"
)
func TestAddResult(t *testing.T) {

View File

@@ -8,10 +8,10 @@ import (
"strings"
"time"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/storage/store/common"
"github.com/TwinProduction/gatus/storage/store/common/paging"
"github.com/TwinProduction/gatus/util"
"github.com/TwinProduction/gatus/v3/core"
"github.com/TwinProduction/gatus/v3/storage/store/common"
"github.com/TwinProduction/gatus/v3/storage/store/common/paging"
"github.com/TwinProduction/gatus/v3/util"
_ "github.com/lib/pq"
_ "modernc.org/sqlite"
)
@@ -461,10 +461,7 @@ func (s *Store) updateServiceUptime(tx *sql.Tx, serviceID int64, result *core.Re
successfulExecutions,
result.Duration.Milliseconds(),
)
if err != nil {
return err
}
return nil
return err
}
func (s *Store) getAllServiceKeys(tx *sql.Tx) (keys []string, err error) {
@@ -477,7 +474,6 @@ func (s *Store) getAllServiceKeys(tx *sql.Tx) (keys []string, err error) {
_ = rows.Scan(&key)
keys = append(keys, key)
}
_ = rows.Close()
return
}
@@ -546,7 +542,6 @@ func (s *Store) getEventsByServiceID(tx *sql.Tx, serviceID int64, page, pageSize
_ = rows.Scan(&event.Type, &event.Timestamp)
events = append(events, event)
}
_ = rows.Close()
return
}
@@ -579,7 +574,6 @@ func (s *Store) getResultsByServiceID(tx *sql.Tx, serviceID int64, page, pageSiz
results = append([]*core.Result{result}, results...)
idResultMap[id] = result
}
_ = rows.Close()
// Get condition results
args := make([]interface{}, 0, len(idResultMap))
query := `SELECT service_result_id, condition, success
@@ -596,6 +590,7 @@ func (s *Store) getResultsByServiceID(tx *sql.Tx, serviceID int64, page, pageSiz
if err != nil {
return nil, err
}
defer rows.Close() // explicitly defer the close in case an error happens during the scan
for rows.Next() {
conditionResult := &core.ConditionResult{}
var serviceResultID int64
@@ -626,9 +621,7 @@ func (s *Store) getServiceUptime(tx *sql.Tx, serviceID int64, from, to time.Time
var totalExecutions, totalSuccessfulExecutions, totalResponseTime int
for rows.Next() {
_ = rows.Scan(&totalExecutions, &totalSuccessfulExecutions, &totalResponseTime)
break
}
_ = rows.Close()
if totalExecutions > 0 {
uptime = float64(totalSuccessfulExecutions) / float64(totalExecutions)
avgResponseTime = time.Duration(float64(totalResponseTime)/float64(totalExecutions)) * time.Millisecond
@@ -657,7 +650,6 @@ func (s *Store) getServiceAverageResponseTime(tx *sql.Tx, serviceID int64, from,
for rows.Next() {
_ = rows.Scan(&totalExecutions, &totalResponseTime)
}
_ = rows.Close()
if totalExecutions == 0 {
return 0, nil
}
@@ -688,7 +680,6 @@ func (s *Store) getServiceHourlyAverageResponseTimes(tx *sql.Tx, serviceID int64
_ = rows.Scan(&unixTimestampFlooredAtHour, &totalExecutions, &totalResponseTime)
hourlyAverageResponseTimes[unixTimestampFlooredAtHour] = int(float64(totalResponseTime) / float64(totalExecutions))
}
_ = rows.Close()
return hourlyAverageResponseTimes, nil
}
@@ -735,9 +726,7 @@ func (s *Store) getAgeOfOldestServiceUptimeEntry(tx *sql.Tx, serviceID int64) (t
for rows.Next() {
_ = rows.Scan(&oldestServiceUptimeUnixTimestamp)
found = true
break
}
_ = rows.Close()
if !found {
return 0, errNoRowsReturned
}

View File

@@ -4,9 +4,9 @@ import (
"testing"
"time"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/storage/store/common"
"github.com/TwinProduction/gatus/storage/store/common/paging"
"github.com/TwinProduction/gatus/v3/core"
"github.com/TwinProduction/gatus/v3/storage/store/common"
"github.com/TwinProduction/gatus/v3/storage/store/common/paging"
)
var (

View File

@@ -3,10 +3,10 @@ package store
import (
"time"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/storage/store/common/paging"
"github.com/TwinProduction/gatus/storage/store/memory"
"github.com/TwinProduction/gatus/storage/store/sql"
"github.com/TwinProduction/gatus/v3/core"
"github.com/TwinProduction/gatus/v3/storage/store/common/paging"
"github.com/TwinProduction/gatus/v3/storage/store/memory"
"github.com/TwinProduction/gatus/v3/storage/store/sql"
)
// Store is the interface that each stores should implement

View File

@@ -4,10 +4,10 @@ import (
"testing"
"time"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/storage/store/common/paging"
"github.com/TwinProduction/gatus/storage/store/memory"
"github.com/TwinProduction/gatus/storage/store/sql"
"github.com/TwinProduction/gatus/v3/core"
"github.com/TwinProduction/gatus/v3/storage/store/common/paging"
"github.com/TwinProduction/gatus/v3/storage/store/memory"
"github.com/TwinProduction/gatus/v3/storage/store/sql"
)
func BenchmarkStore_GetAllServiceStatuses(b *testing.B) {

View File

@@ -4,11 +4,11 @@ import (
"testing"
"time"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/storage/store/common"
"github.com/TwinProduction/gatus/storage/store/common/paging"
"github.com/TwinProduction/gatus/storage/store/memory"
"github.com/TwinProduction/gatus/storage/store/sql"
"github.com/TwinProduction/gatus/v3/core"
"github.com/TwinProduction/gatus/v3/storage/store/common"
"github.com/TwinProduction/gatus/v3/storage/store/common/paging"
"github.com/TwinProduction/gatus/v3/storage/store/memory"
"github.com/TwinProduction/gatus/v3/storage/store/sql"
)
var (

View File

@@ -1,11 +0,0 @@
module github.com/TwinProduction/gocache
go 1.16
require (
github.com/go-redis/redis v6.15.9+incompatible
github.com/onsi/ginkgo v1.14.1 // indirect
github.com/onsi/gomega v1.10.2 // indirect
github.com/tidwall/redcon v1.3.2
go.etcd.io/bbolt v1.3.5
)

View File

@@ -1,63 +0,0 @@
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.1 h1:jMU0WaQrP0a/YAEq8eJmJKjBoMs+pClEr1vDMlM/Do4=
github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.10.2 h1:aY/nuoWlKJud2J6U0E3NWsjlg+0GtwXxgEqthRdzlcs=
github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/tidwall/redcon v1.3.2 h1:8INx/Nm3VSUbDUT16TH1rMgYQsbXNqy9xcX70edHXbo=
github.com/tidwall/redcon v1.3.2/go.mod h1:bdYBm4rlcWpst2XMwKVzWDF9CoUxEbUmM7CQrKeOZas=
go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@@ -1,3 +0,0 @@
module github.com/TwinProduction/health
go 1.15

View File

@@ -1,3 +0,0 @@
module github.com/cespare/xxhash/v2
go 1.11

View File

View File

@@ -1,5 +0,0 @@
module github.com/go-ping/ping
go 1.14
require golang.org/x/net v0.0.0-20200904194848-62affa334b73

View File

@@ -1,10 +0,0 @@
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA=
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

View File

@@ -1,3 +0,0 @@
module github.com/gorilla/mux
go 1.12

3
vendor/github.com/lib/pq/go.mod generated vendored
View File

@@ -1,3 +0,0 @@
module github.com/lib/pq
go 1.13

View File

@@ -1,5 +0,0 @@
module github.com/mattn/go-isatty
go 1.12
require golang.org/x/sys v0.0.0-20200116001909-b77594299b42

View File

@@ -1,2 +0,0 @@
golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

11
vendor/github.com/miekg/dns/go.mod generated vendored
View File

@@ -1,11 +0,0 @@
module github.com/miekg/dns
go 1.12
require (
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550
golang.org/x/net v0.0.0-20190923162816-aa69164e4478
golang.org/x/sync v0.0.0-20190423024810-112230192c58
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 // indirect
)

39
vendor/github.com/miekg/dns/go.sum generated vendored
View File

@@ -1,39 +0,0 @@
golang.org/x/crypto v0.0.0-20181001203147-e3636079e1a4 h1:Vk3wNqEZwyGyei9yq5ekj7frek2u7HUfffJ1/opblzc=
golang.org/x/crypto v0.0.0-20181001203147-e3636079e1a4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472 h1:Gv7RPwsi3eZ2Fgewe3CBsuOebPwO27PoXzRpJPsvSSM=
golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392 h1:ACG4HJsFiNMf47Y4PeRoebLNy/2lXT9EtprMuTFWt1M=
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20180926154720-4dfa2610cdf3 h1:dgd4x4kJt7G4k4m93AYLzM8Ni6h2qLTfh9n9vXJT3/0=
golang.org/x/net v0.0.0-20180926154720-4dfa2610cdf3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180928133829-e4b3c5e90611 h1:O33LKL7WyJgjN9CvxfTIomjIClbd/Kq86/iipowHQU0=
golang.org/x/sys v0.0.0-20180928133829-e4b3c5e90611/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd h1:DBH9mDw0zluJT/R+nGuV3jWFWLFaHyYZWD4tOT+cjn0=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPTs2tR8uOySCbBP7BN/M=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@@ -1,9 +0,0 @@
module github.com/prometheus/procfs
go 1.12
require (
github.com/google/go-cmp v0.3.1
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e
)

View File

@@ -1,6 +0,0 @@
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e h1:LwyF2AFISC9nVbS6MgzsaQNSUsRXI49GS+YQ5KX/QH0=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View File

@@ -1,3 +0,0 @@
module github.com/remyoudompheng/bigfft
go 1.12

View File

@@ -1,8 +0,0 @@
module github.com/wcharczuk/go-chart/v2
go 1.15
require (
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5
)

View File

@@ -1,5 +0,0 @@
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 h1:QelT11PB4FXiDEXucrfNckHoFxwt8USGY1ajP1ZF5lM=
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

5
vendor/go.etcd.io/bbolt/go.mod generated vendored
View File

@@ -1,5 +0,0 @@
module go.etcd.io/bbolt
go 1.12
require golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5

2
vendor/go.etcd.io/bbolt/go.sum generated vendored
View File

@@ -1,2 +0,0 @@
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

3
vendor/golang.org/x/xerrors/go.mod generated vendored
View File

@@ -1,3 +0,0 @@
module golang.org/x/xerrors
go 1.11

5
vendor/gopkg.in/yaml.v2/go.mod generated vendored
View File

@@ -1,5 +0,0 @@
module gopkg.in/yaml.v2
go 1.15
require gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405

View File

@@ -1,3 +0,0 @@
module lukechampine.com/uint128
go 1.12

Some files were not shown because too many files have changed in this diff Show More