aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lilin_test.go18
-rw-r--r--server.go6
-rw-r--r--server_options.go7
-rw-r--r--service.go5
-rw-r--r--testdata/etc/lilin/service.d/http.cfg5
-rw-r--r--testdata/etc/lilin/service.d/tcp.cfg4
-rw-r--r--testdata/etc/lilin/service.d/udp.cfg4
l---------testdata/worker/readError/etc/lilin/service.d/broken.cfg1
-rw-r--r--testdata/worker/readError/etc/lilin/service.d/dir.cfg/.gitignore0
-rw-r--r--testdata/worker/withoutServiceDir/etc/lilin/.gitignore2
-rw-r--r--worker.go67
-rw-r--r--worker_test.go58
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)
}
diff --git a/server.go b/server.go
index 2675521..bd272ec 100644
--- a/server.go
+++ b/server.go
@@ -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
diff --git a/service.go b/service.go
index f6c2e9c..29593a1 100644
--- a/service.go
+++ b/service.go
@@ -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
diff --git a/worker.go b/worker.go
index d8916b4..bb061e9 100644
--- a/worker.go
+++ b/worker.go
@@ -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)
+ }
+}