mirror of
https://github.com/0x1d/rcond.git
synced 2025-12-14 18:25:21 +01:00
feat: introduce cluster agent
This commit is contained in:
55
pkg/cluster/agent.go
Normal file
55
pkg/cluster/agent.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package cluster
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/0x1d/rcond/pkg/config"
|
||||
"github.com/hashicorp/serf/serf"
|
||||
)
|
||||
|
||||
type Agent struct {
|
||||
Serf *serf.Serf
|
||||
}
|
||||
|
||||
func NewAgent(clusterConfig *config.ClusterConfig) (*Agent, error) {
|
||||
config := serf.DefaultConfig()
|
||||
config.NodeName = clusterConfig.NodeName
|
||||
config.ProtocolVersion = serf.ProtocolVersionMax
|
||||
config.MemberlistConfig.SecretKey = []byte(clusterConfig.SecretKey)
|
||||
config.MemberlistConfig.AdvertiseAddr = clusterConfig.AdvertiseAddr
|
||||
config.MemberlistConfig.AdvertisePort = clusterConfig.AdvertisePort
|
||||
config.MemberlistConfig.BindAddr = clusterConfig.BindAddr
|
||||
config.MemberlistConfig.BindPort = clusterConfig.BindPort
|
||||
|
||||
serf, err := serf.Create(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Agent{Serf: serf}, nil
|
||||
}
|
||||
|
||||
func (a *Agent) Members() ([]serf.Member, error) {
|
||||
log.Printf("Getting members of the cluster")
|
||||
return a.Serf.Members(), nil
|
||||
}
|
||||
|
||||
func (a *Agent) Join(addrs []string, ignoreOld bool) (int, error) {
|
||||
log.Printf("Joining nodes in the cluster: %v", addrs)
|
||||
n, err := a.Serf.Join(addrs, ignoreOld)
|
||||
if err != nil {
|
||||
log.Printf("Failed to join nodes in the cluster: %v", err)
|
||||
return 0, err
|
||||
}
|
||||
log.Printf("Joined %d nodes in the cluster", n)
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (a *Agent) Leave() error {
|
||||
return a.Serf.Leave()
|
||||
}
|
||||
|
||||
func (a *Agent) Shutdown() error {
|
||||
log.Printf("Shutting down cluster agent")
|
||||
return a.Serf.Shutdown()
|
||||
}
|
||||
@@ -7,7 +7,8 @@ import (
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Rcond RcondConfig `yaml:"rcond"`
|
||||
Rcond RcondConfig `yaml:"rcond"`
|
||||
Cluster ClusterConfig `yaml:"cluster"`
|
||||
}
|
||||
|
||||
type RcondConfig struct {
|
||||
@@ -15,6 +16,17 @@ type RcondConfig struct {
|
||||
ApiToken string `yaml:"api_token"`
|
||||
}
|
||||
|
||||
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"`
|
||||
}
|
||||
|
||||
func LoadConfig(path string) (*Config, error) {
|
||||
yamlFile, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/0x1d/rcond/pkg/cluster"
|
||||
network "github.com/0x1d/rcond/pkg/network"
|
||||
"github.com/0x1d/rcond/pkg/system"
|
||||
"github.com/0x1d/rcond/pkg/user"
|
||||
@@ -287,3 +288,24 @@ func HandleShutdown(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]string{"status": "success"})
|
||||
}
|
||||
|
||||
func ClusterAgentWrapper(agent *cluster.Agent) func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
HandleClusterMembers(w, r, agent)
|
||||
}
|
||||
}
|
||||
|
||||
func HandleClusterMembers(w http.ResponseWriter, r *http.Request, agent *cluster.Agent) {
|
||||
if agent == nil {
|
||||
writeError(w, "cluster agent is not initialized", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
members, err := agent.Members()
|
||||
if err != nil {
|
||||
writeError(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(members)
|
||||
}
|
||||
|
||||
@@ -6,14 +6,16 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/0x1d/rcond/pkg/cluster"
|
||||
"github.com/0x1d/rcond/pkg/config"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
router *mux.Router
|
||||
srv *http.Server
|
||||
apiToken string
|
||||
router *mux.Router
|
||||
srv *http.Server
|
||||
apiToken string
|
||||
clusterAgent *cluster.Agent
|
||||
}
|
||||
|
||||
func NewServer(cfg *config.Config) *Server {
|
||||
@@ -37,6 +39,11 @@ func NewServer(cfg *config.Config) *Server {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) WithClusterAgent(agent *cluster.Agent) *Server {
|
||||
s.clusterAgent = agent
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Server) Start() error {
|
||||
return s.srv.ListenAndServe()
|
||||
}
|
||||
@@ -69,6 +76,7 @@ func (s *Server) RegisterRoutes() {
|
||||
s.router.HandleFunc("/users/{user}/keys/{fingerprint}", s.verifyToken(HandleRemoveAuthorizedKey)).Methods(http.MethodDelete)
|
||||
s.router.HandleFunc("/system/restart", s.verifyToken(HandleReboot)).Methods(http.MethodPost)
|
||||
s.router.HandleFunc("/system/shutdown", s.verifyToken(HandleShutdown)).Methods(http.MethodPost)
|
||||
s.router.HandleFunc("/cluster/members", s.verifyToken(ClusterAgentWrapper(s.clusterAgent))).Methods(http.MethodGet)
|
||||
}
|
||||
|
||||
func (s *Server) healthHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
Reference in New Issue
Block a user