refactor: move http api to a new transport layer
This commit is contained in:
parent
476c4b056f
commit
0448f66ab2
41 changed files with 822 additions and 390 deletions
5
internal/transport/caller.go
Normal file
5
internal/transport/caller.go
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
package transport
|
||||
|
||||
type Caller interface {
|
||||
Call(host string, path string, data any, out any) error
|
||||
}
|
||||
16
internal/transport/codec/codec.go
Normal file
16
internal/transport/codec/codec.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package codec
|
||||
|
||||
type Codec interface {
|
||||
Decode(data []byte, out any) error
|
||||
Encode(data any) ([]byte, error)
|
||||
}
|
||||
|
||||
func Decode[T any](c Codec, data []byte) (T, error) {
|
||||
var out T
|
||||
err := c.Decode(data, &out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
func Encode[T any](c Codec, data T) ([]byte, error) {
|
||||
return c.Encode(data)
|
||||
}
|
||||
15
internal/transport/codec/json.go
Normal file
15
internal/transport/codec/json.go
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
package codec
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
type jsonCodec struct{}
|
||||
|
||||
var JSON = jsonCodec{}
|
||||
|
||||
func (jsonCodec) Decode(data []byte, out any) error {
|
||||
return json.Unmarshal(data, out)
|
||||
}
|
||||
|
||||
func (jsonCodec) Encode(data any) ([]byte, error) {
|
||||
return json.Marshal(data)
|
||||
}
|
||||
20
internal/transport/dns/client.go
Normal file
20
internal/transport/dns/client.go
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package dns
|
||||
|
||||
import (
|
||||
"git.wzray.com/homelab/hivemind/internal/transport"
|
||||
"git.wzray.com/homelab/hivemind/internal/types"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
caller transport.Caller
|
||||
}
|
||||
|
||||
func New(caller transport.Caller) *Client {
|
||||
return &Client{
|
||||
caller: caller,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) Callback(endpoint string, state types.HostState) (bool, error) {
|
||||
return callbackRoute.Call(c.caller, endpoint, state)
|
||||
}
|
||||
14
internal/transport/dns/handlers.go
Normal file
14
internal/transport/dns/handlers.go
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
package dns
|
||||
|
||||
import (
|
||||
"git.wzray.com/homelab/hivemind/internal/transport"
|
||||
"git.wzray.com/homelab/hivemind/internal/types"
|
||||
)
|
||||
|
||||
type DnsHandlers interface {
|
||||
Callback(types.HostState) (bool, error)
|
||||
}
|
||||
|
||||
func Register(registrator transport.Registrator, h DnsHandlers) {
|
||||
callbackRoute.Register(registrator, h.Callback)
|
||||
}
|
||||
10
internal/transport/dns/routes.go
Normal file
10
internal/transport/dns/routes.go
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
package dns
|
||||
|
||||
import (
|
||||
"git.wzray.com/homelab/hivemind/internal/transport"
|
||||
"git.wzray.com/homelab/hivemind/internal/types"
|
||||
)
|
||||
|
||||
var (
|
||||
callbackRoute = transport.NewRoute[types.HostState, bool]("/dns/callback")
|
||||
)
|
||||
9
internal/transport/helpers.go
Normal file
9
internal/transport/helpers.go
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
package transport
|
||||
|
||||
func WithoutInput[T any](f func() (T, error)) func(struct{}) (T, error) {
|
||||
return func(s struct{}) (T, error) { return f() }
|
||||
}
|
||||
|
||||
func WithoutOutput[T any](f func(T) error) func(T) (struct{}, error) {
|
||||
return func(t T) (struct{}, error) { return struct{}{}, f(t) }
|
||||
}
|
||||
24
internal/transport/host/client.go
Normal file
24
internal/transport/host/client.go
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
package host
|
||||
|
||||
import (
|
||||
"git.wzray.com/homelab/hivemind/internal/transport"
|
||||
"git.wzray.com/homelab/hivemind/internal/types"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
caller transport.Caller
|
||||
}
|
||||
|
||||
func New(caller transport.Caller) *Client {
|
||||
return &Client{
|
||||
caller: caller,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) Dns(endpoint string) (types.HostState, error) {
|
||||
return dnsRoute.CallNoInput(c.caller, endpoint)
|
||||
}
|
||||
|
||||
func (c *Client) Nameserver(endpoint string) (types.HostState, error) {
|
||||
return nsRoute.CallNoInput(c.caller, endpoint)
|
||||
}
|
||||
16
internal/transport/host/handlers.go
Normal file
16
internal/transport/host/handlers.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package host
|
||||
|
||||
import (
|
||||
"git.wzray.com/homelab/hivemind/internal/transport"
|
||||
"git.wzray.com/homelab/hivemind/internal/types"
|
||||
)
|
||||
|
||||
type HostHandlers interface {
|
||||
Dns() (types.HostState, error)
|
||||
Nameserver() (types.HostState, error)
|
||||
}
|
||||
|
||||
func Register(registrator transport.Registrator, h HostHandlers) {
|
||||
dnsRoute.Register(registrator, transport.WithoutInput(h.Dns))
|
||||
nsRoute.Register(registrator, transport.WithoutInput(h.Nameserver))
|
||||
}
|
||||
11
internal/transport/host/routes.go
Normal file
11
internal/transport/host/routes.go
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
package host
|
||||
|
||||
import (
|
||||
"git.wzray.com/homelab/hivemind/internal/transport"
|
||||
"git.wzray.com/homelab/hivemind/internal/types"
|
||||
)
|
||||
|
||||
var (
|
||||
dnsRoute = transport.NewRoute[struct{}, types.HostState]("/host/dns")
|
||||
nsRoute = transport.NewRoute[struct{}, types.HostState]("/host/ns")
|
||||
)
|
||||
40
internal/transport/master/client.go
Normal file
40
internal/transport/master/client.go
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
package master
|
||||
|
||||
import (
|
||||
"git.wzray.com/homelab/hivemind/internal/transport"
|
||||
"git.wzray.com/homelab/hivemind/internal/types"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
caller transport.Caller
|
||||
}
|
||||
|
||||
func New(caller transport.Caller) *Client {
|
||||
return &Client{
|
||||
caller: caller,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) Heartbeat(endpoint string, n types.Node) (types.Nodes, error) {
|
||||
return heartbeatRoute.Call(c.caller, endpoint, n)
|
||||
}
|
||||
|
||||
func (c *Client) Join(endpoint string, n types.Node) (types.Nodes, error) {
|
||||
return joinRoute.Call(c.caller, endpoint, n)
|
||||
}
|
||||
|
||||
func (c *Client) Leave(endpoint string, n types.Node) error {
|
||||
return leaveRoute.CallNoOutput(c.caller, endpoint, n)
|
||||
}
|
||||
|
||||
func (c *Client) EventHeartbeat(endpoint string, n types.Node) (types.Nodes, error) {
|
||||
return eventHeartbeatRoute.Call(c.caller, endpoint, n)
|
||||
}
|
||||
|
||||
func (c *Client) EventJoin(endpoint string, n types.Node) (types.Nodes, error) {
|
||||
return eventJoinRoute.Call(c.caller, endpoint, n)
|
||||
}
|
||||
|
||||
func (c *Client) EventLeave(endpoint string, n types.Node) error {
|
||||
return eventLeaveRoute.CallNoOutput(c.caller, endpoint, n)
|
||||
}
|
||||
26
internal/transport/master/handlers.go
Normal file
26
internal/transport/master/handlers.go
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
package master
|
||||
|
||||
import (
|
||||
"git.wzray.com/homelab/hivemind/internal/transport"
|
||||
"git.wzray.com/homelab/hivemind/internal/types"
|
||||
)
|
||||
|
||||
type MasterHandlers interface {
|
||||
Heartbeat(types.Node) (types.Nodes, error)
|
||||
Join(types.Node) (types.Nodes, error)
|
||||
Leave(types.Node) error
|
||||
|
||||
EventHeartbeat(types.Node) (types.Nodes, error)
|
||||
EventJoin(types.Node) (types.Nodes, error)
|
||||
EventLeave(types.Node) error
|
||||
}
|
||||
|
||||
func Register(registrator transport.Registrator, h MasterHandlers) {
|
||||
heartbeatRoute.Register(registrator, h.Heartbeat)
|
||||
joinRoute.Register(registrator, h.Join)
|
||||
leaveRoute.Register(registrator, transport.WithoutOutput(h.Leave))
|
||||
|
||||
eventHeartbeatRoute.Register(registrator, h.EventHeartbeat)
|
||||
eventJoinRoute.Register(registrator, h.EventJoin)
|
||||
eventLeaveRoute.Register(registrator, transport.WithoutOutput(h.EventLeave))
|
||||
}
|
||||
16
internal/transport/master/routes.go
Normal file
16
internal/transport/master/routes.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package master
|
||||
|
||||
import (
|
||||
"git.wzray.com/homelab/hivemind/internal/transport"
|
||||
"git.wzray.com/homelab/hivemind/internal/types"
|
||||
)
|
||||
|
||||
var (
|
||||
heartbeatRoute = transport.NewRoute[types.Node, types.Nodes]("/master/heartbeat")
|
||||
joinRoute = transport.NewRoute[types.Node, types.Nodes]("/master/join")
|
||||
leaveRoute = transport.NewRoute[types.Node, struct{}]("/master/leave")
|
||||
|
||||
eventHeartbeatRoute = transport.NewRoute[types.Node, types.Nodes]("/master/event/heartbeat")
|
||||
eventJoinRoute = transport.NewRoute[types.Node, types.Nodes]("/master/event/join")
|
||||
eventLeaveRoute = transport.NewRoute[types.Node, struct{}]("/master/event/leave")
|
||||
)
|
||||
17
internal/transport/node/client.go
Normal file
17
internal/transport/node/client.go
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package node
|
||||
|
||||
import (
|
||||
"git.wzray.com/homelab/hivemind/internal/transport"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
caller transport.Caller
|
||||
}
|
||||
|
||||
func New(caller transport.Caller) *Client {
|
||||
return &Client{caller: caller}
|
||||
}
|
||||
|
||||
func (c *Client) Healthcheck(endpoint string) (string, error) {
|
||||
return healthcheckRoute.CallNoInput(c.caller, endpoint)
|
||||
}
|
||||
13
internal/transport/node/handlers.go
Normal file
13
internal/transport/node/handlers.go
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package node
|
||||
|
||||
import (
|
||||
"git.wzray.com/homelab/hivemind/internal/transport"
|
||||
)
|
||||
|
||||
type NodeHandlers interface {
|
||||
Healthcheck() (string, error)
|
||||
}
|
||||
|
||||
func Register(registrator transport.Registrator, h NodeHandlers) {
|
||||
healthcheckRoute.Register(registrator, transport.WithoutInput(h.Healthcheck))
|
||||
}
|
||||
9
internal/transport/node/routes.go
Normal file
9
internal/transport/node/routes.go
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
package node
|
||||
|
||||
import (
|
||||
"git.wzray.com/homelab/hivemind/internal/transport"
|
||||
)
|
||||
|
||||
var (
|
||||
healthcheckRoute = transport.NewRoute[struct{}, string]("/node/healthcheck")
|
||||
)
|
||||
22
internal/transport/registrator.go
Normal file
22
internal/transport/registrator.go
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
package transport
|
||||
|
||||
import (
|
||||
"git.wzray.com/homelab/hivemind/internal/transport/codec"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
path string
|
||||
handler func(codec.Codec, []byte) ([]byte, error)
|
||||
}
|
||||
|
||||
func (h Handler) Path() string {
|
||||
return h.path
|
||||
}
|
||||
|
||||
func (h Handler) Handle(c codec.Codec, v []byte) ([]byte, error) {
|
||||
return h.handler(c, v)
|
||||
}
|
||||
|
||||
type Registrator interface {
|
||||
Register(endpoint Handler)
|
||||
}
|
||||
65
internal/transport/route.go
Normal file
65
internal/transport/route.go
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
package transport
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.wzray.com/homelab/hivemind/internal/transport/codec"
|
||||
)
|
||||
|
||||
type route[In, Out any] struct {
|
||||
path string
|
||||
}
|
||||
|
||||
func NewRoute[In, Out any](path string) route[In, Out] {
|
||||
return route[In, Out]{
|
||||
path: path,
|
||||
}
|
||||
}
|
||||
|
||||
func routeToHandler[In, Out any](r route[In, Out], handler func(In) (Out, error)) Handler {
|
||||
return Handler{
|
||||
path: r.path,
|
||||
handler: func(c codec.Codec, b []byte) ([]byte, error) {
|
||||
data, err := codec.Decode[In](c, b)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to decode body: %w", err)
|
||||
}
|
||||
|
||||
out, err := handler(data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error while handling request: %w", err)
|
||||
}
|
||||
|
||||
raw, err := codec.Encode(c, out)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to encode body: %w", err)
|
||||
}
|
||||
|
||||
return raw, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (e route[In, Out]) Call(caller Caller, host string, data In) (Out, error) {
|
||||
var out Out
|
||||
err := caller.Call(host, e.path, data, &out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
func (e route[In, Out]) CallNoInput(caller Caller, host string) (Out, error) {
|
||||
var out Out
|
||||
err := caller.Call(host, e.path, struct{}{}, &out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
func (e route[In, Out]) CallNoOutput(caller Caller, host string, data In) error {
|
||||
return caller.Call(host, e.path, data, nil)
|
||||
}
|
||||
|
||||
func (e route[In, Out]) Path() string {
|
||||
return e.path
|
||||
}
|
||||
|
||||
func (e route[In, Out]) Register(r Registrator, h func(In) (Out, error)) {
|
||||
r.Register(routeToHandler(e, h))
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue