Holy moly a lot was done in this push
This commit is contained in:
parent
9c19c402c0
commit
f9a5542ca2
78
cmd/interfacecli.go
Normal file
78
cmd/interfacecli.go
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"pixelridgesoftworks.com/BackGo/pkg/backup"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InterfaceCLI manages commands related to node interface operations
|
||||||
|
type InterfaceCLI struct {
|
||||||
|
NodeAddress string
|
||||||
|
DBManager *backup.DBManager // Assuming DBManager is part of the backup package
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInterfaceCLI creates a new InterfaceCLI instance
|
||||||
|
func NewInterfaceCLI(nodeAddress string, dbManager *backup.DBManager) *InterfaceCLI {
|
||||||
|
return &InterfaceCLI{
|
||||||
|
NodeAddress: nodeAddress,
|
||||||
|
DBManager: dbManager,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TriggerCommand sends a trigger command to a node
|
||||||
|
func (cli *InterfaceCLI) TriggerCommand(action, value string) error {
|
||||||
|
return cli.sendCommandToNode("trigger", action, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInfo sends a get_info command to a node
|
||||||
|
func (cli *InterfaceCLI) GetInfo(value string) error {
|
||||||
|
return cli.sendCommandToNode("get_info", cli.NodeAddress, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddNode handles adding a new node on the master node
|
||||||
|
func (cli *InterfaceCLI) AddNode(hostname, token string) error {
|
||||||
|
// Verify the hostname resolves to an expected IP before adding
|
||||||
|
// This might involve additional logic to determine what the expected IP is
|
||||||
|
expectedIP := "" // Placeholder for expected IP determination logic
|
||||||
|
if !cli.DBManager.VerifyHostname(hostname, expectedIP) {
|
||||||
|
return fmt.Errorf("hostname %s does not resolve to the expected IP %s", hostname, expectedIP)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a token for the new node
|
||||||
|
if err := cli.DBManager.CreateToken(hostname, token); err != nil {
|
||||||
|
return fmt.Errorf("failed to create token for hostname %s: %v", hostname, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Node %s added successfully with token %s\n", hostname, token)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenCert generates a certificate for a node
|
||||||
|
func (cli *InterfaceCLI) GenCert(nodeName string) error {
|
||||||
|
return cli.sendCommandToNode("gencert", nodeName, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// sendCommandToNode sends a command to the specified node
|
||||||
|
func (cli *InterfaceCLI) sendCommandToNode(action, node, value string) error {
|
||||||
|
payload := backup.CommandPayload{
|
||||||
|
Action: action,
|
||||||
|
Node: node,
|
||||||
|
Value: value,
|
||||||
|
}
|
||||||
|
payloadBytes, err := json.Marshal(payload)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error marshaling command payload: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.Post(fmt.Sprintf("http://%s/api/command", cli.NodeAddress), "application/json", bytes.NewReader(payloadBytes))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error sending command to node %s: %v", node, err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
fmt.Printf("Command '%s' sent to node %s successfully\n", action, node)
|
||||||
|
return nil
|
||||||
|
}
|
116
cmd/root.go
116
cmd/root.go
|
@ -3,9 +3,10 @@ package cmd
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"pixelridgesoftworks.com/BackGo/pkg/backup"
|
||||||
|
"pixelridgesoftworks.com/BackGo/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Command represents a CLI command.
|
|
||||||
type Command struct {
|
type Command struct {
|
||||||
Name string
|
Name string
|
||||||
Description string
|
Description string
|
||||||
|
@ -14,10 +15,75 @@ type Command struct {
|
||||||
Execute func(cmd *Command, args []string)
|
Execute func(cmd *Command, args []string)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RootCmd is now defined at the package level, making it accessible from other packages.
|
var appConfig = config.LoadConfig()
|
||||||
|
var dbManager *backup.DBManager
|
||||||
|
|
||||||
var RootCmd = NewCommand("backup", "Main entry point for backup utility", nil)
|
var RootCmd = NewCommand("backup", "Main entry point for backup utility", nil)
|
||||||
|
|
||||||
// NewCommand creates a new command instance.
|
func init() {
|
||||||
|
// Assuming NewDBManager is correctly implemented in the backup package and accepts *config.Config
|
||||||
|
dbManager = backup.NewDBManager(appConfig)
|
||||||
|
|
||||||
|
remoteCmd := NewCommand("interface", "Remote node management", nil)
|
||||||
|
|
||||||
|
triggerCmd := NewCommand("trigger", "Trigger an action on a node", handleTrigger)
|
||||||
|
getInfoCmd := NewCommand("getinfo", "Get information from a node", handleGetInfo)
|
||||||
|
addNodeCmd := NewCommand("addnode", "Add a new node to the cluster", handleAddNode)
|
||||||
|
genCertCmd := NewCommand("gencert", "Generate a certificate for a node", handleGenCert)
|
||||||
|
|
||||||
|
remoteCmd.AddSubCommand(triggerCmd)
|
||||||
|
remoteCmd.AddSubCommand(getInfoCmd)
|
||||||
|
remoteCmd.AddSubCommand(addNodeCmd)
|
||||||
|
remoteCmd.AddSubCommand(genCertCmd)
|
||||||
|
|
||||||
|
RootCmd.AddSubCommand(remoteCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleTrigger(cmd *Command, args []string) {
|
||||||
|
nodeFlag := cmd.FlagSet.String("node", "", "Specify the node hostname")
|
||||||
|
actionFlag := cmd.FlagSet.String("action", "", "Specify the action to trigger")
|
||||||
|
cmd.FlagSet.Parse(args)
|
||||||
|
|
||||||
|
if *nodeFlag == "" || *actionFlag == "" {
|
||||||
|
fmt.Println("Both node hostname and action are required")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since backup.NewInterfaceCLI is undefined, this part is commented out
|
||||||
|
// Assuming you will implement or correct it later
|
||||||
|
// interfaceCLI := backup.NewInterfaceCLI(*nodeFlag, dbManager)
|
||||||
|
// if err := interfaceCLI.TriggerCommand(*actionFlag, ""); err != nil {
|
||||||
|
// fmt.Printf("Error triggering command on node %s: %v\n", *nodeFlag, err)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// fmt.Printf("Command '%s' triggered successfully on node %s\n", *actionFlag, *nodeFlag)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleGetInfo(cmd *Command, args []string) {
|
||||||
|
// Implementation similar to handleTrigger, tailored for get_info
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleAddNode(cmd *Command, args []string) {
|
||||||
|
nodeName := cmd.FlagSet.String("node", "", "Specify the node name")
|
||||||
|
cmd.FlagSet.Parse(args)
|
||||||
|
|
||||||
|
if *nodeName == "" {
|
||||||
|
fmt.Println("Node name is required")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
token := "some_generated_token" // Replace with actual token generation logic
|
||||||
|
if err := dbManager.CreateToken(*nodeName, token); err != nil {
|
||||||
|
fmt.Printf("Error adding new node %s: %v\n", *nodeName, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Printf("New node %s added successfully with token %s\n", *nodeName, token)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleGenCert(cmd *Command, args []string) {
|
||||||
|
// Implementation for generating a certificate for a node
|
||||||
|
}
|
||||||
|
|
||||||
func NewCommand(name, description string, execute func(cmd *Command, args []string)) *Command {
|
func NewCommand(name, description string, execute func(cmd *Command, args []string)) *Command {
|
||||||
return &Command{
|
return &Command{
|
||||||
Name: name,
|
Name: name,
|
||||||
|
@ -28,18 +94,15 @@ func NewCommand(name, description string, execute func(cmd *Command, args []stri
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddSubCommand adds a subcommand to a command.
|
|
||||||
func (c *Command) AddSubCommand(subCmd *Command) {
|
func (c *Command) AddSubCommand(subCmd *Command) {
|
||||||
c.SubCommands[subCmd.Name] = subCmd
|
c.SubCommands[subCmd.Name] = subCmd
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindSubCommand looks for a subcommand by name.
|
|
||||||
func (c *Command) FindSubCommand(name string) (*Command, bool) {
|
func (c *Command) FindSubCommand(name string) (*Command, bool) {
|
||||||
subCmd, found := c.SubCommands[name]
|
subCmd, found := c.SubCommands[name]
|
||||||
return subCmd, found
|
return subCmd, found
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecuteCommand executes the command with the provided arguments.
|
|
||||||
func ExecuteCommand(rootCmd *Command, args []string) error {
|
func ExecuteCommand(rootCmd *Command, args []string) error {
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
fmt.Println("No command provided")
|
fmt.Println("No command provided")
|
||||||
|
@ -55,58 +118,17 @@ func ExecuteCommand(rootCmd *Command, args []string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look for sub-subcommands if applicable.
|
|
||||||
if len(subArgs) > 0 {
|
if len(subArgs) > 0 {
|
||||||
subCmdName := subArgs[0]
|
subCmdName := subArgs[0]
|
||||||
subCmd, found := cmd.FindSubCommand(subCmdName)
|
subCmd, found := cmd.FindSubCommand(subCmdName)
|
||||||
if found {
|
if found {
|
||||||
// Parse flags for sub-subcommands.
|
|
||||||
subCmd.FlagSet.Parse(subArgs[1:])
|
subCmd.FlagSet.Parse(subArgs[1:])
|
||||||
subCmd.Execute(subCmd, subCmd.FlagSet.Args())
|
subCmd.Execute(subCmd, subCmd.FlagSet.Args())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse flags for direct subcommands.
|
|
||||||
cmd.FlagSet.Parse(subArgs)
|
cmd.FlagSet.Parse(subArgs)
|
||||||
cmd.Execute(cmd, cmd.FlagSet.Args())
|
cmd.Execute(cmd, cmd.FlagSet.Args())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
|
||||||
configCmd := NewCommand("config", "Configuration management", nil)
|
|
||||||
|
|
||||||
editCmd := NewCommand("edit", "Edit a configuration option", func(cmd *Command, args []string) {
|
|
||||||
// Ensure the flag is parsed before trying to access it
|
|
||||||
cmd.FlagSet.Parse(args)
|
|
||||||
configOption := cmd.FlagSet.Lookup("config-option").Value.String()
|
|
||||||
if configOption == "" {
|
|
||||||
fmt.Println("No config option specified")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// The new value should be the first argument after the flags
|
|
||||||
if len(cmd.FlagSet.Args()) == 0 {
|
|
||||||
fmt.Println("No new value specified for the config option")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
newValue := cmd.FlagSet.Arg(0)
|
|
||||||
if err := EditConfigOption(configOption, newValue); err != nil {
|
|
||||||
fmt.Println("Error editing config option:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fmt.Println("Config option updated successfully")
|
|
||||||
})
|
|
||||||
editCmd.FlagSet.String("config-option", "", "The configuration option to edit")
|
|
||||||
|
|
||||||
openCmd := NewCommand("open", "Open the configuration in nano", func(cmd *Command, args []string) {
|
|
||||||
if err := OpenConfigInNano(); err != nil {
|
|
||||||
fmt.Println("Error opening config in nano:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fmt.Println("Configuration opened in nano")
|
|
||||||
})
|
|
||||||
|
|
||||||
configCmd.AddSubCommand(editCmd)
|
|
||||||
configCmd.AddSubCommand(openCmd)
|
|
||||||
RootCmd.AddSubCommand(configCmd)
|
|
||||||
}
|
|
||||||
|
|
|
@ -23,6 +23,11 @@ const (
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
LogLevel LogLevel
|
LogLevel LogLevel
|
||||||
|
MySQLUser string
|
||||||
|
MySQLPassword string
|
||||||
|
MySQLHost string
|
||||||
|
MySQLPort string
|
||||||
|
MySQLDBName string
|
||||||
S3Enabled bool
|
S3Enabled bool
|
||||||
S3ConnectionAddress string
|
S3ConnectionAddress string
|
||||||
S3AuthorizationInfo string
|
S3AuthorizationInfo string
|
||||||
|
@ -47,6 +52,11 @@ type Config struct {
|
||||||
func LoadConfig() *Config {
|
func LoadConfig() *Config {
|
||||||
return &Config{
|
return &Config{
|
||||||
LogLevel: getEnvAsLogLevel("LOG_LEVEL", LogLevelInfo),
|
LogLevel: getEnvAsLogLevel("LOG_LEVEL", LogLevelInfo),
|
||||||
|
MySQLUser: os.Getenv("MYSQL_USER"),
|
||||||
|
MySQLPassword: os.Getenv("MYSQL_PASSWORD"),
|
||||||
|
MySQLHost: os.Getenv("MYSQL_HOST"),
|
||||||
|
MySQLPort: os.Getenv("MYSQL_PORT"),
|
||||||
|
MySQLDBName: os.Getenv("MYSQL_DB_NAME"),
|
||||||
S3Enabled: getEnvAsBool("S3_ENABLED", false),
|
S3Enabled: getEnvAsBool("S3_ENABLED", false),
|
||||||
S3ConnectionAddress: os.Getenv("S3_CONNECTION_ADDRESS"),
|
S3ConnectionAddress: os.Getenv("S3_CONNECTION_ADDRESS"),
|
||||||
S3AuthorizationInfo: os.Getenv("S3_AUTHORIZATION_INFO"),
|
S3AuthorizationInfo: os.Getenv("S3_AUTHORIZATION_INFO"),
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -5,11 +5,13 @@ go 1.22.0
|
||||||
require (
|
require (
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.27.9
|
github.com/aws/aws-sdk-go-v2/config v1.27.9
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.53.0
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.53.0
|
||||||
|
github.com/go-sql-driver/mysql v1.8.1
|
||||||
github.com/hirochachacha/go-smb2 v1.1.0
|
github.com/hirochachacha/go-smb2 v1.1.0
|
||||||
golang.org/x/crypto v0.21.0
|
golang.org/x/crypto v0.21.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
filippo.io/edwards25519 v1.1.0 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2 v1.26.0 // indirect
|
github.com/aws/aws-sdk-go-v2 v1.26.0 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.1 // indirect
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.1 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.9 // indirect
|
github.com/aws/aws-sdk-go-v2/credentials v1.17.9 // indirect
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -1,3 +1,5 @@
|
||||||
|
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||||
|
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||||
github.com/aws/aws-sdk-go-v2 v1.26.0 h1:/Ce4OCiM3EkpW7Y+xUnfAFpchU78K7/Ug01sZni9PgA=
|
github.com/aws/aws-sdk-go-v2 v1.26.0 h1:/Ce4OCiM3EkpW7Y+xUnfAFpchU78K7/Ug01sZni9PgA=
|
||||||
github.com/aws/aws-sdk-go-v2 v1.26.0/go.mod h1:35hUlJVYd+M++iLI3ALmVwMOyRYMmRqUXpTtRGW+K9I=
|
github.com/aws/aws-sdk-go-v2 v1.26.0/go.mod h1:35hUlJVYd+M++iLI3ALmVwMOyRYMmRqUXpTtRGW+K9I=
|
||||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.1 h1:gTK2uhtAPtFcdRRJilZPx8uJLL2J85xK11nKtWL0wfU=
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.1 h1:gTK2uhtAPtFcdRRJilZPx8uJLL2J85xK11nKtWL0wfU=
|
||||||
|
@ -36,6 +38,8 @@ github.com/aws/smithy-go v1.20.1 h1:4SZlSlMr36UEqC7XOyRVb27XMeZubNcBNN+9IgEPIQw=
|
||||||
github.com/aws/smithy-go v1.20.1/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
|
github.com/aws/smithy-go v1.20.1/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
|
||||||
github.com/geoffgarside/ber v1.1.0 h1:qTmFG4jJbwiSzSXoNJeHcOprVzZ8Ulde2Rrrifu5U9w=
|
github.com/geoffgarside/ber v1.1.0 h1:qTmFG4jJbwiSzSXoNJeHcOprVzZ8Ulde2Rrrifu5U9w=
|
||||||
github.com/geoffgarside/ber v1.1.0/go.mod h1:jVPKeCbj6MvQZhwLYsGwaGI52oUorHoHKNecGT85ZCc=
|
github.com/geoffgarside/ber v1.1.0/go.mod h1:jVPKeCbj6MvQZhwLYsGwaGI52oUorHoHKNecGT85ZCc=
|
||||||
|
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||||
|
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||||
github.com/hirochachacha/go-smb2 v1.1.0 h1:b6hs9qKIql9eVXAiN0M2wSFY5xnhbHAQoCwRKbaRTZI=
|
github.com/hirochachacha/go-smb2 v1.1.0 h1:b6hs9qKIql9eVXAiN0M2wSFY5xnhbHAQoCwRKbaRTZI=
|
||||||
github.com/hirochachacha/go-smb2 v1.1.0/go.mod h1:8F1A4d5EZzrGu5R7PU163UcMRDJQl4FtcxjBfsY8TZE=
|
github.com/hirochachacha/go-smb2 v1.1.0/go.mod h1:8F1A4d5EZzrGu5R7PU163UcMRDJQl4FtcxjBfsY8TZE=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
|
76
pkg/backup/database-manager.go
Normal file
76
pkg/backup/database-manager.go
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
package backup
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
_ "github.com/go-sql-driver/mysql"
|
||||||
|
"pixelridgesoftworks.com/BackGo/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DBManager struct {
|
||||||
|
DB *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDBManager creates a new DBManager instance using the provided configuration
|
||||||
|
func NewDBManager(cfg *config.Config) *DBManager {
|
||||||
|
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?parseTime=true",
|
||||||
|
cfg.MySQLUser, cfg.MySQLPassword, cfg.MySQLHost, cfg.MySQLPort, cfg.MySQLDBName)
|
||||||
|
|
||||||
|
db, err := sql.Open("mysql", dsn)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Could not connect to MySQL: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.Ping(); err != nil {
|
||||||
|
log.Fatalf("Could not ping MySQL: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
manager := &DBManager{DB: db}
|
||||||
|
manager.InitDB()
|
||||||
|
|
||||||
|
return manager
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitDB initializes the database tables
|
||||||
|
func (manager *DBManager) InitDB() {
|
||||||
|
// Creating a tokens table
|
||||||
|
createTokensTableSQL := `CREATE TABLE IF NOT EXISTS tokens (
|
||||||
|
token VARCHAR(255) PRIMARY KEY,
|
||||||
|
hostname VARCHAR(255) NOT NULL UNIQUE
|
||||||
|
);`
|
||||||
|
|
||||||
|
if _, err := manager.DB.Exec(createTokensTableSQL); err != nil {
|
||||||
|
log.Fatalf("Failed to create tokens table: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (manager *DBManager) AddToken(token, hostname string) error {
|
||||||
|
_, err := manager.DB.Exec("INSERT INTO tokens (token, hostname) VALUES (?, ?)", token, hostname)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to add token: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (manager *DBManager) VerifyToken(token string) bool {
|
||||||
|
var retrievedToken string
|
||||||
|
err := manager.DB.QueryRow("SELECT token FROM tokens WHERE token = ?", token).Scan(&retrievedToken)
|
||||||
|
if err != nil {
|
||||||
|
if err != sql.ErrNoRows {
|
||||||
|
log.Printf("Failed to verify token: %v", err)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (manager *DBManager) RemoveToken(token string) error {
|
||||||
|
_, err := manager.DB.Exec("DELETE FROM tokens WHERE token = ?", token)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to remove token: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1,13 +1,54 @@
|
||||||
package backup
|
package backup
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"golang.org/x/crypto/acme/autocert"
|
"golang.org/x/crypto/acme/autocert"
|
||||||
|
"io"
|
||||||
|
"bytes"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type CommandPayload struct {
|
||||||
|
Action string `json:"action"`
|
||||||
|
Node string `json:"node"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type JoinRequest struct {
|
||||||
|
NodeHostname string `json:"nodeHostname"`
|
||||||
|
Token string `json:"token"`
|
||||||
|
IP string `json:"ip"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method to create a token
|
||||||
|
func (manager *DBManager) CreateToken(hostname, token string) error {
|
||||||
|
_, err := manager.DB.Exec("INSERT INTO tokens (hostname, token) VALUES (?, ?)", hostname, token)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to insert token: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method to verify if the hostname resolves to the expected IP
|
||||||
|
func (manager *DBManager) VerifyHostname(hostname, expectedIP string) bool {
|
||||||
|
ips, err := net.LookupIP(hostname)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to lookup IP for hostname %s: %v", hostname, err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, ip := range ips {
|
||||||
|
if ip.String() == expectedIP {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// GenerateCertForDomain generates an SSL certificate for a domain using Let's Encrypt
|
// GenerateCertForDomain generates an SSL certificate for a domain using Let's Encrypt
|
||||||
func GenerateCertForDomain(domain string) {
|
func GenerateCertForDomain(domain string) {
|
||||||
certManager := autocert.Manager{
|
certManager := autocert.Manager{
|
||||||
|
@ -30,22 +71,117 @@ func GenerateCertForDomain(domain string) {
|
||||||
log.Fatal(server.ListenAndServeTLS("", "")) // Keys are provided by Let's Encrypt
|
log.Fatal(server.ListenAndServeTLS("", "")) // Keys are provided by Let's Encrypt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var dbManager *DBManager
|
||||||
|
|
||||||
// HandleJoinRequest handles a join request from a node
|
// HandleJoinRequest handles a join request from a node
|
||||||
func HandleJoinRequest(w http.ResponseWriter, r *http.Request) {
|
func HandleJoinRequest(w http.ResponseWriter, r *http.Request) {
|
||||||
// TODO: Implement join request logic here
|
if r.Method != http.MethodPost {
|
||||||
fmt.Fprintf(w, "Join request received\n")
|
http.Error(w, "Only POST method is allowed", http.StatusMethodNotAllowed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var req JoinRequest
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||||
|
http.Error(w, "Error decoding request body", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract the IP address from r.RemoteAddr
|
||||||
|
remoteIP, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Failed to parse remote IP", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the token exists in the database and the hostname resolves to the connecting IP
|
||||||
|
if !dbManager.VerifyToken(req.Token) || !dbManager.VerifyHostname(req.NodeHostname, remoteIP) {
|
||||||
|
http.Error(w, "Invalid token or hostname does not resolve to the connecting IP", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(w, "Join request for %s with token %s accepted\n", req.NodeHostname, req.Token)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendCommandToNode sends a CLI command to a specified node in the cluster
|
// SendCommandToNode sends a CLI command to a specified node in the cluster
|
||||||
func SendCommandToNode(nodeAddress, command string) {
|
func SendCommandToNode(nodeAddress, action, nodeName, value string) {
|
||||||
// TODO: Implement command sending logic here
|
// Construct the command payload
|
||||||
fmt.Printf("Sending command '%s' to node %s\n", command, nodeAddress)
|
payload := CommandPayload{
|
||||||
|
Action: action,
|
||||||
|
Node: nodeName,
|
||||||
|
Value: value,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal the payload into JSON
|
||||||
|
payloadBytes, err := json.Marshal(payload)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error marshaling command payload: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the command to the node
|
||||||
|
resp, err := http.Post(fmt.Sprintf("http://%s/api/command", nodeAddress), "application/json", bytes.NewReader(payloadBytes))
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error sending command to node %s: %v", nodeName, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// Optionally, read the response body
|
||||||
|
// Note: As of Go 1.16, ioutil.ReadAll is deprecated. Use io.ReadAll instead.
|
||||||
|
responseBody, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error reading response from node %s: %v", nodeName, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Command sent to node %s, response: %s\n", nodeName, string(responseBody))
|
||||||
}
|
}
|
||||||
|
|
||||||
// StartMasterServer starts the master API server that listens for join requests and other commands
|
// StartMasterServer starts the master API server that listens for join requests and other commands
|
||||||
func StartMasterServer(domain string) {
|
func StartMasterServer(domain string) {
|
||||||
http.HandleFunc("/join", HandleJoinRequest) // Endpoint for nodes to join the cluster
|
go SetupWebServer() // Start the API server in a separate goroutine
|
||||||
|
|
||||||
// Generate SSL certificate and start HTTPS server
|
// Generate SSL certificate and start HTTPS server
|
||||||
GenerateCertForDomain(domain)
|
GenerateCertForDomain(domain)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetupWebServer initializes and starts the web server for handling API requests.
|
||||||
|
func SetupWebServer() {
|
||||||
|
// Define your HTTP request handlers
|
||||||
|
http.HandleFunc("/api/join", HandleJoinRequest) // Handle join requests
|
||||||
|
http.HandleFunc("/api/command", HandleCommand) // Handle commands sent to nodes
|
||||||
|
|
||||||
|
// More API endpoints can be added here
|
||||||
|
|
||||||
|
fmt.Println("Starting API server on port 8080...")
|
||||||
|
if err := http.ListenAndServe(":8080", nil); err != nil {
|
||||||
|
log.Fatalf("Failed to start API server: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleCommand processes commands sent to nodes.
|
||||||
|
func HandleCommand(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != "POST" {
|
||||||
|
http.Error(w, "Invalid request method", http.StatusMethodNotAllowed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example: Extracting command from the request body
|
||||||
|
// You'll need to define the structure of your request body
|
||||||
|
var req struct {
|
||||||
|
NodeAddress string `json:"nodeAddress"`
|
||||||
|
Command string `json:"command"`
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||||
|
http.Error(w, "Error decoding request body", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the command to the specified node
|
||||||
|
fmt.Printf("Sending command '%s' to node %s\n", req.Command, req.NodeAddress)
|
||||||
|
// Implement the logic to send the command to the node here
|
||||||
|
|
||||||
|
// Respond to the request indicating success
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
fmt.Fprintf(w, "Command sent successfully to node %s\n", req.NodeAddress)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user