From 775c81977e0e23aeab9bf48f9738353a4115a42d Mon Sep 17 00:00:00 2001 From: Julien Dessaux Date: Sat, 20 Mar 2021 21:11:41 +0100 Subject: Added basic config module --- config/config.go | 56 ++++++++++++++++++ config/config_test.go | 68 ++++++++++++++++++++++ config/test_data/invalid_address.yaml | 3 + config/test_data/invalid_address_unresolvable.yaml | 3 + config/test_data/invalid_port.yaml | 3 + config/test_data/invalid_token.yaml | 3 + config/test_data/invalid_yaml | 1 + config/test_data/minimal.yaml | 3 + config/test_data/minimal_with_hostname.yaml | 3 + go.mod | 5 ++ go.sum | 6 ++ 11 files changed, 154 insertions(+) create mode 100644 config/config.go create mode 100644 config/config_test.go create mode 100644 config/test_data/invalid_address.yaml create mode 100644 config/test_data/invalid_address_unresolvable.yaml create mode 100644 config/test_data/invalid_port.yaml create mode 100644 config/test_data/invalid_token.yaml create mode 100644 config/test_data/invalid_yaml create mode 100644 config/test_data/minimal.yaml create mode 100644 config/test_data/minimal_with_hostname.yaml diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..653aeb6 --- /dev/null +++ b/config/config.go @@ -0,0 +1,56 @@ +package config + +import ( + "net" + "os" + "regexp" + + "github.com/pkg/errors" + "gopkg.in/yaml.v3" +) + +var validToken = regexp.MustCompile(`^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$`) + +type Config struct { + // Address is the hostname or ip the web server will listen to + Address string `yaml:"address",default:"127.0.0.1"` + Port string `yaml:"port",default:"8080"` + // Token is the sncf api token + Token string `yaml:"token"` +} + +func (c *Config) validate() error { + // address + if ip := net.ParseIP(c.Address); ip == nil { + if _, err := net.LookupIP(c.Address); err != nil { + return errors.New("Invalid address " + c.Address + ", it must be a valid ipv4 address, ipv6 address, or resolvable name.") + } + } + // port + if _, err := net.LookupPort("tcp", c.Port); err != nil { + return errors.New("Invalid port " + c.Port + ", it must be a valid port number or tcp service name. Got error : " + err.Error()) + } + // token + if ok := validToken.MatchString(c.Token); !ok { + return errors.New("Invalid token, must be an hexadecimal string that lookslike 12345678-9abc-def0-1234-56789abcdef0, got " + c.Token + " instead.") + } + return nil +} + +// LoadFile loads the c from a given file +func LoadFile(path string) (*Config, error) { + var c *Config + f, errOpen := os.Open(path) + if errOpen != nil { + return nil, errors.Wrapf(errOpen, "Failed to open configuration file %s", path) + } + defer f.Close() + decoder := yaml.NewDecoder(f) + if err := decoder.Decode(&c); err != nil { + return nil, errors.Wrap(err, "Failed to decode configuration file") + } + if err := c.validate(); err != nil { + return nil, errors.Wrap(err, "Failed to validate configuration") + } + return c, nil +} diff --git a/config/config_test.go b/config/config_test.go new file mode 100644 index 0000000..0b78260 --- /dev/null +++ b/config/config_test.go @@ -0,0 +1,68 @@ +package config + +import ( + "reflect" + "testing" +) + +func TestLoadFile(t *testing.T) { + // Non existant file + _, err := LoadFile("test_data/non-existant") + if err == nil { + t.Fatal("non-existant config file failed without error") + } + + // Invalid yaml file + _, err = LoadFile("test_data/invalid_yaml") + if err == nil { + t.Fatal("invalid_yaml config file failed without error") + } + + // Invalid address + if _, err = LoadFile("test_data/invalid_address.yaml"); err == nil { + t.Fatal("Invalid address should fail to load") + } + + // Invalid address unreasolvable + if _, err = LoadFile("test_data/invalid_address_unresolvable.yaml"); err == nil { + t.Fatal("Unresolvable address should fail to load") + } + + // Invalid port + if _, err = LoadFile("test_data/invalid_port.yaml"); err == nil { + t.Fatal("Invalid port should fail to load") + } + + // Invalid token + if _, err = LoadFile("test_data/invalid_token.yaml"); err == nil { + t.Fatal("Invalid token should fail to load") + } + + // Minimal yaml file + want := Config{ + Address: "127.0.0.2", + Port: "8082", + Token: "12345678-9abc-def0-1234-56789abcdef0", + } + config, err := LoadFile("test_data/minimal.yaml") + if err != nil { + t.Fatalf("minimal example failed with error: %v", err) + } + if config != nil && !reflect.DeepEqual(want, *config) { + t.Fatalf("minimal example failed:\nwant:%+v\ngot: %+v", want, *config) + } + + // Minimal yaml file with hostname resolving + want = Config{ + Address: "localhost", + Port: "8082", + Token: "12345678-9abc-def0-1234-56789abcdef0", + } + config, err = LoadFile("test_data/minimal_with_hostname.yaml") + if err != nil { + t.Fatalf("minimal example failed with error: %v", err) + } + if config != nil && !reflect.DeepEqual(want, *config) { + t.Fatalf("minimal example failed:\nwant:%+v\ngot: %+v", want, *config) + } +} diff --git a/config/test_data/invalid_address.yaml b/config/test_data/invalid_address.yaml new file mode 100644 index 0000000..e3682b2 --- /dev/null +++ b/config/test_data/invalid_address.yaml @@ -0,0 +1,3 @@ +address: "0.0.0.0.0" +port: 8082 +token: 12345678-9abc-def0-1234-56789abcdef0 diff --git a/config/test_data/invalid_address_unresolvable.yaml b/config/test_data/invalid_address_unresolvable.yaml new file mode 100644 index 0000000..d476093 --- /dev/null +++ b/config/test_data/invalid_address_unresolvable.yaml @@ -0,0 +1,3 @@ +address: "invalid" +port: 8082 +token: 12345678-9abc-def0-1234-56789abcdef0 diff --git a/config/test_data/invalid_port.yaml b/config/test_data/invalid_port.yaml new file mode 100644 index 0000000..2790b3f --- /dev/null +++ b/config/test_data/invalid_port.yaml @@ -0,0 +1,3 @@ +address: localhost +port: invalid +token: 12345678-9abc-def0-1234-56789abcdef0 diff --git a/config/test_data/invalid_token.yaml b/config/test_data/invalid_token.yaml new file mode 100644 index 0000000..6f9e297 --- /dev/null +++ b/config/test_data/invalid_token.yaml @@ -0,0 +1,3 @@ +address: 127.0.0.1 +port: 8080 +token: invalid diff --git a/config/test_data/invalid_yaml b/config/test_data/invalid_yaml new file mode 100644 index 0000000..db1ddad --- /dev/null +++ b/config/test_data/invalid_yaml @@ -0,0 +1 @@ +blargh(ads) diff --git a/config/test_data/minimal.yaml b/config/test_data/minimal.yaml new file mode 100644 index 0000000..2944645 --- /dev/null +++ b/config/test_data/minimal.yaml @@ -0,0 +1,3 @@ +address: 127.0.0.2 +port: 8082 +token: 12345678-9abc-def0-1234-56789abcdef0 diff --git a/config/test_data/minimal_with_hostname.yaml b/config/test_data/minimal_with_hostname.yaml new file mode 100644 index 0000000..716b2d5 --- /dev/null +++ b/config/test_data/minimal_with_hostname.yaml @@ -0,0 +1,3 @@ +address: localhost +port: 8082 +token: 12345678-9abc-def0-1234-56789abcdef0 diff --git a/go.mod b/go.mod index 542b54e..6b55e17 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,8 @@ module git.adyxax.org/adyxax/trains go 1.16 + +require ( + github.com/pkg/errors v0.9.1 + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b +) diff --git a/go.sum b/go.sum index e69de29..51a261f 100644 --- a/go.sum +++ b/go.sum @@ -0,0 +1,6 @@ +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -- cgit v1.2.3