From df3a2016ffce9887e8b4796e0938e8bae6273d30 Mon Sep 17 00:00:00 2001 From: TwinProduction Date: Wed, 22 Sep 2021 00:47:51 -0400 Subject: [PATCH] Move web and ui configurations in their own packages --- config/config.go | 24 ++++------ config/config_test.go | 30 ++++++------ config/maintenance/maintenance_test.go | 2 +- config/ui.go | 41 ---------------- config/ui/ui.go | 48 +++++++++++++++++++ config/ui/ui_test.go | 26 +++++++++++ config/ui_test.go | 24 ---------- config/{ => web}/web.go | 26 +++++++---- config/web/web_test.go | 65 ++++++++++++++++++++++++++ config/web_test.go | 15 ------ controller/controller.go | 7 +-- controller/controller_test.go | 3 +- controller/handler/handler.go | 4 +- controller/handler/spa.go | 4 +- 14 files changed, 191 insertions(+), 128 deletions(-) delete mode 100644 config/ui.go create mode 100644 config/ui/ui.go create mode 100644 config/ui/ui_test.go delete mode 100644 config/ui_test.go rename config/{ => web}/web.go (52%) create mode 100644 config/web/web_test.go delete mode 100644 config/web_test.go diff --git a/config/config.go b/config/config.go index 5df80299..9ef21014 100644 --- a/config/config.go +++ b/config/config.go @@ -11,6 +11,8 @@ import ( "github.com/TwinProduction/gatus/alerting/alert" "github.com/TwinProduction/gatus/alerting/provider" "github.com/TwinProduction/gatus/config/maintenance" + "github.com/TwinProduction/gatus/config/ui" + "github.com/TwinProduction/gatus/config/web" "github.com/TwinProduction/gatus/core" "github.com/TwinProduction/gatus/security" "github.com/TwinProduction/gatus/storage" @@ -25,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 ( @@ -42,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 @@ -78,10 +70,10 @@ 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"` @@ -221,9 +213,9 @@ func validateMaintenanceConfig(config *Config) error { 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 } } @@ -232,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 } diff --git a/config/config_test.go b/config/config_test.go index 14ca81ba..85179be5 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -17,6 +17,8 @@ import ( "github.com/TwinProduction/gatus/alerting/provider/telegram" "github.com/TwinProduction/gatus/alerting/provider/twilio" "github.com/TwinProduction/gatus/client" + "github.com/TwinProduction/gatus/config/ui" + "github.com/TwinProduction/gatus/config/web" "github.com/TwinProduction/gatus/core" ) @@ -36,9 +38,9 @@ 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: @@ -175,11 +177,11 @@ 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://twin.sh/health" { t.Errorf("URL should have been %s", "https://twin.sh/health") @@ -226,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) } } @@ -256,8 +258,8 @@ 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 != 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) @@ -340,11 +342,11 @@ 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 != 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) diff --git a/config/maintenance/maintenance_test.go b/config/maintenance/maintenance_test.go index 8b087996..d90aabc9 100644 --- a/config/maintenance/maintenance_test.go +++ b/config/maintenance/maintenance_test.go @@ -14,7 +14,7 @@ func TestGetDefaultConfig(t *testing.T) { } } -func TestConfig_Validate(t *testing.T) { +func TestConfig_ValidateAndSetDefaults(t *testing.T) { yes, no := true, false scenarios := []struct { name string diff --git a/config/ui.go b/config/ui.go deleted file mode 100644 index 3de77948..00000000 --- a/config/ui.go +++ /dev/null @@ -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 -} diff --git a/config/ui/ui.go b/config/ui/ui.go new file mode 100644 index 00000000..5b445496 --- /dev/null +++ b/config/ui/ui.go @@ -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 +} diff --git a/config/ui/ui_test.go b/config/ui/ui_test.go new file mode 100644 index 00000000..8f5d5d87 --- /dev/null +++ b/config/ui/ui_test.go @@ -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) + } +} diff --git a/config/ui_test.go b/config/ui_test.go deleted file mode 100644 index fc31261d..00000000 --- a/config/ui_test.go +++ /dev/null @@ -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) - } -} diff --git a/config/web.go b/config/web/web.go similarity index 52% rename from config/web.go rename to config/web/web.go index 01543c16..b06aca98 100644 --- a/config/web.go +++ b/config/web/web.go @@ -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) } diff --git a/config/web/web_test.go b/config/web/web_test.go new file mode 100644 index 00000000..a6f68848 --- /dev/null +++ b/config/web/web_test.go @@ -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()) + } +} diff --git a/config/web_test.go b/config/web_test.go deleted file mode 100644 index 2a4e43ea..00000000 --- a/config/web_test.go +++ /dev/null @@ -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()) - } -} diff --git a/controller/controller.go b/controller/controller.go index 9355fc0f..6b54e5b4 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -8,7 +8,8 @@ import ( "os" "time" - "github.com/TwinProduction/gatus/config" + "github.com/TwinProduction/gatus/config/ui" + "github.com/TwinProduction/gatus/config/web" "github.com/TwinProduction/gatus/controller/handler" "github.com/TwinProduction/gatus/security" ) @@ -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) } diff --git a/controller/controller_test.go b/controller/controller_test.go index ea66bdac..f46aab03 100644 --- a/controller/controller_test.go +++ b/controller/controller_test.go @@ -8,12 +8,13 @@ import ( "testing" "github.com/TwinProduction/gatus/config" + "github.com/TwinProduction/gatus/config/web" "github.com/TwinProduction/gatus/core" ) func TestHandle(t *testing.T) { cfg := &config.Config{ - Web: &config.WebConfig{ + Web: &web.Config{ Address: "0.0.0.0", Port: rand.Intn(65534), }, diff --git a/controller/handler/handler.go b/controller/handler/handler.go index 25479648..6a5cb21a 100644 --- a/controller/handler/handler.go +++ b/controller/handler/handler.go @@ -3,14 +3,14 @@ package handler import ( "net/http" - "github.com/TwinProduction/gatus/config" + "github.com/TwinProduction/gatus/config/ui" "github.com/TwinProduction/gatus/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") diff --git a/controller/handler/spa.go b/controller/handler/spa.go index a66b4b56..9d3806fa 100644 --- a/controller/handler/spa.go +++ b/controller/handler/spa.go @@ -5,10 +5,10 @@ import ( "log" "net/http" - "github.com/TwinProduction/gatus/config" + "github.com/TwinProduction/gatus/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 {