mirror of
https://github.com/0x1d/rcond.git
synced 2025-12-15 18:48:19 +01:00
feat: apply configuration on start
This commit is contained in:
@@ -15,7 +15,7 @@ A distributed management daemon designed to remotely configure system components
|
|||||||
- Go
|
- Go
|
||||||
- NetworkManager
|
- NetworkManager
|
||||||
- systemd
|
- systemd
|
||||||
- Linux operating system
|
- Linux
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ import (
|
|||||||
"github.com/0x1d/rcond/pkg/cluster"
|
"github.com/0x1d/rcond/pkg/cluster"
|
||||||
"github.com/0x1d/rcond/pkg/config"
|
"github.com/0x1d/rcond/pkg/config"
|
||||||
http "github.com/0x1d/rcond/pkg/http"
|
http "github.com/0x1d/rcond/pkg/http"
|
||||||
|
"github.com/0x1d/rcond/pkg/network"
|
||||||
|
"github.com/0x1d/rcond/pkg/system"
|
||||||
|
"github.com/godbus/dbus/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
func usage() {
|
func usage() {
|
||||||
@@ -26,11 +29,13 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
configureSystem(appConfig)
|
||||||
clusterAgent := startClusterAgent(appConfig)
|
clusterAgent := startClusterAgent(appConfig)
|
||||||
startApiServer(appConfig, clusterAgent)
|
startApiServer(appConfig, clusterAgent)
|
||||||
|
|
||||||
select {}
|
select {}
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadConfig() (*config.Config, error) {
|
func loadConfig() (*config.Config, error) {
|
||||||
configPath := "/etc/rcond/config.yaml"
|
configPath := "/etc/rcond/config.yaml"
|
||||||
appConfig := &config.Config{}
|
appConfig := &config.Config{}
|
||||||
@@ -83,7 +88,7 @@ func startApiServer(appConfig *config.Config, clusterAgent *cluster.Agent) *http
|
|||||||
srv.WithClusterAgent(clusterAgent)
|
srv.WithClusterAgent(clusterAgent)
|
||||||
srv.RegisterRoutes()
|
srv.RegisterRoutes()
|
||||||
|
|
||||||
log.Printf("Starting API server on %s", appConfig.Rcond.Addr)
|
log.Printf("[INFO] Starting API server on %s", appConfig.Rcond.Addr)
|
||||||
if err := srv.Start(); err != nil {
|
if err := srv.Start(); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -93,7 +98,7 @@ func startApiServer(appConfig *config.Config, clusterAgent *cluster.Agent) *http
|
|||||||
func startClusterAgent(appConfig *config.Config) *cluster.Agent {
|
func startClusterAgent(appConfig *config.Config) *cluster.Agent {
|
||||||
clusterConfig := &appConfig.Cluster
|
clusterConfig := &appConfig.Cluster
|
||||||
if clusterConfig.Enabled {
|
if clusterConfig.Enabled {
|
||||||
log.Printf("Starting cluster agent on %s:%d", clusterConfig.BindAddr, clusterConfig.BindPort)
|
log.Printf("[INFO] Starting cluster agent on %s:%d", clusterConfig.BindAddr, clusterConfig.BindPort)
|
||||||
clusterAgent, err := cluster.NewAgent(clusterConfig, cluster.ClusterEventsMap())
|
clusterAgent, err := cluster.NewAgent(clusterConfig, cluster.ClusterEventsMap())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
@@ -107,6 +112,43 @@ func startClusterAgent(appConfig *config.Config) *cluster.Agent {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func configureSystem(appConfig *config.Config) error {
|
||||||
|
log.Print("[INFO] Configure system")
|
||||||
|
// configure hostname
|
||||||
|
if err := network.SetHostname(appConfig.Hostname); err != nil {
|
||||||
|
log.Printf("[ERROR] %s", err)
|
||||||
|
}
|
||||||
|
// configure network connections
|
||||||
|
for _, connection := range appConfig.Network.Connections {
|
||||||
|
err := system.WithDbus(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] %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
log.Print("[INFO] System configured")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func overrideConfigValuesFromEnv(envMap map[string]*string) {
|
func overrideConfigValuesFromEnv(envMap map[string]*string) {
|
||||||
for varName, configValue := range envMap {
|
for varName, configValue := range envMap {
|
||||||
if envValue, ok := os.LookupEnv(varName); ok {
|
if envValue, ok := os.LookupEnv(varName); ok {
|
||||||
|
|||||||
33
config/rcond-rpi-ap.yaml
Normal file
33
config/rcond-rpi-ap.yaml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
hostname: rpi-test
|
||||||
|
|
||||||
|
rcond:
|
||||||
|
addr: 0.0.0.0:8081
|
||||||
|
api_token: 1234567890
|
||||||
|
|
||||||
|
network:
|
||||||
|
connections:
|
||||||
|
# create access point and share network connection on wlan0
|
||||||
|
- name: MyHomeWiFi
|
||||||
|
type: 802-11-wireless
|
||||||
|
interface: wlan0
|
||||||
|
ssid: MyHomeWiFi
|
||||||
|
mode: ap
|
||||||
|
band: bg
|
||||||
|
channel: 1
|
||||||
|
keymgmt: wpa-psk
|
||||||
|
psk: SuperSecure
|
||||||
|
ipv4method: shared
|
||||||
|
ipv6method: ignore
|
||||||
|
autoconnect: true
|
||||||
|
|
||||||
|
cluster:
|
||||||
|
enabled: true
|
||||||
|
log_level: INFO
|
||||||
|
node_name: rcond-agent
|
||||||
|
secret_key: DMXnaJUUbIBMj1Df0dPsQY+Sks1VxWTa
|
||||||
|
advertise_addr: 0.0.0.0
|
||||||
|
advertise_port: 7946
|
||||||
|
bind_addr: 0.0.0.0
|
||||||
|
bind_port: 7946
|
||||||
|
#join:
|
||||||
|
# - 127.0.0.1:7946
|
||||||
38
config/rcond-rpi-sta.yaml
Normal file
38
config/rcond-rpi-sta.yaml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
hostname: rpi-test
|
||||||
|
|
||||||
|
rcond:
|
||||||
|
addr: 0.0.0.0:8081
|
||||||
|
api_token: 1234567890
|
||||||
|
|
||||||
|
network:
|
||||||
|
connections:
|
||||||
|
# connect to MyHomeWifi on wlan0
|
||||||
|
- name: MyHomeWiFi
|
||||||
|
type: 802-11-wireless
|
||||||
|
interface: wlan0
|
||||||
|
ssid: MyHomeWiFi
|
||||||
|
mode: infrastructure
|
||||||
|
keymgmt: wpa-psk
|
||||||
|
psk: SuperSecure
|
||||||
|
ipv4method: auto
|
||||||
|
ipv6method: ignore
|
||||||
|
autoconnect: true
|
||||||
|
# TODO
|
||||||
|
# provide DHCP on eth0
|
||||||
|
#- name: MyThingsNetwork
|
||||||
|
# type: 802-3-ethernet
|
||||||
|
# interface: eth0
|
||||||
|
# method: shared
|
||||||
|
# autoconnect: true
|
||||||
|
|
||||||
|
cluster:
|
||||||
|
enabled: true
|
||||||
|
log_level: INFO
|
||||||
|
node_name: rcond-agent
|
||||||
|
secret_key: DMXnaJUUbIBMj1Df0dPsQY+Sks1VxWTa
|
||||||
|
advertise_addr: 0.0.0.0
|
||||||
|
advertise_port: 7946
|
||||||
|
bind_addr: 0.0.0.0
|
||||||
|
bind_port: 7946
|
||||||
|
#join:
|
||||||
|
# - 127.0.0.1:7946
|
||||||
@@ -8,7 +8,7 @@ cluster:
|
|||||||
# Enable the cluster agent
|
# Enable the cluster agent
|
||||||
enabled: true
|
enabled: true
|
||||||
# Log level
|
# Log level
|
||||||
log_level: INFO
|
log_level: DEBUG
|
||||||
# Name of the node in the cluster
|
# Name of the node in the cluster
|
||||||
node_name: rcond
|
node_name: rcond
|
||||||
# Secret key for the cluster agent used for message encryption.
|
# Secret key for the cluster agent used for message encryption.
|
||||||
|
|||||||
1
go.mod
1
go.mod
@@ -7,6 +7,7 @@ replace github.com/0x1d/rcond/cmd => ./cmd
|
|||||||
replace github.com/0x1d/rcond/pkg => ./pkg
|
replace github.com/0x1d/rcond/pkg => ./pkg
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/godbus/dbus v4.1.0+incompatible
|
||||||
github.com/godbus/dbus/v5 v5.1.0
|
github.com/godbus/dbus/v5 v5.1.0
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/gorilla/mux v1.8.1
|
github.com/gorilla/mux v1.8.1
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -23,6 +23,8 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
|
|||||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
|
github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4=
|
||||||
|
github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
|
||||||
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
||||||
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
|
|||||||
@@ -69,19 +69,19 @@ func (a *Agent) Event(event ClusterEvent) error {
|
|||||||
|
|
||||||
// Members returns a list of members in the Serf cluster.
|
// Members returns a list of members in the Serf cluster.
|
||||||
func (a *Agent) Members() ([]serf.Member, error) {
|
func (a *Agent) Members() ([]serf.Member, error) {
|
||||||
log.Printf("Getting members of the cluster")
|
log.Printf("[INFO] Getting members of the cluster")
|
||||||
return a.Serf.Members(), nil
|
return a.Serf.Members(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Join attempts to join the Serf cluster with the given addresses, optionally ignoring old nodes.
|
// Join attempts to join the Serf cluster with the given addresses, optionally ignoring old nodes.
|
||||||
func (a *Agent) Join(addrs []string, ignoreOld bool) (int, error) {
|
func (a *Agent) Join(addrs []string, ignoreOld bool) (int, error) {
|
||||||
log.Printf("Joining nodes in the cluster: %v", addrs)
|
log.Printf("[INFO] Joining nodes in the cluster: %v", addrs)
|
||||||
n, err := a.Serf.Join(addrs, ignoreOld)
|
n, err := a.Serf.Join(addrs, ignoreOld)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to join nodes in the cluster: %v", err)
|
log.Printf("[ERROR] Failed to join nodes in the cluster: %v", err)
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
log.Printf("Joined %d nodes in the cluster", n)
|
log.Printf("[INFO] Joined %d nodes in the cluster", n)
|
||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,24 +92,24 @@ func (a *Agent) Leave() error {
|
|||||||
|
|
||||||
// Shutdown shuts down the Serf cluster agent.
|
// Shutdown shuts down the Serf cluster agent.
|
||||||
func (a *Agent) Shutdown() error {
|
func (a *Agent) Shutdown() error {
|
||||||
log.Printf("Shutting down cluster agent")
|
log.Printf("[INFO] Shutting down cluster agent")
|
||||||
return a.Serf.Shutdown()
|
return a.Serf.Shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleEvents handles Serf events received on the event channel.
|
// handleEvents handles Serf events received on the event channel.
|
||||||
func handleEvents(eventCh chan serf.Event, clusterEvents map[string]func([]byte)) {
|
func handleEvents(eventCh chan serf.Event, clusterEvents map[string]func([]byte)) {
|
||||||
|
eventHandlers := clusterEvents
|
||||||
for event := range eventCh {
|
for event := range eventCh {
|
||||||
switch event.EventType() {
|
switch event.EventType() {
|
||||||
case serf.EventUser:
|
case serf.EventUser:
|
||||||
userEvent := event.(serf.UserEvent)
|
userEvent := event.(serf.UserEvent)
|
||||||
eventHandlers := clusterEvents
|
|
||||||
if handler, ok := eventHandlers[userEvent.Name]; ok {
|
if handler, ok := eventHandlers[userEvent.Name]; ok {
|
||||||
handler(userEvent.Payload)
|
handler(userEvent.Payload)
|
||||||
} else {
|
} else {
|
||||||
log.Printf("No event handler found for event: %s", userEvent.Name)
|
log.Printf("[INFO] No event handler found for event: %s", userEvent.Name)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
log.Printf("Received event: %s\n", event.EventType())
|
log.Printf("[INFO] Received event: %s\n", event.EventType())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,18 +17,18 @@ func ClusterEventsMap() map[string]func([]byte) {
|
|||||||
|
|
||||||
func restart(payload []byte) {
|
func restart(payload []byte) {
|
||||||
if err := system.Restart(); err != nil {
|
if err := system.Restart(); err != nil {
|
||||||
log.Printf("(ClusterEvent:restart) failed: %s", err)
|
log.Printf("[ERROR] (ClusterEvent:restart) failed: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func shutdown(payload []byte) {
|
func shutdown(payload []byte) {
|
||||||
if err := system.Shutdown(); err != nil {
|
if err := system.Shutdown(); err != nil {
|
||||||
log.Printf("(ClusterEvent:shutdown) failed: %s", err)
|
log.Printf("[ERROR] (ClusterEvent:shutdown) failed: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// just a sample function to test event functionality
|
// just a sample function to test event functionality
|
||||||
func printHostname(payload []byte) {
|
func printHostname(payload []byte) {
|
||||||
hostname, _ := network.GetHostname()
|
hostname, _ := network.GetHostname()
|
||||||
log.Printf("(ClusterEvent:printHostname): %s", hostname)
|
log.Printf("[INFO] (ClusterEvent:printHostname): %s", hostname)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Rcond RcondConfig `yaml:"rcond"`
|
Hostname string `yaml:"hostname"`
|
||||||
Cluster ClusterConfig `yaml:"cluster"`
|
Rcond RcondConfig `yaml:"rcond"`
|
||||||
|
Network NetworkConfig `yaml:"network"`
|
||||||
|
Cluster ClusterConfig `yaml:"cluster"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type RcondConfig struct {
|
type RcondConfig struct {
|
||||||
@@ -16,6 +18,25 @@ type RcondConfig struct {
|
|||||||
ApiToken string `yaml:"api_token"`
|
ApiToken string `yaml:"api_token"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type NetworkConfig struct {
|
||||||
|
Connections []ConnectionConfig `yaml:"connections"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConnectionConfig struct {
|
||||||
|
Type string `yaml:"type,omitempty"`
|
||||||
|
UUID string `yaml:"uuid,omitempty"`
|
||||||
|
ID string `yaml:"id,omitempty"`
|
||||||
|
AutoConnect bool `yaml:"autoconnect,omitempty"`
|
||||||
|
SSID string `yaml:"ssid,omitempty"`
|
||||||
|
Mode string `yaml:"mode,omitempty"`
|
||||||
|
Band string `yaml:"band,omitempty"`
|
||||||
|
Channel uint32 `yaml:"channel,omitempty"`
|
||||||
|
KeyMgmt string `yaml:"keymgmt,omitempty"`
|
||||||
|
PSK string `yaml:"psk,omitempty"`
|
||||||
|
IPv4Method string `yaml:"ipv4method,omitempty"`
|
||||||
|
IPv6Method string `yaml:"ipv6method,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type ClusterConfig struct {
|
type ClusterConfig struct {
|
||||||
Enabled bool `yaml:"enabled"`
|
Enabled bool `yaml:"enabled"`
|
||||||
NodeName string `yaml:"node_name"`
|
NodeName string `yaml:"node_name"`
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ import (
|
|||||||
func WithDbus(fn func(*dbus.Conn) error) error {
|
func WithDbus(fn func(*dbus.Conn) error) error {
|
||||||
conn, err := dbus.SystemBus()
|
conn, err := dbus.SystemBus()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to connect to system bus: %v", err)
|
log.Printf("[ERROR] Failed to connect to system bus: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := fn(conn); err != nil {
|
if err := fn(conn); err != nil {
|
||||||
log.Print(err)
|
log.Printf("[ERROR] Failed to execute D-Bus function: %s", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
conn.Close()
|
conn.Close()
|
||||||
|
|||||||
Reference in New Issue
Block a user