Add configuration values for address and port

Add (non-mandatory) configuration values to set address and port on which the web frontent will be served. If not set defaults will be applied.
This commit is contained in:
Michael Engelhardt 2020-11-19 19:39:48 +01:00
parent 9f485b14e0
commit 10310cf380
5 changed files with 210 additions and 34 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
bin
.idea
.vscode

View File

@ -133,6 +133,8 @@ Note that you can also add environment variables in the configuration file (i.e.
| `security.basic.username` | Username for Basic authentication | Required `""` |
| `security.basic.password-sha512` | Password's SHA512 hash for Basic authentication | Required `""` |
| `disable-monitoring-lock` | Whether to [disable the monitoring lock](#disable-monitoring-lock) | `false` |
| `web.address` | Address to listen on | `0.0.0.0` |
| `web.port` | Port to listen on | `8080` |
For Kubernetes configuration, see [Kubernetes](#kubernetes-alpha)

View File

@ -2,8 +2,10 @@ package config
import (
"errors"
"fmt"
"io/ioutil"
"log"
"math"
"os"
"github.com/TwinProduction/gatus/alerting"
@ -22,6 +24,12 @@ 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 (
@ -64,6 +72,9 @@ type Config struct {
// Kubernetes is the Kubernetes configuration
Kubernetes *k8s.Config `yaml:"kubernetes"`
// webConfig is the optional configuration of the web listener providing the frontend UI
Web *webConfig `yaml:"web"`
}
// Get returns the configuration, or panics if the configuration hasn't loaded yet
@ -127,10 +138,20 @@ func parseAndValidateConfigBytes(yamlBytes []byte) (config *Config, err error) {
validateSecurityConfig(config)
validateServicesConfig(config)
validateKubernetesConfig(config)
validateAddressAndPortConfig(config)
}
return
}
func validateAddressAndPortConfig(config *Config) {
if config.Web == nil {
config.Web = &webConfig{Address: DefaultAddress, Port: DefaultPort}
} else {
config.Web.validateAndSetDefaults()
}
}
func validateKubernetesConfig(config *Config) {
if config.Kubernetes != nil && config.Kubernetes.AutoDiscover {
if config.Kubernetes.ServiceTemplate == nil {
@ -237,3 +258,27 @@ func GetAlertingProviderByAlertType(config *Config, alertType core.AlertType) pr
}
return nil
}
// webConfig is the structure which supports the configuration of the endpoint
// which provides access to the web frontend
type webConfig struct {
// Address to listen on (defaults to 0.0.0.0 specified by DefaultAddress)
Address string `yaml:"address"`
// Port to listen on (default to 8080 specified by DefaultPort)
Port int `yaml:"port"`
}
// validateAndSetDefaults checks and sets missing values based on the defaults
// in given in DefaultAddress and DefaultPort if necessary
func (web *webConfig) validateAndSetDefaults() {
if len(web.Address) == 0 {
web.Address = DefaultAddress
}
if web.Port == 0 {
web.Port = DefaultPort
} else if web.Port < 0 || web.Port > math.MaxUint16 {
panic(fmt.Sprintf("port has an invalid value %d shoud be between %d - %d\r\n", web.Port, 0, math.MaxUint16))
}
}

View File

@ -103,6 +103,120 @@ 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.Port != DefaultPort {
t.Errorf("Port should have been %d, because it is the default value", DefaultPort)
}
}
func TestParseAndValidateConfigBytesWithAddress(t *testing.T) {
config, err := parseAndValidateConfigBytes([]byte(`
web:
address: 127.0.0.1
services:
- name: twinnation
url: https://twinnation.org/actuator/health
conditions:
- "[STATUS] == 200"
`))
if err != nil {
t.Error("No error should've been returned")
}
if config == nil {
t.Fatal("Config shouldn't have been nil")
}
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].Interval != 60*time.Second {
t.Errorf("Interval should have been %s, because it is the default value", 60*time.Second)
}
if config.Web.Address != "127.0.0.1" {
t.Errorf("Bind address should have been %s, because it is specified in config", "127.0.0.1")
}
if config.Web.Port != DefaultPort {
t.Errorf("Port should have been %d, because it is the default value", DefaultPort)
}
}
func TestParseAndValidateConfigBytesWithPort(t *testing.T) {
config, err := parseAndValidateConfigBytes([]byte(`
web:
port: 12345
services:
- name: twinnation
url: https://twinnation.org/actuator/health
conditions:
- "[STATUS] == 200"
`))
if err != nil {
t.Error("No error should've been returned")
}
if config == nil {
t.Fatal("Config shouldn't have been nil")
}
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].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.Port != 12345 {
t.Errorf("Port should have been %d, because it is specified in config", 12345)
}
}
func TestParseAndValidateConfigBytesWithPortAndHost(t *testing.T) {
config, err := parseAndValidateConfigBytes([]byte(`
web:
port: 12345
address: 127.0.0.1
services:
- name: twinnation
url: https://twinnation.org/actuator/health
conditions:
- "[STATUS] == 200"
`))
if err != nil {
t.Error("No error should've been returned")
}
if config == nil {
t.Fatal("Config shouldn't have been nil")
}
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].Interval != 60*time.Second {
t.Errorf("Interval should have been %s, because it is the default value", 60*time.Second)
}
if config.Web.Address != "127.0.0.1" {
t.Errorf("Bind address should have been %s, because it is specified in config", "127.0.0.1")
}
if config.Web.Port != 12345 {
t.Errorf("Port should have been %d, because it is specified in config", 12345)
}
}
func TestParseAndValidateConfigBytesWithMetrics(t *testing.T) {
@ -129,6 +243,51 @@ 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 specified in config", DefaultAddress)
}
if config.Web.Port != DefaultPort {
t.Errorf("Port should have been %d, because it is specified in config", DefaultPort)
}
}
func TestParseAndValidateConfigBytesWithMetricsAndHostAndPort(t *testing.T) {
config, err := parseAndValidateConfigBytes([]byte(`
metrics: true
services:
- name: twinnation
url: https://twinnation.org/actuator/health
conditions:
- "[STATUS] == 200"
web:
address: 192.168.0.1
port: 9090
`))
if err != nil {
t.Error("No error should've been returned")
}
if config == nil {
t.Fatal("Config shouldn't have been nil")
}
if !config.Metrics {
t.Error("Metrics should have been true")
}
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].Interval != 60*time.Second {
t.Errorf("Interval should have been %s, because it is the default value", 60*time.Second)
}
if config.Web.Address != "192.168.0.1" {
t.Errorf("Bind address should have been %s, because it is the default value", "192.168.0.1")
}
if config.Web.Port != 9090 {
t.Errorf("Port should have been %d, because it is specified in config", 9090)
}
}
func TestParseAndValidateBadConfigBytes(t *testing.T) {

35
main.go
View File

@ -8,7 +8,6 @@ import (
"log"
"net/http"
"os"
"strconv"
"strings"
"time"
@ -24,38 +23,8 @@ var (
cachedServiceResults []byte
cachedServiceResultsGzipped []byte
cachedServiceResultsTimestamp time.Time
port int
host string
)
func init() {
// Customizing priority:
// (1) command line parameters will be preferred over
// (2) environment variables will be preferred over
// (3) application defaults
// set defaults for the case that neither an environment variable nor a
// command line parameter is passed
var defaultHost = ""
var defaultPort = 8080
// assume set if the is a valid port number
if p, err := strconv.Atoi(os.Getenv("GATUS_CONFIG_PORT")); err == nil && p > 0 {
defaultPort = p
}
// explicitly asked if the user has set a the environment variable to
// blank / empty in order to allow listening on all interfaces
if h, set := os.LookupEnv("GATUS_CONFIG_HOST"); set == true {
defaultHost = h
}
flag.IntVar(&port, "port", defaultPort, "port to listen (default: 8080)")
flag.IntVar(&port, "p", defaultPort, "port to listen (default: 8080 ; shorthand)")
flag.StringVar(&host, "host", defaultHost, "host to listen on (default all interfaces on host)")
flag.StringVar(&host, "h", defaultHost, "host to listen on (default all interfaces on host; shorthand)")
}
func main() {
flag.Parse()
@ -71,9 +40,9 @@ func main() {
http.Handle("/metrics", promhttp.Handler())
}
log.Printf("[main][main] Listening on %s:%d", host, port)
log.Printf("[main][main] Listening on %s:%d\r\n", cfg.Web.Address, cfg.Web.Port)
go watchdog.Monitor(cfg)
log.Fatal(http.ListenAndServe(fmt.Sprintf("%s:%d", host, port), nil))
log.Fatal(http.ListenAndServe(fmt.Sprintf("%s:%d", cfg.Web.Address, cfg.Web.Port), nil))
}
func loadConfiguration() *config.Config {