diff --git a/cmd/configcli.go b/cmd/configcli.go index 5e7ee17..e96c80c 100644 --- a/cmd/configcli.go +++ b/cmd/configcli.go @@ -1,37 +1,45 @@ package cmd import ( + "context" + "reflect" + "strings" + "strconv" "fmt" "os" "os/exec" - "pixelridgesoftworks.com/BackGo/config" - "reflect" - "strconv" - "strings" + "git.pixelridgesoftworks.com/BackGo/config" ) -const configFilePath = "/etc/PixelRidge/BackGo/config.json" - -// OpenConfigInNano opens the configuration file in the nano editor. func OpenConfigInNano() error { - cmd := exec.Command("nano", configFilePath) - cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr - err := cmd.Run() - if err != nil { - return fmt.Errorf("error opening config in nano: %v", err) - } - return nil + configFilePath := config.GetConfigFilePath() + cmd := exec.Command("nano", configFilePath) + cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr + err := cmd.Run() + if err != nil { + return fmt.Errorf("error opening config in nano: %v", err) + } + return nil } -// EditConfigOption allows editing a specific configuration option from the CLI -func EditConfigOption(optionName, newValue string) error { - // Load the current configuration from file +func EditConfigOption(ctx context.Context, optionName, newValue string) error { cfg, err := config.LoadConfigFromFile() if err != nil { return fmt.Errorf("loading config: %v", err) } - // Use reflection to find and set the field in the Config struct + if err := setConfigOption(cfg, optionName, newValue); err != nil { + return err + } + + if err := config.SaveConfigToFile(cfg); err != nil { + return fmt.Errorf("saving config: %v", err) + } + + return nil +} + +func setConfigOption(cfg *config.Config, optionName, newValue string) error { rConfig := reflect.ValueOf(cfg).Elem() field := rConfig.FieldByNameFunc(func(s string) bool { return strings.EqualFold(s, optionName) @@ -41,12 +49,10 @@ func EditConfigOption(optionName, newValue string) error { return fmt.Errorf("unknown configuration option: %s", optionName) } - // Ensure the field can be set if !field.CanSet() { return fmt.Errorf("cannot set configuration option: %s", optionName) } - // Convert and set the field value based on its kind switch field.Kind() { case reflect.String: field.SetString(newValue) @@ -56,21 +62,15 @@ func EditConfigOption(optionName, newValue string) error { return fmt.Errorf("invalid value for %s: %v", optionName, err) } field.SetBool(boolVal) - case reflect.Int: - intVal, err := strconv.Atoi(newValue) + case reflect.Int, reflect.Int64: + intVal, err := strconv.ParseInt(newValue, 10, 64) if err != nil { return fmt.Errorf("invalid value for %s: %v", optionName, err) } - // Additional checks can be implemented here for specific int fields like LogLevel - field.SetInt(int64(intVal)) + field.SetInt(intVal) default: return fmt.Errorf("unsupported configuration type for: %s", optionName) } - // Save the updated configuration back to the file - if err := config.SaveConfigToFile(cfg); err != nil { - return fmt.Errorf("saving config: %v", err) - } - return nil } diff --git a/cmd/interfacecli.go b/cmd/interfacecli.go index 8c43a26..fb26f63 100644 --- a/cmd/interfacecli.go +++ b/cmd/interfacecli.go @@ -5,43 +5,38 @@ import ( "encoding/json" "fmt" "net/http" - "pixelridgesoftworks.com/BackGo/pkg/backup" + "git.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 + DBManager *backup.DBManager } -// NewInterfaceCLI creates a new InterfaceCLI instance func NewInterfaceCLI(nodeAddress string, dbManager *backup.DBManager) *InterfaceCLI { + if dbManager == nil { + panic("DBManager cannot be nil") + } 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) + return cli.sendCommandToNode("trigger", 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) + return cli.sendCommandToNode("get_info", 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 + expectedIP := "" // Implement IP determination logic here 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) } @@ -50,16 +45,14 @@ func (cli *InterfaceCLI) AddNode(hostname, token string) error { return nil } -// GenCert generates a certificate for a node func (cli *InterfaceCLI) GenCert(nodeName string) error { - return cli.sendCommandToNode("gencert", nodeName, "") + return cli.sendCommandToNode("gencert", nodeName) } -// sendCommandToNode sends a command to the specified node -func (cli *InterfaceCLI) sendCommandToNode(action, node, value string) error { +func (cli *InterfaceCLI) sendCommandToNode(action, value string) error { payload := backup.CommandPayload{ Action: action, - Node: node, + Node: cli.NodeAddress, Value: value, } payloadBytes, err := json.Marshal(payload) @@ -69,10 +62,10 @@ func (cli *InterfaceCLI) sendCommandToNode(action, node, value string) error { 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) + return fmt.Errorf("error sending command to node %s: %v", cli.NodeAddress, err) } defer resp.Body.Close() - fmt.Printf("Command '%s' sent to node %s successfully\n", action, node) + fmt.Printf("Command '%s' sent to node %s successfully\n", action, cli.NodeAddress) return nil } diff --git a/cmd/root.go b/cmd/root.go index 95716e0..b2b2d4a 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -1,10 +1,11 @@ package cmd import ( + "context" "flag" "fmt" - "pixelridgesoftworks.com/BackGo/pkg/backup" - "pixelridgesoftworks.com/BackGo/config" + "git.pixelridgesoftworks.com/BackGo/pkg/backup" + "git.pixelridgesoftworks.com/BackGo/pkg/logger" ) type Command struct { @@ -12,18 +13,12 @@ type Command struct { Description string FlagSet *flag.FlagSet SubCommands map[string]*Command - Execute func(cmd *Command, args []string) + Execute func(ctx context.Context, cmd *Command, args []string) } -var appConfig = config.LoadConfig() -var dbManager *backup.DBManager - var RootCmd = NewCommand("backup", "Main entry point for backup utility", nil) 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) @@ -39,31 +34,48 @@ func init() { RootCmd.AddSubCommand(remoteCmd) } -func handleTrigger(cmd *Command, args []string) { +func handleTrigger(ctx context.Context, cmd *Command, args []string) { + log := ctx.Value("logger").(*logger.Logger) + dbManager := ctx.Value("dbManager").(*backup.DBManager) 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") + log.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) + cli := NewInterfaceCLI(*nodeFlag, dbManager) + if err := cli.TriggerCommand(*actionFlag, ""); err != nil { + log.Printf("Error triggering command on node %s: %v\n", *nodeFlag, err) + } else { + log.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 handleGetInfo(ctx context.Context, cmd *Command, args []string) { + log := ctx.Value("logger").(*logger.Logger) + nodeFlag := cmd.FlagSet.String("node", "", "Specify the node address") + valueFlag := cmd.FlagSet.String("value", "", "Specify the value for get_info") + cmd.FlagSet.Parse(args) + + if *nodeFlag == "" { + log.Println("Node address is required") + return + } + + dbManager := ctx.Value("dbManager").(*backup.DBManager) + cli := NewInterfaceCLI(*nodeFlag, dbManager) + if err := cli.GetInfo(*valueFlag); err != nil { + log.Printf("Error getting info from node %s: %v\n", *nodeFlag, err) + } else { + log.Printf("Successfully got info from node %s\n", *nodeFlag) + } } -func handleAddNode(cmd *Command, args []string) { +func handleAddNode(ctx context.Context, cmd *Command, args []string) { + dbManager := ctx.Value("dbManager").(*backup.DBManager) nodeName := cmd.FlagSet.String("node", "", "Specify the node name") cmd.FlagSet.Parse(args) @@ -72,19 +84,34 @@ func handleAddNode(cmd *Command, args []string) { return } - token := "some_generated_token" // Replace with actual token generation logic + token := "some_generated_token" if err := dbManager.CreateToken(*nodeName, token); err != nil { fmt.Printf("Error adding new node %s: %v\n", *nodeName, err) + } else { + fmt.Printf("New node %s added successfully with token %s\n", *nodeName, token) + } +} + +func handleGenCert(ctx context.Context, cmd *Command, args []string) { + log := ctx.Value("logger").(*logger.Logger) + dbManager := ctx.Value("dbManager").(*backup.DBManager) + nodeNameFlag := cmd.FlagSet.String("node", "", "Specify the node name for which to generate a certificate") + cmd.FlagSet.Parse(args) + + if *nodeNameFlag == "" { + log.Println("Node name is required") return } - fmt.Printf("New node %s added successfully with token %s\n", *nodeName, token) + + cli := NewInterfaceCLI(*nodeNameFlag, dbManager) + if err := cli.GenCert(*nodeNameFlag); err != nil { + log.Printf("Error generating certificate for node %s: %v\n", *nodeNameFlag, err) + } else { + log.Printf("Certificate generated successfully for node %s\n", *nodeNameFlag) + } } -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(ctx context.Context, cmd *Command, args []string)) *Command { return &Command{ Name: name, Description: description, @@ -103,7 +130,7 @@ func (c *Command) FindSubCommand(name string) (*Command, bool) { return subCmd, found } -func ExecuteCommand(rootCmd *Command, args []string) error { +func ExecuteCommandWithContext(ctx context.Context, rootCmd *Command, args []string) error { if len(args) < 1 { fmt.Println("No command provided") return nil @@ -118,17 +145,11 @@ func ExecuteCommand(rootCmd *Command, args []string) error { return nil } - if len(subArgs) > 0 { - subCmdName := subArgs[0] - subCmd, found := cmd.FindSubCommand(subCmdName) - if found { - subCmd.FlagSet.Parse(subArgs[1:]) - subCmd.Execute(subCmd, subCmd.FlagSet.Args()) - return nil - } + if cmd.Execute != nil { + cmd.Execute(ctx, cmd, subArgs) + } else { + fmt.Println("No execute function provided for command:", cmdName) } - cmd.FlagSet.Parse(subArgs) - cmd.Execute(cmd, cmd.FlagSet.Args()) return nil } diff --git a/config/config.go b/config/config.go index 6d3fe50..4386c96 100644 --- a/config/config.go +++ b/config/config.go @@ -185,3 +185,8 @@ func SaveConfigToFile(config *Config) error { func ReloadConfig() { fmt.Println("Configuration reloaded") } + +// GetConfigFilePath returns the path to the configuration file. +func GetConfigFilePath() string { + return configFilePath +} diff --git a/go.mod b/go.mod index 956a87b..7157307 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,10 @@ -module pixelridgesoftworks.com/BackGo +module git.pixelridgesoftworks.com/BackGo go 1.22.0 require ( github.com/aws/aws-sdk-go-v2/config v1.27.9 + github.com/aws/aws-sdk-go-v2/credentials v1.17.9 github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.13 github.com/aws/aws-sdk-go-v2/service/s3 v1.53.0 github.com/go-sql-driver/mysql v1.8.1 @@ -15,7 +16,6 @@ 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/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/feature/ec2/imds v1.16.0 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.4 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.4 // indirect diff --git a/main.go b/main.go index 606d6a7..5752240 100644 --- a/main.go +++ b/main.go @@ -3,13 +3,59 @@ package main import ( "fmt" "os" - "pixelridgesoftworks.com/BackGo/cmd" + "context" + "git.pixelridgesoftworks.com/BackGo/cmd" + backGoConfig "git.pixelridgesoftworks.com/BackGo/config" + "git.pixelridgesoftworks.com/BackGo/pkg/backup" + "git.pixelridgesoftworks.com/BackGo/pkg/logger" ) func main() { - // Correctly pass RootCmd as it's already a *Command - if err := cmd.ExecuteCommand(cmd.RootCmd, os.Args[1:]); err != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) + // Load configuration + cfg := backGoConfig.LoadConfig() + + // Initialize Logger + log := logger.NewLogger() + + // Set the logger level based on the configuration + log.SetLevel(mapConfigLogLevelToLoggerLogLevel(cfg.LogLevel)) + + // Adjust logger output based on configuration or environment variables + logFileEnv := os.Getenv("LOG_FILE") + if logFileEnv != "" { + if err := log.LogToFile(logFileEnv); err != nil { + fmt.Fprintf(os.Stderr, "Failed to set logger output to file: %v\n", err) + os.Exit(1) + } + } + + // Initialize the database manager + dbManager := backup.NewDBManager(cfg) + + // Create a context + ctx := context.Background() + + // Attach the logger and DB manager to the context + ctx = context.WithValue(ctx, "logger", log) + ctx = context.WithValue(ctx, "dbManager", dbManager) + + // Now, pass only the context (and any other necessary parameters) to your command execution + // Note: You'll need to adjust your application's command handling to work with context + if err := cmd.ExecuteCommandWithContext(ctx, cmd.RootCmd, os.Args[1:]); err != nil { + log.Error(fmt.Sprintf("Command execution failed: %v", err)) os.Exit(1) } } + +func mapConfigLogLevelToLoggerLogLevel(lvl backGoConfig.LogLevel) logger.LogLevel { + switch lvl { + case backGoConfig.LogLevelInfo: + return logger.LogLevelInfo + case backGoConfig.LogLevelWarning: + return logger.LogLevelWarning + case backGoConfig.LogLevelError: + return logger.LogLevelError + default: + return logger.LogLevelInfo // Default to Info if unsure + } +} diff --git a/pkg/backup/database-manager.go b/pkg/backup/database-manager.go index 51b78c1..8ce86c1 100644 --- a/pkg/backup/database-manager.go +++ b/pkg/backup/database-manager.go @@ -5,7 +5,7 @@ import ( "fmt" "log" _ "github.com/go-sql-driver/mysql" - "pixelridgesoftworks.com/BackGo/config" + "git.pixelridgesoftworks.com/BackGo/config" ) type DBManager struct { diff --git a/pkg/backup/s3.go b/pkg/backup/s3.go index 7d12f72..368186e 100644 --- a/pkg/backup/s3.go +++ b/pkg/backup/s3.go @@ -9,7 +9,7 @@ import ( awsConfig "github.com/aws/aws-sdk-go-v2/config" // Aliased AWS config package "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/aws/aws-sdk-go-v2/feature/s3/manager" - backGoConfig "pixelridgesoftworks.com/BackGo/config" // Aliased your config package + backGoConfig "git.pixelridgesoftworks.com/BackGo/config" // Aliased your config package ) // S3Client wraps the AWS S3 client diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go index dea91c7..85b48e2 100644 --- a/pkg/logger/logger.go +++ b/pkg/logger/logger.go @@ -6,18 +6,36 @@ import ( "os" ) -// Logger holds the log package's Logger instance. +// LogLevel type for defining different logging levels. +type LogLevel int + +const ( + // Define different log levels. + LogLevelInfo LogLevel = iota + LogLevelWarning + LogLevelError +) + +// Logger wraps the standard log.Logger from the Go standard library. +// It adds additional functionality like setting log levels. type Logger struct { *log.Logger + level LogLevel } -// NewLogger creates a new Logger instance. By default, it logs to stdout. +// NewLogger creates a new Logger instance. By default, it logs to stdout with LogLevelInfo. func NewLogger() *Logger { return &Logger{ Logger: log.New(os.Stdout, "", log.LstdFlags), + level: LogLevelInfo, } } +// SetLevel changes the logging level of the Logger. +func (l *Logger) SetLevel(level LogLevel) { + l.level = level +} + // LogToFile configures the logger to log to a specified file. func (l *Logger) LogToFile(filePath string) error { file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) @@ -43,3 +61,24 @@ func (l *Logger) LogToBoth(filePath string) error { l.SetOutput(multi) return nil } + +// Info logs a message at level Info, respecting the set log level. +func (l *Logger) Info(msg string) { + if l.level <= LogLevelInfo { + l.Println("INFO: " + msg) + } +} + +// Warning logs a message at level Warning, respecting the set log level. +func (l *Logger) Warning(msg string) { + if l.level <= LogLevelWarning { + l.Println("WARNING: " + msg) + } +} + +// Error logs a message at level Error, respecting the set log level. +func (l *Logger) Error(msg string) { + if l.level <= LogLevelError { + l.Println("ERROR: " + msg) + } +}