mirror of
https://github.com/0x1d/rcond.git
synced 2025-12-14 18:25:21 +01:00
feat: improve ENV variable overrides, introduce Node struct, refactoring
This commit is contained in:
@@ -54,6 +54,23 @@ func NewAgent(clusterConfig *config.ClusterConfig, clusterEvents map[string]func
|
||||
return &Agent{Serf: serf}, nil
|
||||
}
|
||||
|
||||
func Up(clusterConfig *config.ClusterConfig) (*Agent, error) {
|
||||
if clusterConfig.Enabled {
|
||||
log.Printf("[INFO] Starting cluster agent on %s:%d", clusterConfig.BindAddr, clusterConfig.BindPort)
|
||||
clusterAgent, err := NewAgent(clusterConfig, ClusterEventsMap())
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return nil, err
|
||||
}
|
||||
// join nodes in the cluster if the join addresses are provided
|
||||
if len(clusterConfig.Join) > 0 {
|
||||
clusterAgent.Join(clusterConfig.Join, true)
|
||||
}
|
||||
return clusterAgent, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Event sends a custom event to the Serf cluster.
|
||||
// It marshals the provided ClusterEvent into JSON and then uses Serf's UserEvent method to send the event.
|
||||
func (a *Agent) Event(event ClusterEvent) error {
|
||||
|
||||
@@ -3,19 +3,20 @@ package config
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/kelseyhightower/envconfig"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Hostname string `yaml:"hostname"`
|
||||
Hostname string `yaml:"hostname" envconfig:"HOSTNAME"`
|
||||
Rcond RcondConfig `yaml:"rcond"`
|
||||
Network NetworkConfig `yaml:"network"`
|
||||
Cluster ClusterConfig `yaml:"cluster"`
|
||||
}
|
||||
|
||||
type RcondConfig struct {
|
||||
Addr string `yaml:"addr"`
|
||||
ApiToken string `yaml:"api_token"`
|
||||
Addr string `yaml:"addr" envconfig:"RCOND_ADDR"`
|
||||
ApiToken string `yaml:"api_token" envconfig:"RCOND_API_TOKEN"`
|
||||
}
|
||||
|
||||
type NetworkConfig struct {
|
||||
@@ -38,28 +39,35 @@ type ConnectionConfig struct {
|
||||
}
|
||||
|
||||
type ClusterConfig struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
NodeName string `yaml:"node_name"`
|
||||
SecretKey string `yaml:"secret_key"`
|
||||
Join []string `yaml:"join"`
|
||||
AdvertiseAddr string `yaml:"advertise_addr"`
|
||||
AdvertisePort int `yaml:"advertise_port"`
|
||||
BindAddr string `yaml:"bind_addr"`
|
||||
BindPort int `yaml:"bind_port"`
|
||||
LogLevel string `yaml:"log_level"`
|
||||
Enabled bool `yaml:"enabled" envconfig:"CLUSTER_ENABLED"`
|
||||
NodeName string `yaml:"node_name" envconfig:"CLUSTER_NODE_NAME"`
|
||||
SecretKey string `yaml:"secret_key" envconfig:"CLUSTER_SECRET_KEY"`
|
||||
Join []string `yaml:"join" envconfig:"CLUSTER_JOIN"`
|
||||
AdvertiseAddr string `yaml:"advertise_addr" envconfig:"CLUSTER_ADVERTISE_ADDR"`
|
||||
AdvertisePort int `yaml:"advertise_port" envconfig:"CLUSTER_ADVERTISE_PORT"`
|
||||
BindAddr string `yaml:"bind_addr" envconfig:"CLUSTER_BIND_ADDR"`
|
||||
BindPort int `yaml:"bind_port" envconfig:"CLUSTER_BIND_PORT"`
|
||||
LogLevel string `yaml:"log_level" envconfig:"CLUSTER_LOG_LEVEL"`
|
||||
}
|
||||
|
||||
func LoadConfig(path string) (*Config, error) {
|
||||
yamlFile, err := os.ReadFile(path)
|
||||
// LoadConfig reads the configuration from a YAML file and environment variables.
|
||||
func LoadConfig(filename string) (*Config, error) {
|
||||
var config Config
|
||||
data, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var config Config
|
||||
err = yaml.Unmarshal(yamlFile, &config)
|
||||
err = yaml.Unmarshal(data, &config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = envconfig.Process("", &config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package http
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
@@ -44,6 +45,18 @@ func (s *Server) WithClusterAgent(agent *cluster.Agent) *Server {
|
||||
return s
|
||||
}
|
||||
|
||||
func Up(appConfig *config.Config, clusterAgent *cluster.Agent) *Server {
|
||||
srv := NewServer(appConfig)
|
||||
srv.WithClusterAgent(clusterAgent)
|
||||
srv.RegisterRoutes()
|
||||
|
||||
log.Printf("[INFO] Starting API server on %s", appConfig.Rcond.Addr)
|
||||
if err := srv.Start(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return srv
|
||||
}
|
||||
|
||||
func (s *Server) Start() error {
|
||||
return s.srv.ListenAndServe()
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/0x1d/rcond/pkg/system"
|
||||
"github.com/0x1d/rcond/pkg/util"
|
||||
"github.com/godbus/dbus/v5"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
@@ -334,7 +334,7 @@ func GetHostname() (string, error) {
|
||||
// SetHostname changes the static hostname via the system bus.
|
||||
// newHost is your desired hostname, interactive=false skips any prompt.
|
||||
func SetHostname(newHost string) error {
|
||||
return system.WithDbus(func(conn *dbus.Conn) error {
|
||||
return util.WithConnection(func(conn *dbus.Conn) error {
|
||||
obj := conn.Object(
|
||||
"org.freedesktop.hostname1",
|
||||
dbus.ObjectPath("/org/freedesktop/hostname1"),
|
||||
@@ -355,7 +355,7 @@ func SetHostname(newHost string) error {
|
||||
func ConfigureSTA(iface string, ssid string, password string, autoconnect bool) (string, error) {
|
||||
uuid := uuid.New()
|
||||
|
||||
err := system.WithDbus(func(conn *dbus.Conn) error {
|
||||
err := util.WithConnection(func(conn *dbus.Conn) error {
|
||||
_, err := AddStationConnection(conn, uuid, ssid, password, autoconnect)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create station connection: %v", err)
|
||||
@@ -377,7 +377,7 @@ func ConfigureSTA(iface string, ssid string, password string, autoconnect bool)
|
||||
func ConfigureAP(iface string, ssid string, password string, autoconnect bool) (string, error) {
|
||||
uuid := uuid.New()
|
||||
|
||||
err := system.WithDbus(func(conn *dbus.Conn) error {
|
||||
err := util.WithConnection(func(conn *dbus.Conn) error {
|
||||
_, err := AddAccessPointConnection(conn, uuid, ssid, password, autoconnect)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create access point connection: %v", err)
|
||||
@@ -398,7 +398,7 @@ func ConfigureAP(iface string, ssid string, password string, autoconnect bool) (
|
||||
// The connection will be activated on the specified interface.
|
||||
// Returns an error if any operation fails.
|
||||
func Up(iface string, uuid string) error {
|
||||
return system.WithDbus(func(conn *dbus.Conn) error {
|
||||
return util.WithConnection(func(conn *dbus.Conn) error {
|
||||
connPath, err := GetConnectionPath(conn, uuid)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -428,7 +428,7 @@ func Up(iface string, uuid string) error {
|
||||
// It takes the interface name as an argument.
|
||||
// Returns an error if the device cannot be found or disconnected.
|
||||
func Down(iface string) error {
|
||||
return system.WithDbus(func(conn *dbus.Conn) error {
|
||||
return util.WithConnection(func(conn *dbus.Conn) error {
|
||||
devPath, err := GetDeviceByIpIface(conn, iface)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -446,7 +446,7 @@ func Down(iface string) error {
|
||||
// If no connection with the UUID exists, it returns nil.
|
||||
// Returns an error if the connection exists but cannot be deleted.
|
||||
func Remove(uuid string) error {
|
||||
return system.WithDbus(func(conn *dbus.Conn) error {
|
||||
return util.WithConnection(func(conn *dbus.Conn) error {
|
||||
connPath, err := GetConnectionPath(conn, uuid)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
57
pkg/rcond/node.go
Normal file
57
pkg/rcond/node.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package rcond
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/0x1d/rcond/pkg/cluster"
|
||||
"github.com/0x1d/rcond/pkg/config"
|
||||
"github.com/0x1d/rcond/pkg/http"
|
||||
"github.com/0x1d/rcond/pkg/system"
|
||||
)
|
||||
|
||||
type Node struct {
|
||||
Config *config.Config
|
||||
ClusterAgent *cluster.Agent
|
||||
HttpApi *http.Server
|
||||
}
|
||||
|
||||
func NewNode(appConfig *config.Config) *Node {
|
||||
return &Node{
|
||||
Config: appConfig,
|
||||
HttpApi: Api(appConfig),
|
||||
ClusterAgent: Cluster(&appConfig.Cluster),
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Node) Up() {
|
||||
system.Configure(n.Config)
|
||||
n.HttpApi.WithClusterAgent(n.ClusterAgent)
|
||||
n.HttpApi.RegisterRoutes()
|
||||
|
||||
log.Printf("[INFO] Starting API server on %s", n.Config.Rcond.Addr)
|
||||
if err := n.HttpApi.Start(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Api(appConfig *config.Config) *http.Server {
|
||||
srv := http.NewServer(appConfig)
|
||||
return srv
|
||||
}
|
||||
|
||||
func Cluster(clusterConfig *config.ClusterConfig) *cluster.Agent {
|
||||
if clusterConfig.Enabled {
|
||||
log.Printf("[INFO] Starting cluster agent on %s:%d", clusterConfig.BindAddr, clusterConfig.BindPort)
|
||||
clusterAgent, err := cluster.NewAgent(clusterConfig, cluster.ClusterEventsMap())
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return nil
|
||||
}
|
||||
// join nodes in the cluster if the join addresses are provided
|
||||
if len(clusterConfig.Join) > 0 {
|
||||
clusterAgent.Join(clusterConfig.Join, true)
|
||||
}
|
||||
return clusterAgent
|
||||
}
|
||||
return nil
|
||||
}
|
||||
47
pkg/system/init.go
Normal file
47
pkg/system/init.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/0x1d/rcond/pkg/config"
|
||||
"github.com/0x1d/rcond/pkg/network"
|
||||
"github.com/0x1d/rcond/pkg/util"
|
||||
"github.com/godbus/dbus/v5"
|
||||
)
|
||||
|
||||
func Configure(appConfig *config.Config) error {
|
||||
log.Print("[INFO] Configure system")
|
||||
// configure hostname
|
||||
if err := network.SetHostname(appConfig.Hostname); err != nil {
|
||||
log.Printf("[ERROR] setting hostname failed: %s", err)
|
||||
}
|
||||
// configure network connections
|
||||
for _, connection := range appConfig.Network.Connections {
|
||||
err := util.WithConnection(func(conn *dbus.Conn) error {
|
||||
_, err := network.AddConnectionWithConfig(conn, &network.ConnectionConfig{
|
||||
Type: connection.Type,
|
||||
UUID: connection.UUID,
|
||||
ID: connection.ID,
|
||||
AutoConnect: connection.AutoConnect,
|
||||
SSID: connection.SSID,
|
||||
Mode: connection.Mode,
|
||||
Band: connection.Band,
|
||||
Channel: connection.Channel,
|
||||
KeyMgmt: connection.KeyMgmt,
|
||||
PSK: connection.PSK,
|
||||
IPv4Method: connection.IPv4Method,
|
||||
IPv6Method: connection.IPv6Method,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] configuring connections failed: %s", err)
|
||||
}
|
||||
|
||||
}
|
||||
log.Print("[INFO] System configured")
|
||||
return nil
|
||||
}
|
||||
@@ -3,12 +3,13 @@ package system
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/0x1d/rcond/pkg/util"
|
||||
"github.com/godbus/dbus/v5"
|
||||
)
|
||||
|
||||
// Restart restarts the system.
|
||||
func Restart() error {
|
||||
return WithDbus(func(conn *dbus.Conn) error {
|
||||
return util.WithConnection(func(conn *dbus.Conn) error {
|
||||
obj := conn.Object("org.freedesktop.systemd1", "/org/freedesktop/systemd1")
|
||||
log.Println("Rebooting system...")
|
||||
call := obj.Call("org.freedesktop.systemd1.Manager.Reboot", 0)
|
||||
@@ -21,7 +22,7 @@ func Restart() error {
|
||||
|
||||
// Shutdown shuts down the system.
|
||||
func Shutdown() error {
|
||||
return WithDbus(func(conn *dbus.Conn) error {
|
||||
return util.WithConnection(func(conn *dbus.Conn) error {
|
||||
obj := conn.Object("org.freedesktop.systemd1", "/org/freedesktop/systemd1")
|
||||
log.Println("Shutting down system...")
|
||||
call := obj.Call("org.freedesktop.systemd1.Manager.PowerOff", 0)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package system
|
||||
package util
|
||||
|
||||
import (
|
||||
"log"
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
"github.com/godbus/dbus/v5"
|
||||
)
|
||||
|
||||
// WithDbus executes the given function with a D-Bus system connection
|
||||
// WithConnection executes the given function with a D-Bus system connection
|
||||
// and handles any connection errors
|
||||
func WithDbus(fn func(*dbus.Conn) error) error {
|
||||
func WithConnection(fn func(*dbus.Conn) error) error {
|
||||
conn, err := dbus.SystemBus()
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Failed to connect to system bus: %v", err)
|
||||
Reference in New Issue
Block a user