mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-07-04 01:07:49 +02:00
cmd/ctrld: add control server/client via unix socket
This commit is contained in:
committed by
Cuong Manh Le
parent
fa3af372ab
commit
3007cb86ec
@@ -134,6 +134,11 @@ func initCLI() {
|
|||||||
stopCh: stopCh,
|
stopCh: stopCh,
|
||||||
cfg: &cfg,
|
cfg: &cfg,
|
||||||
}
|
}
|
||||||
|
if homedir == "" {
|
||||||
|
if dir, err := userHomeDir(); err == nil {
|
||||||
|
homedir = dir
|
||||||
|
}
|
||||||
|
}
|
||||||
sockPath := filepath.Join(homedir, ctrldLogUnixSock)
|
sockPath := filepath.Join(homedir, ctrldLogUnixSock)
|
||||||
if addr, err := net.ResolveUnixAddr("unix", sockPath); err == nil {
|
if addr, err := net.ResolveUnixAddr("unix", sockPath); err == nil {
|
||||||
if conn, err := net.Dial(addr.Network(), addr.String()); err == nil {
|
if conn, err := net.Dial(addr.Network(), addr.String()); err == nil {
|
||||||
@@ -186,6 +191,11 @@ func initCLI() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
p.router = router.New(&cfg)
|
p.router = router.New(&cfg)
|
||||||
|
cs, err := newControlServer(filepath.Join(homedir, ctrldControlUnixSock))
|
||||||
|
if err != nil {
|
||||||
|
mainLog.Warn().Err(err).Msg("could not create control server")
|
||||||
|
}
|
||||||
|
p.cs = cs
|
||||||
|
|
||||||
// Processing --cd flag require connecting to ControlD API, which needs valid
|
// Processing --cd flag require connecting to ControlD API, which needs valid
|
||||||
// time for validating server certificate. Some routers need NTP synchronization
|
// time for validating server certificate. Some routers need NTP synchronization
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type controlClient struct {
|
||||||
|
c *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func newControlClient(addr string) *controlClient {
|
||||||
|
return &controlClient{c: &http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
|
||||||
|
d := net.Dialer{}
|
||||||
|
return d.DialContext(ctx, "unix", addr)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Timeout: time.Second * 5,
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *controlClient) post(path string, data io.Reader) (*http.Response, error) {
|
||||||
|
return c.c.Post("http://unix"+path, contentTypeJson, data)
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const contentTypeJson = "application/json"
|
||||||
|
|
||||||
|
type controlServer struct {
|
||||||
|
server *http.Server
|
||||||
|
mux *http.ServeMux
|
||||||
|
addr string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newControlServer(addr string) (*controlServer, error) {
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
s := &controlServer{
|
||||||
|
server: &http.Server{Handler: mux},
|
||||||
|
mux: mux,
|
||||||
|
}
|
||||||
|
s.addr = addr
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *controlServer) start() error {
|
||||||
|
_ = os.Remove(s.addr)
|
||||||
|
unixListener, err := net.Listen("unix", s.addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go s.server.Serve(unixListener)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *controlServer) stop() error {
|
||||||
|
_ = os.Remove(s.addr)
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
|
||||||
|
defer cancel()
|
||||||
|
return s.server.Shutdown(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *controlServer) register(pattern string, handler http.Handler) {
|
||||||
|
s.mux.Handle(pattern, jsonResponse(handler))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *prog) registerControlServerHandler() {
|
||||||
|
// TODO: register handler here.
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsonResponse(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestControlServer(t *testing.T) {
|
||||||
|
f, err := os.CreateTemp("", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.Remove(f.Name())
|
||||||
|
f.Close()
|
||||||
|
|
||||||
|
s, err := newControlServer(f.Name())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
pattern := "/ping"
|
||||||
|
respBody := []byte("pong")
|
||||||
|
s.register(pattern, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
_, _ = w.Write(respBody)
|
||||||
|
}))
|
||||||
|
if err := s.start(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c := newControlClient(f.Name())
|
||||||
|
resp, err := c.post(pattern, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
t.Fatalf("unepxected response code: %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
if ct := resp.Header.Get("content-type"); ct != contentTypeJson {
|
||||||
|
t.Fatalf("unexpected content type: %s", ct)
|
||||||
|
}
|
||||||
|
buf, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(buf, respBody) {
|
||||||
|
t.Errorf("unexpected response body, want: %q, got: %q", string(respBody), string(buf))
|
||||||
|
}
|
||||||
|
if err := s.stop(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
-2
@@ -22,8 +22,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultSemaphoreCap = 256
|
defaultSemaphoreCap = 256
|
||||||
ctrldLogUnixSock = "ctrld_start.sock"
|
ctrldLogUnixSock = "ctrld_start.sock"
|
||||||
|
ctrldControlUnixSock = "ctrld_control.sock"
|
||||||
)
|
)
|
||||||
|
|
||||||
var logf = func(format string, args ...any) {
|
var logf = func(format string, args ...any) {
|
||||||
@@ -45,6 +46,7 @@ type prog struct {
|
|||||||
waitCh chan struct{}
|
waitCh chan struct{}
|
||||||
stopCh chan struct{}
|
stopCh chan struct{}
|
||||||
logConn net.Conn
|
logConn net.Conn
|
||||||
|
cs *controlServer
|
||||||
|
|
||||||
cfg *ctrld.Config
|
cfg *ctrld.Config
|
||||||
cache dnscache.Cacher
|
cache dnscache.Cacher
|
||||||
@@ -183,6 +185,12 @@ func (p *prog) run() {
|
|||||||
if p.logConn != nil {
|
if p.logConn != nil {
|
||||||
_ = p.logConn.Close()
|
_ = p.logConn.Close()
|
||||||
}
|
}
|
||||||
|
if p.cs != nil {
|
||||||
|
p.registerControlServerHandler()
|
||||||
|
if err := p.cs.start(); err != nil {
|
||||||
|
mainLog.Warn().Err(err).Msg("could not start control server")
|
||||||
|
}
|
||||||
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user