diff options
| author | Shulhan <ms@kilabit.info> | 2025-07-21 00:22:50 +0700 |
|---|---|---|
| committer | Shulhan <ms@kilabit.info> | 2025-07-23 09:32:16 +0700 |
| commit | 3d4c7b48674cd553ce8f670933af9b609992ae77 (patch) | |
| tree | ae35fc18ed901bcb3d13aa3a0e4abf45287f2579 | |
| parent | 4d4b26dff03083955871439ee6e0ef9bd07c5a1c (diff) | |
| download | lilin-3d4c7b48674cd553ce8f670933af9b609992ae77.tar.xz | |
wip: implement loading per service configuration
The service configuration is stored under "$BASE_DIR/etc/lilin/service.d".
with ".cfg" as the file extension.
The service configuration use INI format.
| -rw-r--r-- | lilin_test.go | 18 | ||||
| -rw-r--r-- | server.go | 6 | ||||
| -rw-r--r-- | server_options.go | 7 | ||||
| -rw-r--r-- | service.go | 5 | ||||
| -rw-r--r-- | testdata/etc/lilin/service.d/http.cfg | 5 | ||||
| -rw-r--r-- | testdata/etc/lilin/service.d/tcp.cfg | 4 | ||||
| -rw-r--r-- | testdata/etc/lilin/service.d/udp.cfg | 4 | ||||
| l--------- | testdata/worker/readError/etc/lilin/service.d/broken.cfg | 1 | ||||
| -rw-r--r-- | testdata/worker/readError/etc/lilin/service.d/dir.cfg/.gitignore | 0 | ||||
| -rw-r--r-- | testdata/worker/withoutServiceDir/etc/lilin/.gitignore | 2 | ||||
| -rw-r--r-- | worker.go | 67 | ||||
| -rw-r--r-- | worker_test.go | 58 |
12 files changed, 167 insertions, 10 deletions
diff --git a/lilin_test.go b/lilin_test.go index 804bc3c..ba1c069 100644 --- a/lilin_test.go +++ b/lilin_test.go @@ -84,6 +84,22 @@ func TestServer_handleServicesSummary(t *testing.T) { t.Fatal(err) } - var expSummary []lilin.Service + var expSummary = []lilin.Service{{ + Name: `example http`, + Type: `http`, + Method: `GET`, + Address: `http://127.0.0.1:6121/health`, + Timeout: `5s`, + }, { + Name: `example tcp`, + Type: `tcp`, + Address: `127.0.0.1:6122`, + Timeout: `5s`, + }, { + Name: `example udp`, + Type: `udp`, + Address: `127.0.0.1:6123`, + Timeout: `5s`, + }} test.Assert(t, `ServicesSummary`, expSummary, gotSummary) } @@ -32,7 +32,6 @@ func NewServer(opts ServerOptions) (srv *Server, err error) { srv = &Server{ Options: opts, - worker: newWorker(), } err = srv.Options.init() @@ -40,6 +39,11 @@ func NewServer(opts ServerOptions) (srv *Server, err error) { return nil, fmt.Errorf(`%s: %w`, logp, err) } + srv.worker, err = newWorker(srv.Options.configDir) + if err != nil { + return nil, fmt.Errorf(`%s: %w`, logp, err) + } + var mux = http.NewServeMux() mux.HandleFunc(`GET `+pathAPIServicesSummary, srv.handleServicesSummary) diff --git a/server_options.go b/server_options.go index 681ad14..5197ffd 100644 --- a/server_options.go +++ b/server_options.go @@ -19,6 +19,9 @@ type ServerOptions struct { // logs. BaseDir string + // The BaseDir + "/etc/lilin/". + configDir string + // The address to listen for HTTP server and APIs. Address string `ini:"server::address"` } @@ -32,7 +35,9 @@ func (opts *ServerOptions) init() (err error) { opts.BaseDir = `/` } - var cfg = filepath.Join(opts.BaseDir, `etc`, `lilin`, `lilin.cfg`) + opts.configDir = filepath.Join(opts.BaseDir, `etc`, `lilin`) + + var cfg = filepath.Join(opts.configDir, `lilin.cfg`) _, err = os.Stat(cfg) if err != nil { return nil @@ -4,4 +4,9 @@ package lilin type Service struct { + Name string + Type string `ini:"::type"` + Method string `ini:"::method"` + Address string `ini:"::address"` + Timeout string `ini:"::timeout"` } diff --git a/testdata/etc/lilin/service.d/http.cfg b/testdata/etc/lilin/service.d/http.cfg new file mode 100644 index 0000000..70e6d58 --- /dev/null +++ b/testdata/etc/lilin/service.d/http.cfg @@ -0,0 +1,5 @@ +[service "example http"] +type = http +method = GET +address = http://127.0.0.1:6121/health +timeout = 5s diff --git a/testdata/etc/lilin/service.d/tcp.cfg b/testdata/etc/lilin/service.d/tcp.cfg new file mode 100644 index 0000000..dce3c7d --- /dev/null +++ b/testdata/etc/lilin/service.d/tcp.cfg @@ -0,0 +1,4 @@ +[service "example tcp"] +type = tcp +address = 127.0.0.1:6122 +timeout = 5s diff --git a/testdata/etc/lilin/service.d/udp.cfg b/testdata/etc/lilin/service.d/udp.cfg new file mode 100644 index 0000000..b553c17 --- /dev/null +++ b/testdata/etc/lilin/service.d/udp.cfg @@ -0,0 +1,4 @@ +[service "example udp"] +type = udp +address = 127.0.0.1:6123 +timeout = 5s diff --git a/testdata/worker/readError/etc/lilin/service.d/broken.cfg b/testdata/worker/readError/etc/lilin/service.d/broken.cfg new file mode 120000 index 0000000..e4f37c6 --- /dev/null +++ b/testdata/worker/readError/etc/lilin/service.d/broken.cfg @@ -0,0 +1 @@ +../broken
\ No newline at end of file diff --git a/testdata/worker/readError/etc/lilin/service.d/dir.cfg/.gitignore b/testdata/worker/readError/etc/lilin/service.d/dir.cfg/.gitignore new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/testdata/worker/readError/etc/lilin/service.d/dir.cfg/.gitignore diff --git a/testdata/worker/withoutServiceDir/etc/lilin/.gitignore b/testdata/worker/withoutServiceDir/etc/lilin/.gitignore new file mode 100644 index 0000000..a68d087 --- /dev/null +++ b/testdata/worker/withoutServiceDir/etc/lilin/.gitignore @@ -0,0 +1,2 @@ +/* +!/.gitignore @@ -5,29 +5,82 @@ package lilin import ( "maps" + "os" + "path/filepath" "slices" + "strings" "sync" + + "git.sr.ht/~shulhan/pakakeh.go/lib/ini" ) // worker contains the report of all services. type worker struct { - // Internal service status with key is service name. - services map[string]Service + // Internal service status, where key is service name. + Services map[string]*Service `ini:"service"` sync.Mutex } -func newWorker() (wrk *worker) { - return &worker{ - services: make(map[string]Service), +func newWorker(configDir string) (wrk *worker, err error) { + wrk = &worker{ + Services: make(map[string]*Service), + } + err = wrk.loadServiceDir(configDir) + if err != nil { + return nil, err + } + return wrk, nil +} + +// loadServiceDir Load all the service configurations. +func (wrk *worker) loadServiceDir(configDir string) (err error) { + var serviceDir = filepath.Join(configDir, `service.d`) + + var listde []os.DirEntry + listde, err = os.ReadDir(serviceDir) + if err != nil { + return err + } + + var de os.DirEntry + for _, de = range listde { + if de.IsDir() { + continue + } + var name = de.Name() + if name[0] == '.' { + // Exclude hidden file. + continue + } + if !strings.HasSuffix(name, `.cfg`) { + continue + } + + var serviceCfg = filepath.Join(serviceDir, name) + var rawcfg []byte + + rawcfg, err = os.ReadFile(serviceCfg) + if err != nil { + return err + } + + err = ini.Unmarshal(rawcfg, wrk) + if err != nil { + return err + } + } + for name, service := range wrk.Services { + service.Name = name } + return nil } // summary return all services status ordered by name. func (wrk *worker) summary() (list []Service) { wrk.Lock() - var keys = slices.Sorted(maps.Keys(wrk.services)) + var keys = slices.Sorted(maps.Keys(wrk.Services)) for _, name := range keys { - list = append(list, wrk.services[name]) + list = append(list, *wrk.Services[name]) } wrk.Unlock() return list diff --git a/worker_test.go b/worker_test.go new file mode 100644 index 0000000..b8c7494 --- /dev/null +++ b/worker_test.go @@ -0,0 +1,58 @@ +// SPDX-FileCopyrightText: 2025 M. Shulhan <ms@kilabit.info> +// SPDX-License-Identifier: GPL-3.0-only + +package lilin + +import ( + "testing" + + "git.sr.ht/~shulhan/pakakeh.go/lib/test" +) + +func TestNewWorker(t *testing.T) { + type testCase struct { + expServices map[string]*Service + configDir string + expError string + } + + var listCase = []testCase{{ + configDir: `testdata/worker/withoutServiceDir/etc/lilin/`, + expError: `open testdata/worker/withoutServiceDir/etc/lilin/service.d: no such file or directory`, + }, { + configDir: `testdata/worker/readError/etc/lilin/`, + expError: `open testdata/worker/readError/etc/lilin/service.d/broken.cfg: no such file or directory`, + }, { + configDir: `testdata/etc/lilin/`, + expServices: map[string]*Service{ + `example http`: &Service{ + Name: `example http`, + Type: `http`, + Method: `GET`, + Address: `http://127.0.0.1:6121/health`, + Timeout: `5s`, + }, + `example tcp`: &Service{ + Name: `example tcp`, + Type: `tcp`, + Address: `127.0.0.1:6122`, + Timeout: `5s`, + }, + `example udp`: &Service{ + Name: `example udp`, + Type: `udp`, + Address: `127.0.0.1:6123`, + Timeout: `5s`, + }, + }, + }} + + for _, tcase := range listCase { + wrk, err := newWorker(tcase.configDir) + if err != nil { + test.Assert(t, ``, tcase.expError, err.Error()) + continue + } + test.Assert(t, `worker.Services`, tcase.expServices, wrk.Services) + } +} |
