mirror of
https://github.com/0x1d/rcond.git
synced 2025-12-14 10:16:50 +01:00
feat: improve ENV variable overrides, introduce Node struct, refactoring
This commit is contained in:
17
README.md
17
README.md
@@ -121,10 +121,19 @@ cluster:
|
||||
|
||||
### Environment Variables
|
||||
|
||||
| Environment Variable | Description | Default |
|
||||
|----------------------|-----------------------------------------|---------------|
|
||||
| RCOND_ADDR | Address to bind the HTTP server to. | 0.0.0.0:8080 |
|
||||
| RCOND_API_TOKEN | API token to use for authentication. | N/A |
|
||||
| Environment Variable | Description | Default |
|
||||
|------------------------------|------------------------------------------|----------------|
|
||||
| HOSTNAME | Hostname to be set at startup. | N/A |
|
||||
| RCOND_ADDR | Address to bind the HTTP server to. | 0.0.0.0:8080 |
|
||||
| RCOND_API_TOKEN | API token to use for authentication. | N/A |
|
||||
| RCOND_CLUSTER_ENABLED | Enable the cluster agent. | false |
|
||||
| RCOND_CLUSTER_NODE_NAME | Name of the node in the cluster. | rcond |
|
||||
| RCOND_CLUSTER_SECRET_KEY | Secret key for the cluster agent. | N/A |
|
||||
| RCOND_CLUSTER_ADVERTISE_ADDR | Advertise address for the cluster agent. | 0.0.0.0 |
|
||||
| RCOND_CLUSTER_ADVERTISE_PORT | Advertise port for the cluster agent. | 7946 |
|
||||
| RCOND_CLUSTER_BIND_ADDR | Bind address for the cluster agent. | 0.0.0.0 |
|
||||
| RCOND_CLUSTER_BIND_PORT | Bind port for the cluster agent. | 7946 |
|
||||
| RCOND_CLUSTER_JOIN | Join addresses for the cluster agent. | 127.0.0.1:7947 |
|
||||
|
||||
## API
|
||||
|
||||
|
||||
@@ -5,15 +5,10 @@ package main
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/0x1d/rcond/pkg/cluster"
|
||||
"github.com/0x1d/rcond/pkg/config"
|
||||
http "github.com/0x1d/rcond/pkg/http"
|
||||
"github.com/0x1d/rcond/pkg/network"
|
||||
"github.com/0x1d/rcond/pkg/system"
|
||||
"github.com/godbus/dbus/v5"
|
||||
"github.com/0x1d/rcond/pkg/rcond"
|
||||
)
|
||||
|
||||
func usage() {
|
||||
@@ -29,9 +24,7 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
configureSystem(appConfig)
|
||||
clusterAgent := startClusterAgent(appConfig)
|
||||
startApiServer(appConfig, clusterAgent)
|
||||
rcond.NewNode(appConfig).Up()
|
||||
|
||||
select {}
|
||||
}
|
||||
@@ -42,8 +35,6 @@ func loadConfig() (*config.Config, error) {
|
||||
help := false
|
||||
|
||||
flag.StringVar(&configPath, "config", configPath, "Path to the configuration file")
|
||||
flag.StringVar(&appConfig.Rcond.Addr, "addr", "", "Address to bind the HTTP server to")
|
||||
flag.StringVar(&appConfig.Rcond.ApiToken, "token", "", "API token to use for authentication")
|
||||
flag.BoolVar(&help, "help", false, "Show help")
|
||||
flag.Parse()
|
||||
|
||||
@@ -61,17 +52,6 @@ func loadConfig() (*config.Config, error) {
|
||||
appConfig = configFile
|
||||
}
|
||||
|
||||
// Override config values from environment variables and flags
|
||||
overrideConfigValuesFromEnv(map[string]*string{
|
||||
"RCOND_ADDR": &appConfig.Rcond.Addr,
|
||||
"RCOND_API_TOKEN": &appConfig.Rcond.ApiToken,
|
||||
})
|
||||
|
||||
overrideConfigValuesFromFlag(map[string]*string{
|
||||
"addr": &appConfig.Rcond.Addr,
|
||||
"token": &appConfig.Rcond.ApiToken,
|
||||
})
|
||||
|
||||
// Validate required fields
|
||||
if err := validateRequiredFields(map[string]*string{
|
||||
"addr": &appConfig.Rcond.Addr,
|
||||
@@ -83,88 +63,6 @@ func loadConfig() (*config.Config, error) {
|
||||
return appConfig, nil
|
||||
}
|
||||
|
||||
func startApiServer(appConfig *config.Config, clusterAgent *cluster.Agent) *http.Server {
|
||||
srv := http.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 startClusterAgent(appConfig *config.Config) *cluster.Agent {
|
||||
clusterConfig := &appConfig.Cluster
|
||||
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.Fatal(err)
|
||||
}
|
||||
// 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
|
||||
}
|
||||
|
||||
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) {
|
||||
for varName, configValue := range envMap {
|
||||
if envValue, ok := os.LookupEnv(varName); ok {
|
||||
*configValue = envValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func overrideConfigValuesFromFlag(flagMap map[string]*string) {
|
||||
for flagName, configValue := range flagMap {
|
||||
if flagValue := flag.Lookup(flagName).Value.String(); flagValue != "" {
|
||||
*configValue = flagValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func validateRequiredFields(fields map[string]*string) error {
|
||||
for name, value := range fields {
|
||||
if *value == "" {
|
||||
|
||||
9
go.mod
9
go.mod
@@ -12,6 +12,7 @@ require (
|
||||
github.com/gorilla/mux v1.8.1
|
||||
github.com/hashicorp/logutils v1.0.0
|
||||
github.com/hashicorp/serf v0.10.2
|
||||
github.com/kelseyhightower/envconfig v1.4.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
golang.org/x/crypto v0.37.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
@@ -29,11 +30,13 @@ require (
|
||||
github.com/hashicorp/go-sockaddr v1.0.5 // indirect
|
||||
github.com/hashicorp/golang-lru v1.0.2 // indirect
|
||||
github.com/hashicorp/memberlist v0.5.2 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/miekg/dns v1.1.56 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
|
||||
golang.org/x/mod v0.13.0 // indirect
|
||||
golang.org/x/net v0.21.0 // indirect
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/net v0.33.0 // indirect
|
||||
golang.org/x/sync v0.13.0 // indirect
|
||||
golang.org/x/sys v0.32.0 // indirect
|
||||
golang.org/x/tools v0.14.0 // indirect
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
||||
)
|
||||
|
||||
28
go.sum
28
go.sum
@@ -13,6 +13,7 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -83,14 +84,18 @@ github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
|
||||
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE=
|
||||
github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY=
|
||||
@@ -102,6 +107,7 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@@ -124,6 +130,8 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT
|
||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
@@ -142,23 +150,23 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||
golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
|
||||
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
|
||||
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -178,8 +186,8 @@ golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
|
||||
golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
|
||||
@@ -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