feat: improve ENV variable overrides, introduce Node struct, refactoring

This commit is contained in:
2025-05-20 11:04:23 +02:00
parent bfc82870c3
commit d0a478d172
12 changed files with 210 additions and 149 deletions

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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()
}

View File

@@ -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
View 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
View 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
}

View File

@@ -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)

View File

@@ -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)