diff --git a/cmd/vcluster/main.go b/cmd/vcluster/main.go index aacffef..a0ebeb9 100644 --- a/cmd/vcluster/main.go +++ b/cmd/vcluster/main.go @@ -26,8 +26,8 @@ func main() { // use fmt for print info in this function, because the step of // setting up logs could error out fmt.Println("---{vcluster begin}---") - launcher := commands.MakeClusterCommandLauncher() - runError := launcher.Run(os.Args) + launcher, vcc := commands.MakeClusterCommandLauncher() + runError := launcher.Run(os.Args, vcc) if runError != nil { fmt.Printf("Error during execution: %s\n", runError) os.Exit(1) diff --git a/commands/cluster_command.go b/commands/cluster_command.go index f44d518..c312e63 100644 --- a/commands/cluster_command.go +++ b/commands/cluster_command.go @@ -15,18 +15,21 @@ package commands -import "github.com/vertica/vcluster/vclusterops/vlog" +import ( + "github.com/vertica/vcluster/vclusterops" + "github.com/vertica/vcluster/vclusterops/vlog" +) type ClusterCommand interface { CommandType() string - Parse(argv []string) error + Parse(argv []string, log vlog.Printer) error /* TODO: Analyze information about the state of * the cluster. The information could be * cached in a config file or constructed through * cluster discovery. */ - Analyze() error - Run(log vlog.Printer) error + Analyze(log vlog.Printer) error + Run(vcc vclusterops.VClusterCommands) error PrintUsage(string) } diff --git a/commands/cluster_command_launcher.go b/commands/cluster_command_launcher.go index fc55ed2..20d132d 100644 --- a/commands/cluster_command_launcher.go +++ b/commands/cluster_command_launcher.go @@ -19,6 +19,7 @@ import ( "fmt" "os" + "github.com/vertica/vcluster/vclusterops" "github.com/vertica/vcluster/vclusterops/vlog" ) @@ -52,38 +53,43 @@ type ClusterCommandLauncher struct { */ const minArgs = 2 const helpString = "help" +const defaultLogPath = "/opt/vertica/log/vcluster.log" /* ClusterCommandLauncherFactory() * Returns a new instance of a ClusterCommandLauncher * with some reasonable defaults. */ -func MakeClusterCommandLauncher() ClusterCommandLauncher { +func MakeClusterCommandLauncher() (ClusterCommandLauncher, vclusterops.VClusterCommands) { // setup logs for command launcher initialization - logPath := vlog.ParseLogPathArg(os.Args, vlog.DefaultLogPath) - vlog.SetupOrDie(logPath) - vlog.LogInfoln("New vcluster command initialization") + userCommandString := os.Args[1] + log := vlog.Printer{} + logPath := parseLogPathArg(os.Args, defaultLogPath) + log.SetupOrDie(logPath) + vcc := vclusterops.VClusterCommands{ + Log: log.WithName(userCommandString), + } + vcc.Log.Info("New vcluster command initialization") newLauncher := ClusterCommandLauncher{} - - allCommands := constructCmds() + allCommands := constructCmds(vcc.Log) newLauncher.commands = map[string]ClusterCommand{} for _, c := range allCommands { _, existsInMap := newLauncher.commands[c.CommandType()] if existsInMap { // shout loud if there's a programmer error - vlog.LogPrintError("Programmer Error: tried add command %s to the commands index twice. Check cluster_command_launcher.go", + vcc.Log.PrintError("Programmer Error: tried to add command %s to the commands index twice. Check cluster_command_launcher.go", c.CommandType()) os.Exit(1) } newLauncher.commands[c.CommandType()] = c } - return newLauncher + return newLauncher, vcc } // constructCmds returns a list of commands that will be executed // by the cluster command launcher. -func constructCmds() []ClusterCommand { +func constructCmds(_ vlog.Printer) []ClusterCommand { return []ClusterCommand{ // db-scope cmds makeCmdCreateDB(), @@ -118,24 +124,26 @@ func constructCmds() []ClusterCommand { * + Calls Run() for the sub-command * + Returns any errors to the caller after writing the error to the log */ -func (c ClusterCommandLauncher) Run(inputArgv []string) error { +func (c ClusterCommandLauncher) Run(inputArgv []string, vcc vclusterops.VClusterCommands) error { + userCommandString := os.Args[1] c.argv = inputArgv minArgsError := checkMinimumInput(c.argv) if minArgsError != nil { - vlog.LogError(minArgsError.Error()) + vcc.Log.Error(minArgsError, "fail to check minimum argument") return minArgsError } - subCommand, idError := identifySubcommand(c.commands) + subCommand, idError := identifySubcommand(c.commands, userCommandString, vcc.Log) + if idError != nil { - vlog.LogError(idError.Error()) + vcc.Log.Error(idError, "fail to recognize command") return idError } - parseError := subCommand.Parse(inputArgv[2:]) + parseError := subCommand.Parse(inputArgv[2:], vcc.Log) if parseError != nil { - vlog.LogError(parseError.Error()) + vcc.Log.Error(parseError, "fail to parse command") return parseError } @@ -152,31 +160,29 @@ func (c ClusterCommandLauncher) Run(inputArgv []string) error { /* TODO: this is where we would read a * configuration file. Not currently implemented. */ - analyzeError := subCommand.Analyze() + analyzeError := subCommand.Analyze(vcc.Log) if analyzeError != nil { - vlog.LogError(analyzeError.Error()) + vcc.Log.Error(analyzeError, "fail to analyze command") return analyzeError } - log := vlog.GetGlobalLogger().Printer - runError := subCommand.Run(log) + runError := subCommand.Run(vcc) if runError != nil { - vlog.LogError(runError.Error()) + vcc.Log.Error(runError, "fail to run command") } return runError } -func identifySubcommand(commands map[string]ClusterCommand) (ClusterCommand, error) { - userCommandString := os.Args[1] +func identifySubcommand(commands map[string]ClusterCommand, userCommandString string, + log vlog.Printer) (ClusterCommand, error) { command, ok := commands[userCommandString] - if !ok { return nil, fmt.Errorf("unrecognized command '%s'", userCommandString) } - vlog.LogInfo("Recognized command: %s\n", userCommandString) + log.Log.Info("Recognized command", "cmd", userCommandString) return command, nil } @@ -188,3 +194,12 @@ func checkMinimumInput(inputArgv []string) error { minArgs, len(inputArgv)) } + +func parseLogPathArg(argInput []string, defaultPath string) (logPath string) { + for idx, arg := range argInput { + if arg == "--log-path" { + return argInput[idx+1] + } + } + return defaultPath +} diff --git a/commands/cmd_add_node.go b/commands/cmd_add_node.go index 0505b92..9274a3e 100644 --- a/commands/cmd_add_node.go +++ b/commands/cmd_add_node.go @@ -85,9 +85,9 @@ func (c *CmdAddNode) CommandType() string { return "db_add_node" } -func (c *CmdAddNode) Parse(inputArgv []string) error { +func (c *CmdAddNode) Parse(inputArgv []string, log vlog.Printer) error { c.argv = inputArgv - err := c.ValidateParseArgv(c.CommandType()) + err := c.ValidateParseArgv(c.CommandType(), log) if err != nil { return err } @@ -105,11 +105,11 @@ func (c *CmdAddNode) Parse(inputArgv []string) error { if !util.IsOptionSet(c.parser, "eon-mode") { c.CmdBase.isEon = nil } - return c.validateParse() + return c.validateParse(log) } -func (c *CmdAddNode) validateParse() error { - vlog.LogInfoln("Called validateParse()") +func (c *CmdAddNode) validateParse(log vlog.Printer) error { + log.Info("Called validateParse()") err := c.parseNewHostList() if err != nil { @@ -150,20 +150,17 @@ func (c *CmdAddNode) parseNodeNameList() error { return nil } -func (c *CmdAddNode) Analyze() error { +func (c *CmdAddNode) Analyze(_ vlog.Printer) error { return nil } -func (c *CmdAddNode) Run(log vlog.Printer) error { - vcc := vclusterops.VClusterCommands{ - Log: log.WithName(c.CommandType()), - } +func (c *CmdAddNode) Run(vcc vclusterops.VClusterCommands) error { vcc.Log.V(1).Info("Called method Run()") options := c.addNodeOptions // get config from vertica_cluster.yaml - config, err := options.GetDBConfig() + config, err := options.GetDBConfig(vcc) if err != nil { return err } @@ -174,9 +171,9 @@ func (c *CmdAddNode) Run(log vlog.Printer) error { return addNodeError } // write cluster information to the YAML config file - err = vdb.WriteClusterConfig(options.ConfigDirectory) + err = vdb.WriteClusterConfig(options.ConfigDirectory, vcc.Log) if err != nil { - vlog.LogPrintWarning("fail to write config file, details: %s", err) + vcc.Log.PrintWarning("fail to write config file, details: %s", err) } vcc.Log.PrintInfo("Added nodes %s to database %s", *c.newHostListStr, *options.DBName) return nil diff --git a/commands/cmd_add_subcluster.go b/commands/cmd_add_subcluster.go index 7b4a9ec..f656517 100644 --- a/commands/cmd_add_subcluster.go +++ b/commands/cmd_add_subcluster.go @@ -87,9 +87,9 @@ func (c *CmdAddSubcluster) CommandType() string { return "db_add_subcluster" } -func (c *CmdAddSubcluster) Parse(inputArgv []string) error { +func (c *CmdAddSubcluster) Parse(inputArgv []string, log vlog.Printer) error { c.argv = inputArgv - err := c.ValidateParseArgv(c.CommandType()) + err := c.ValidateParseArgv(c.CommandType(), log) if err != nil { return err } @@ -110,30 +110,27 @@ func (c *CmdAddSubcluster) Parse(inputArgv []string) error { c.addSubclusterOptions.ConfigDirectory = nil } - return c.validateParse() + return c.validateParse(log) } // all validations of the arguments should go in here -func (c *CmdAddSubcluster) validateParse() error { - vlog.LogInfoln("Called validateParse()") +func (c *CmdAddSubcluster) validateParse(log vlog.Printer) error { + log.Info("Called validateParse()") return c.ValidateParseBaseOptions(&c.addSubclusterOptions.DatabaseOptions) } -func (c *CmdAddSubcluster) Analyze() error { - vlog.LogInfoln("Called method Analyze()") +func (c *CmdAddSubcluster) Analyze(log vlog.Printer) error { + log.Info("Called method Analyze()") return nil } -func (c *CmdAddSubcluster) Run(log vlog.Printer) error { - vcc := vclusterops.VClusterCommands{ - Log: log.WithName(c.CommandType()), - } +func (c *CmdAddSubcluster) Run(vcc vclusterops.VClusterCommands) error { vcc.Log.V(1).Info("Called method Run()") options := c.addSubclusterOptions // get config from vertica_cluster.yaml - config, err := options.GetDBConfig() + config, err := options.GetDBConfig(vcc) if err != nil { return err } @@ -145,6 +142,6 @@ func (c *CmdAddSubcluster) Run(log vlog.Printer) error { return err } - vlog.LogPrintInfo("Added subcluster %s to database %s", *options.SCName, *options.DBName) + vcc.Log.PrintInfo("Added subcluster %s to database %s", *options.SCName, *options.DBName) return nil } diff --git a/commands/cmd_base.go b/commands/cmd_base.go index d217483..024d057 100644 --- a/commands/cmd_base.go +++ b/commands/cmd_base.go @@ -55,15 +55,15 @@ func (c *CmdBase) ParseArgv() error { } // validate and parse argv -func (c *CmdBase) ValidateParseArgv(commandType string) error { - vlog.LogArgParse(&c.argv) +func (c *CmdBase) ValidateParseArgv(commandType string, log vlog.Printer) error { + log.LogArgParse(&c.argv) return c.ValidateParseArgvHelper(commandType) } // validate and parse masked argv // Some database actions, such as createDB and reviveDB, need to mask sensitive parameters in the log -func (c *CmdBase) ValidateParseMaskedArgv(commandType string) error { - vlog.LogMaskedArgParse(c.argv) +func (c *CmdBase) ValidateParseMaskedArgv(commandType string, log vlog.Printer) error { + log.LogMaskedArgParse(c.argv) return c.ValidateParseArgvHelper(commandType) } diff --git a/commands/cmd_config.go b/commands/cmd_config.go index 607d947..38898e6 100644 --- a/commands/cmd_config.go +++ b/commands/cmd_config.go @@ -55,8 +55,8 @@ func (c *CmdConfig) CommandType() string { return "config" } -func (c *CmdConfig) Parse(inputArgv []string) error { - vlog.LogArgParse(&inputArgv) +func (c *CmdConfig) Parse(inputArgv []string, log vlog.Printer) error { + log.LogArgParse(&inputArgv) if c.parser == nil { return fmt.Errorf("unexpected nil - the parser was nil") @@ -68,28 +68,27 @@ func (c *CmdConfig) Parse(inputArgv []string) error { return err } - return c.validateParse() + return c.validateParse(log) } -func (c *CmdConfig) validateParse() error { - vlog.LogInfoln("Called validateParse()") - +func (c *CmdConfig) validateParse(log vlog.Printer) error { + log.Info("Called validateParse()") // if directory is not provided, then use the current directory return c.validateDirectory() } -func (c *CmdConfig) Analyze() error { +func (c *CmdConfig) Analyze(_ vlog.Printer) error { return nil } -func (c *CmdConfig) Run(_ vlog.Printer) error { +func (c *CmdConfig) Run(vcc vclusterops.VClusterCommands) error { if *c.show { configFilePath := filepath.Join(*c.directory, vclusterops.ConfigFileName) fileBytes, err := os.ReadFile(configFilePath) if err != nil { return fmt.Errorf("fail to read config file, details: %w", err) } - vlog.LogPrintInfo("Content of the config file:\n%s", string(fileBytes)) + vcc.Log.PrintInfo("Content of the config file:\n%s", string(fileBytes)) } return nil diff --git a/commands/cmd_create_db.go b/commands/cmd_create_db.go index e64cd02..fe470dd 100644 --- a/commands/cmd_create_db.go +++ b/commands/cmd_create_db.go @@ -115,9 +115,9 @@ func (c *CmdCreateDB) CommandType() string { return "create_db" } -func (c *CmdCreateDB) Parse(inputArgv []string) error { +func (c *CmdCreateDB) Parse(inputArgv []string, log vlog.Printer) error { c.argv = inputArgv - err := c.ValidateParseMaskedArgv(c.CommandType()) + err := c.ValidateParseMaskedArgv(c.CommandType(), log) if err != nil { return err } @@ -139,12 +139,12 @@ func (c *CmdCreateDB) Parse(inputArgv []string) error { c.createDBOptions.IsEon = vstruct.False } - return c.validateParse() + return c.validateParse(log) } // all validations of the arguments should go in here -func (c *CmdCreateDB) validateParse() error { - vlog.LogInfoln("Called validateParse()") +func (c *CmdCreateDB) validateParse(log vlog.Printer) error { + log.Info("Called validateParse()") // parse raw host str input into a []string of createDBOptions err := c.createDBOptions.ParseHostList(*c.hostListStr) @@ -166,25 +166,22 @@ func (c *CmdCreateDB) validateParse() error { return nil } -func (c *CmdCreateDB) Analyze() error { - vlog.LogInfoln("Called method Analyze()") +func (c *CmdCreateDB) Analyze(log vlog.Printer) error { + log.Info("Called method Analyze()") return nil } -func (c *CmdCreateDB) Run(log vlog.Printer) error { - vcc := vclusterops.VClusterCommands{ - Log: log.WithName(c.CommandType()), - } +func (c *CmdCreateDB) Run(vcc vclusterops.VClusterCommands) error { vcc.Log.V(1).Info("Called method Run()") vdb, createError := vcc.VCreateDatabase(c.createDBOptions) if createError != nil { return createError } // write cluster information to the YAML config file - err := vdb.WriteClusterConfig(c.createDBOptions.ConfigDirectory) + err := vdb.WriteClusterConfig(c.createDBOptions.ConfigDirectory, vcc.Log) if err != nil { - vlog.LogPrintWarning("fail to write config file, details: %s", err) + vcc.Log.PrintWarning("fail to write config file, details: %s", err) } - vlog.LogPrintInfo("Created a database with name [%s]", vdb.Name) + vcc.Log.PrintInfo("Created a database with name [%s]", vdb.Name) return nil } diff --git a/commands/cmd_drop_db.go b/commands/cmd_drop_db.go index dbab2de..e4079bc 100644 --- a/commands/cmd_drop_db.go +++ b/commands/cmd_drop_db.go @@ -47,9 +47,9 @@ func (c *CmdDropDB) CommandType() string { return "drop_db" } -func (c *CmdDropDB) Parse(inputArgv []string) error { +func (c *CmdDropDB) Parse(inputArgv []string, log vlog.Printer) error { c.argv = inputArgv - err := c.ValidateParseArgv(c.CommandType()) + err := c.ValidateParseArgv(c.CommandType(), log) if err != nil { return err } @@ -57,9 +57,6 @@ func (c *CmdDropDB) Parse(inputArgv []string) error { // for some options, we do not want to use their default values, // if they are not provided in cli, // reset the value of those options to nil - if !util.IsOptionSet(c.parser, "password") { - c.dropDBOptions.Password = nil - } if !util.IsOptionSet(c.parser, "config-directory") { c.dropDBOptions.ConfigDirectory = nil } @@ -67,22 +64,22 @@ func (c *CmdDropDB) Parse(inputArgv []string) error { c.CmdBase.ipv6 = nil } - return c.validateParse() + return c.validateParse(log) } -func (c *CmdDropDB) validateParse() error { - vlog.LogInfo("[%s] Called validateParse()", c.CommandType()) +func (c *CmdDropDB) validateParse(log vlog.Printer) error { + if !util.IsOptionSet(c.parser, "password") { + c.dropDBOptions.Password = nil + } + log.Info("Called validateParse()") return c.ValidateParseBaseOptions(&c.dropDBOptions.DatabaseOptions) } -func (c *CmdDropDB) Analyze() error { +func (c *CmdDropDB) Analyze(_ vlog.Printer) error { return nil } -func (c *CmdDropDB) Run(log vlog.Printer) error { - vcc := vclusterops.VClusterCommands{ - Log: log.WithName(c.CommandType()), - } +func (c *CmdDropDB) Run(vcc vclusterops.VClusterCommands) error { vcc.Log.V(1).Info("Called method Run()") err := vcc.VDropDatabase(c.dropDBOptions) @@ -91,6 +88,6 @@ func (c *CmdDropDB) Run(log vlog.Printer) error { return err } - vlog.LogPrintInfo("Successfully dropped database %s\n", *c.dropDBOptions.DBName) + vcc.Log.PrintInfo("Successfully dropped database %s\n", *c.dropDBOptions.DBName) return nil } diff --git a/commands/cmd_help.go b/commands/cmd_help.go index 69fdb1a..7325547 100644 --- a/commands/cmd_help.go +++ b/commands/cmd_help.go @@ -19,6 +19,7 @@ import ( "flag" "fmt" + "github.com/vertica/vcluster/vclusterops" "github.com/vertica/vcluster/vclusterops/vlog" ) @@ -47,8 +48,8 @@ func (c CmdHelp) CommandType() string { return "help" } -func (c *CmdHelp) Parse(inputArgv []string) error { - vlog.LogArgParse(&inputArgv) +func (c *CmdHelp) Parse(inputArgv []string, log vlog.Printer) error { + log.LogArgParse(&inputArgv) if c.parser == nil { return fmt.Errorf("unexpected nil - the parser was nil") @@ -60,18 +61,18 @@ func (c *CmdHelp) Parse(inputArgv []string) error { return err } - return c.validateParse() + return c.validateParse(log) } -func (c *CmdHelp) validateParse() error { - vlog.LogInfoln("Called validateParse()") +func (c *CmdHelp) validateParse(log vlog.Printer) error { + log.Info("Called validateParse()") return nil } -func (c *CmdHelp) Analyze() error { +func (c *CmdHelp) Analyze(_ vlog.Printer) error { return nil } -func (c *CmdHelp) Run(_ vlog.Printer) error { +func (c *CmdHelp) Run(_ vclusterops.VClusterCommands) error { return nil } diff --git a/commands/cmd_init.go b/commands/cmd_init.go index 79412bb..c693444 100644 --- a/commands/cmd_init.go +++ b/commands/cmd_init.go @@ -58,8 +58,8 @@ func (c *CmdInit) CommandType() string { return "init" } -func (c *CmdInit) Parse(inputArgv []string) error { - vlog.LogArgParse(&inputArgv) +func (c *CmdInit) Parse(inputArgv []string, log vlog.Printer) error { + log.LogArgParse(&inputArgv) if c.parser == nil { return fmt.Errorf("unexpected nil - the parser was nil") @@ -71,11 +71,11 @@ func (c *CmdInit) Parse(inputArgv []string) error { return err } - return c.validateParse() + return c.validateParse(log) } -func (c *CmdInit) validateParse() error { - vlog.LogInfoln("Called validateParse()") +func (c *CmdInit) validateParse(log vlog.Printer) error { + log.Info("Called validateParse()") // if directory is not provided, then use the current directory err := c.validateDirectory() @@ -91,18 +91,19 @@ func (c *CmdInit) validateParse() error { return nil } -func (c *CmdInit) Analyze() error { +func (c *CmdInit) Analyze(log vlog.Printer) error { + log.Info("Called method Analyze()") return nil } -func (c *CmdInit) Run(_ vlog.Printer) error { +func (c *CmdInit) Run(vcc vclusterops.VClusterCommands) error { configFilePath := filepath.Join(*c.directory, vclusterops.ConfigFileName) // check config file existence _, e := os.Stat(configFilePath) if e == nil { errMsg := fmt.Sprintf("The config file %s already exists", configFilePath) - vlog.LogPrintErrorln(errMsg) + vcc.Log.PrintError(errMsg) return errors.New(errMsg) } @@ -118,7 +119,7 @@ func (c *CmdInit) Run(_ vlog.Printer) error { for _, h := range hosts { nodeConfig := vclusterops.NodeConfig{} nodeConfig.Address = h - dbConfig.Nodes = append(dbConfig.Nodes, nodeConfig) + dbConfig.Nodes = append(dbConfig.Nodes, &nodeConfig) } clusterConfig := vclusterops.MakeClusterConfig() @@ -130,7 +131,7 @@ func (c *CmdInit) Run(_ vlog.Printer) error { return err } - vlog.LogPrintInfo("Created config file at %s\n", configFilePath) + vcc.Log.PrintInfo("Created config file at %s\n", configFilePath) return nil } diff --git a/commands/cmd_list_all_nodes.go b/commands/cmd_list_all_nodes.go index 217f1d3..1b7888d 100644 --- a/commands/cmd_list_all_nodes.go +++ b/commands/cmd_list_all_nodes.go @@ -39,9 +39,9 @@ func (c *CmdListAllNodes) CommandType() string { return "list_allnodes" } -func (c *CmdListAllNodes) Parse(inputArgv []string) error { +func (c *CmdListAllNodes) Parse(inputArgv []string, log vlog.Printer) error { c.argv = inputArgv - err := c.ValidateParseArgv(c.CommandType()) + err := c.ValidateParseArgv(c.CommandType(), log) if err != nil { return err } @@ -53,11 +53,11 @@ func (c *CmdListAllNodes) Parse(inputArgv []string) error { c.fetchNodeStateOptions.Password = nil } - return c.validateParse() + return c.validateParse(log) } -func (c *CmdListAllNodes) validateParse() error { - vlog.LogInfoln("Called validateParse()") +func (c *CmdListAllNodes) validateParse(log vlog.Printer) error { + log.Info("Called validateParse()") // parse raw host str input into a []string err := c.ParseHostList(&c.fetchNodeStateOptions.DatabaseOptions) @@ -71,14 +71,11 @@ func (c *CmdListAllNodes) validateParse() error { return nil } -func (c *CmdListAllNodes) Analyze() error { +func (c *CmdListAllNodes) Analyze(_ vlog.Printer) error { return nil } -func (c *CmdListAllNodes) Run(log vlog.Printer) error { - vcc := vclusterops.VClusterCommands{ - Log: log.WithName(c.CommandType()), - } +func (c *CmdListAllNodes) Run(vcc vclusterops.VClusterCommands) error { vcc.Log.V(1).Info("Called method Run()") nodeStates, err := vcc.VFetchNodeState(c.fetchNodeStateOptions) @@ -86,7 +83,7 @@ func (c *CmdListAllNodes) Run(log vlog.Printer) error { // if all nodes are down, the nodeStates list is not empty // for this case, we don't want to show errors but show DOWN for the nodes if len(nodeStates) == 0 { - vlog.LogPrintError("fail to list all nodes: %s", err) + vcc.Log.PrintError("fail to list all nodes: %s", err) return err } } @@ -95,6 +92,6 @@ func (c *CmdListAllNodes) Run(log vlog.Printer) error { if err != nil { return fmt.Errorf("fail to marshal the node state result, details %w", err) } - vlog.LogPrintInfo("Node states: %s", string(bytes)) + vcc.Log.PrintInfo("Node states: %s", string(bytes)) return nil } diff --git a/commands/cmd_re_ip.go b/commands/cmd_re_ip.go index de65360..1bb5d16 100644 --- a/commands/cmd_re_ip.go +++ b/commands/cmd_re_ip.go @@ -40,24 +40,24 @@ func (c *CmdReIP) CommandType() string { return "re_ip" } -func (c *CmdReIP) Parse(inputArgv []string) error { - vlog.LogArgParse(&inputArgv) +func (c *CmdReIP) Parse(inputArgv []string, log vlog.Printer) error { + log.LogArgParse(&inputArgv) if c.parser == nil { return fmt.Errorf("unexpected nil - the parser was nil") } c.argv = inputArgv - err := c.ValidateParseArgv(c.CommandType()) + err := c.ValidateParseArgv(c.CommandType(), log) if err != nil { return err } - return c.validateParse() + return c.validateParse(log) } -func (c *CmdReIP) validateParse() error { - vlog.LogInfo("[%s] Called validateParse()\n", c.CommandType()) +func (c *CmdReIP) validateParse(log vlog.Printer) error { + log.Info("Called validateParse()") // parse raw host str input into a []string err := c.ParseHostList(&c.reIPOptions.DatabaseOptions) @@ -71,7 +71,7 @@ func (c *CmdReIP) validateParse() error { return nil } -func (c *CmdReIP) Analyze() error { +func (c *CmdReIP) Analyze(_ vlog.Printer) error { if *c.reIPFilePath == "" { return errors.New("must specify the re-ip-file path") } @@ -79,10 +79,7 @@ func (c *CmdReIP) Analyze() error { return c.reIPOptions.ReadReIPFile(*c.reIPFilePath) } -func (c *CmdReIP) Run(log vlog.Printer) error { - vcc := vclusterops.VClusterCommands{ - Log: log.WithName(c.CommandType()), - } +func (c *CmdReIP) Run(vcc vclusterops.VClusterCommands) error { vcc.Log.V(1).Info("Called method Run()") err := vcc.VReIP(c.reIPOptions) if err != nil { diff --git a/commands/cmd_remove_node.go b/commands/cmd_remove_node.go index 820ac4b..d12c9e4 100644 --- a/commands/cmd_remove_node.go +++ b/commands/cmd_remove_node.go @@ -72,9 +72,9 @@ func (c *CmdRemoveNode) CommandType() string { return "db_remove_node" } -func (c *CmdRemoveNode) Parse(inputArgv []string) error { +func (c *CmdRemoveNode) Parse(inputArgv []string, log vlog.Printer) error { c.argv = inputArgv - err := c.ValidateParseArgv(c.CommandType()) + err := c.ValidateParseArgv(c.CommandType(), log) if err != nil { return err } @@ -89,11 +89,11 @@ func (c *CmdRemoveNode) Parse(inputArgv []string) error { if !util.IsOptionSet(c.parser, "password") { c.removeNodeOptions.Password = nil } - return c.validateParse() + return c.validateParse(log) } -func (c *CmdRemoveNode) validateParse() error { - vlog.LogInfo("[%s] Called validateParse()", c.CommandType()) +func (c *CmdRemoveNode) validateParse(log vlog.Printer) error { + log.Info("Called validateParse()") err := c.removeNodeOptions.ParseHostToRemoveList(*c.hostToRemoveListStr) if err != nil { @@ -102,20 +102,17 @@ func (c *CmdRemoveNode) validateParse() error { return c.ValidateParseBaseOptions(&c.removeNodeOptions.DatabaseOptions) } -func (c *CmdRemoveNode) Analyze() error { +func (c *CmdRemoveNode) Analyze(_ vlog.Printer) error { return nil } -func (c *CmdRemoveNode) Run(log vlog.Printer) error { - vcc := vclusterops.VClusterCommands{ - Log: log.WithName(c.CommandType()), - } +func (c *CmdRemoveNode) Run(vcc vclusterops.VClusterCommands) error { vcc.Log.V(1).Info("Called method Run()") options := c.removeNodeOptions // get config from vertica_cluster.yaml - config, err := c.removeNodeOptions.GetDBConfig() + config, err := c.removeNodeOptions.GetDBConfig(vcc) if err != nil { return err } @@ -125,13 +122,13 @@ func (c *CmdRemoveNode) Run(log vlog.Printer) error { if err != nil { return err } - vlog.LogPrintInfo("Successfully removed nodes %s from database %s", *c.hostToRemoveListStr, *options.DBName) + vcc.Log.PrintInfo("Successfully removed nodes %s from database %s", *c.hostToRemoveListStr, *options.DBName) // write cluster information to the YAML config file. - err = vdb.WriteClusterConfig(options.ConfigDirectory) + err = vdb.WriteClusterConfig(options.ConfigDirectory, vcc.Log) if err != nil { - vlog.LogPrintWarning("failed to write config file, details: %s", err) + vcc.Log.PrintWarning("failed to write config file, details: %s", err) } - vlog.LogPrintInfo("Successfully updated config file") + vcc.Log.PrintInfo("Successfully updated config file") return nil } diff --git a/commands/cmd_remove_subcluster.go b/commands/cmd_remove_subcluster.go index eb4d3ef..0376bf5 100644 --- a/commands/cmd_remove_subcluster.go +++ b/commands/cmd_remove_subcluster.go @@ -67,9 +67,9 @@ func (c *CmdRemoveSubcluster) CommandType() string { return "db_remove_subcluster" } -func (c *CmdRemoveSubcluster) Parse(inputArgv []string) error { +func (c *CmdRemoveSubcluster) Parse(inputArgv []string, log vlog.Printer) error { c.argv = inputArgv - err := c.ValidateParseArgv(c.CommandType()) + err := c.ValidateParseArgv(c.CommandType(), log) if err != nil { return err } @@ -84,24 +84,20 @@ func (c *CmdRemoveSubcluster) Parse(inputArgv []string) error { if !util.IsOptionSet(c.parser, "password") { c.removeScOptions.Password = nil } - return c.validateParse() + return c.validateParse(log) } -func (c *CmdRemoveSubcluster) validateParse() error { - vlog.LogInfo("[%s] Called validateParse()", c.CommandType()) +func (c *CmdRemoveSubcluster) validateParse(log vlog.Printer) error { + log.Info("Called validateParse()") return c.ValidateParseBaseOptions(&c.removeScOptions.DatabaseOptions) } -func (c *CmdRemoveSubcluster) Analyze() error { +func (c *CmdRemoveSubcluster) Analyze(_ vlog.Printer) error { return nil } -func (c *CmdRemoveSubcluster) Run(log vlog.Printer) error { - vcc := vclusterops.VClusterCommands{ - Log: log.WithName(c.CommandType()), - } - +func (c *CmdRemoveSubcluster) Run(vcc vclusterops.VClusterCommands) error { vcc.Log.V(1).Info("Called method Run()") vdb, err := vcc.VRemoveSubcluster(c.removeScOptions) if err != nil { @@ -111,7 +107,7 @@ func (c *CmdRemoveSubcluster) Run(log vlog.Printer) error { *c.removeScOptions.SubclusterToRemove, *c.removeScOptions.DBName) // write cluster information to the YAML config file. - err = vdb.WriteClusterConfig(c.removeScOptions.ConfigDirectory) + err = vdb.WriteClusterConfig(c.removeScOptions.ConfigDirectory, vcc.Log) if err != nil { vcc.Log.PrintWarning("failed to write config file, details: %s", err) } diff --git a/commands/cmd_restart_node.go b/commands/cmd_restart_node.go index 05050c5..9d2d568 100644 --- a/commands/cmd_restart_node.go +++ b/commands/cmd_restart_node.go @@ -60,13 +60,13 @@ func (c *CmdRestartNodes) CommandType() string { return "restart_node" } -func (c *CmdRestartNodes) Parse(inputArgv []string) error { +func (c *CmdRestartNodes) Parse(inputArgv []string, log vlog.Printer) error { if c.parser == nil { return fmt.Errorf("unexpected nil - the parser was nil") } c.argv = inputArgv - err := c.ValidateParseArgv(c.CommandType()) + err := c.ValidateParseArgv(c.CommandType(), log) if err != nil { return err } @@ -82,11 +82,11 @@ func (c *CmdRestartNodes) Parse(inputArgv []string) error { c.CmdBase.ipv6 = nil } - return c.validateParse() + return c.validateParse(log) } -func (c *CmdRestartNodes) validateParse() error { - vlog.LogInfo("[%s] Called validateParse()", c.CommandType()) +func (c *CmdRestartNodes) validateParse(log vlog.Printer) error { + log.Info("Called validateParse()") err := c.restartNodesOptions.ParseNodesList(*c.vnodeListStr) if err != nil { return err @@ -94,23 +94,20 @@ func (c *CmdRestartNodes) validateParse() error { return c.ValidateParseBaseOptions(&c.restartNodesOptions.DatabaseOptions) } -func (c *CmdRestartNodes) Analyze() error { +func (c *CmdRestartNodes) Analyze(log vlog.Printer) error { // Analyze() is needed to fulfill an interface - vlog.LogInfoln("Called method Analyze()") + log.Info("Called method Analyze()") return nil } -func (c *CmdRestartNodes) Run(log vlog.Printer) error { - vcc := vclusterops.VClusterCommands{ - Log: log.WithName(c.CommandType()), - } +func (c *CmdRestartNodes) Run(vcc vclusterops.VClusterCommands) error { vcc.Log.V(1).Info("Called method Run()") options := c.restartNodesOptions // load vdb info from the YAML config file // get config from vertica_cluster.yaml - config, err := options.GetDBConfig() + config, err := options.GetDBConfig(vcc) if err != nil { return err } @@ -126,7 +123,7 @@ func (c *CmdRestartNodes) Run(log vlog.Printer) error { for _, ip := range options.Nodes { hostToRestart = append(hostToRestart, ip) } - vlog.LogPrintInfo("Successfully restart hosts %s of the database %s", hostToRestart, *options.DBName) + vcc.Log.PrintInfo("Successfully restart hosts %s of the database %s", hostToRestart, *options.DBName) return nil } diff --git a/commands/cmd_revive_db.go b/commands/cmd_revive_db.go index c78baeb..3867e80 100644 --- a/commands/cmd_revive_db.go +++ b/commands/cmd_revive_db.go @@ -60,9 +60,9 @@ func (c *CmdReviveDB) CommandType() string { return "revive_db" } -func (c *CmdReviveDB) Parse(inputArgv []string) error { +func (c *CmdReviveDB) Parse(inputArgv []string, log vlog.Printer) error { c.argv = inputArgv - err := c.ValidateParseMaskedArgv(c.CommandType()) + err := c.ValidateParseMaskedArgv(c.CommandType(), log) if err != nil { return err } @@ -74,11 +74,11 @@ func (c *CmdReviveDB) Parse(inputArgv []string) error { c.CmdBase.ipv6 = nil } - return c.validateParse() + return c.validateParse(log) } -func (c *CmdReviveDB) validateParse() error { - vlog.LogInfo("[%s] Called validateParse()", c.CommandType()) +func (c *CmdReviveDB) validateParse(log vlog.Printer) error { + log.Info("Called validateParse()") // check the format of configuration params string, and parse it into configParams configurationParams, err := util.ParseConfigParams(*c.configurationParams) @@ -100,19 +100,16 @@ func (c *CmdReviveDB) validateParse() error { return c.ValidateParseBaseOptions(&c.reviveDBOptions.DatabaseOptions) } -func (c *CmdReviveDB) Analyze() error { - vlog.LogInfoln("Called method Analyze()") +func (c *CmdReviveDB) Analyze(log vlog.Printer) error { + log.Info("Called method Analyze()") return nil } -func (c *CmdReviveDB) Run(log vlog.Printer) error { - vcc := vclusterops.VClusterCommands{ - Log: log.WithName(c.CommandType()), - } +func (c *CmdReviveDB) Run(vcc vclusterops.VClusterCommands) error { vcc.Log.V(1).Info("Called method Run()") dbInfo, err := vcc.VReviveDatabase(c.reviveDBOptions) if err != nil { - vcc.Log.Error(err, "fail to revive database %s", *c.reviveDBOptions.DBName) + vcc.Log.Error(err, "fail to revive database", "DBName", *c.reviveDBOptions.DBName) return err } diff --git a/commands/cmd_scrutinize.go b/commands/cmd_scrutinize.go index a4832b5..b22c84f 100644 --- a/commands/cmd_scrutinize.go +++ b/commands/cmd_scrutinize.go @@ -63,51 +63,48 @@ func (c *CmdScrutinize) CommandType() string { return "scrutinize" } -func (c *CmdScrutinize) Parse(inputArgv []string) error { +func (c *CmdScrutinize) Parse(inputArgv []string, log vlog.Printer) error { c.argv = inputArgv // from now on we use the internal copy of argv - return c.parseInternal() + return c.parseInternal(log) } -func (c *CmdScrutinize) parseInternal() error { +func (c *CmdScrutinize) parseInternal(log vlog.Printer) error { if c.parser == nil { return fmt.Errorf("unexpected nil for CmdScrutinize.parser") } - vlog.LogPrintInfo("Parsing scrutinize command input") + log.PrintInfo("Parsing scrutinize command input") parseError := c.ParseArgv() if parseError != nil { return parseError } - vlog.LogInfoln("Parsing host list") + log.Info("Parsing host list") var hostParseError error c.sOptions.RawHosts, hostParseError = util.SplitHosts(*c.hostListStr) if hostParseError != nil { return hostParseError } - vlog.LogInfo("Host list size %d values %s", len(c.sOptions.RawHosts), c.sOptions.RawHosts) + log.Info("Host list size and values", "size", len(c.sOptions.RawHosts), "values", c.sOptions.RawHosts) return nil } -func (c *CmdScrutinize) Analyze() error { - vlog.LogInfoln("Called method Analyze()") +func (c *CmdScrutinize) Analyze(log vlog.Printer) error { + log.Info("Called method Analyze()") var resolveError error c.sOptions.Hosts, resolveError = util.ResolveRawHostsToAddresses(c.sOptions.RawHosts, false /*ipv6?*/) if resolveError != nil { return resolveError } - vlog.LogInfo("Resolved host list to IPs: %s", c.sOptions.Hosts) + log.Info("Resolved host list to IPs", "hosts", c.sOptions.Hosts) return nil } -func (c *CmdScrutinize) Run(log vlog.Printer) error { - vcc := vclusterops.VClusterCommands{ - Log: log.WithName(c.CommandType()), - } - vlog.LogPrintInfo("Running scrutinize") +func (c *CmdScrutinize) Run(vcc vclusterops.VClusterCommands) error { + vcc.Log.PrintInfo("Running scrutinize") vcc.Log.V(0).Info("Calling method Run() for command " + c.CommandType()) err := vcc.VScrutinize(&c.sOptions) - vlog.LogPrintInfo("Completed method Run() for command " + c.CommandType()) + vcc.Log.PrintInfo("Completed method Run() for command " + c.CommandType()) return err } diff --git a/commands/cmd_start_db.go b/commands/cmd_start_db.go index 421b5fe..28b69aa 100644 --- a/commands/cmd_start_db.go +++ b/commands/cmd_start_db.go @@ -79,13 +79,13 @@ func (c *CmdStartDB) CommandType() string { return "start_db" } -func (c *CmdStartDB) Parse(inputArgv []string) error { +func (c *CmdStartDB) Parse(inputArgv []string, log vlog.Printer) error { if c.parser == nil { return fmt.Errorf("unexpected nil - the parser was nil") } c.argv = inputArgv - err := c.ValidateParseArgv(c.CommandType()) + err := c.ValidateParseArgv(c.CommandType(), log) if err != nil { return err } @@ -105,11 +105,11 @@ func (c *CmdStartDB) Parse(inputArgv []string) error { c.startDBOptions.ConfigDirectory = nil } - return c.validateParse() + return c.validateParse(log) } -func (c *CmdStartDB) validateParse() error { - vlog.LogInfo("[%s] Called validateParse()", c.CommandType()) +func (c *CmdStartDB) validateParse(log vlog.Printer) error { + log.Info("Called validateParse()", "command", c.CommandType()) // check the format of configuration params string, and parse it into configParams configurationParams, err := util.ParseConfigParams(*c.configurationParams) @@ -123,23 +123,20 @@ func (c *CmdStartDB) validateParse() error { return c.ValidateParseBaseOptions(&c.startDBOptions.DatabaseOptions) } -func (c *CmdStartDB) Analyze() error { +func (c *CmdStartDB) Analyze(log vlog.Printer) error { // Analyze() is needed to fulfill an interface - vlog.LogInfoln("Called method Analyze()") + log.Info("Called method Analyze()") return nil } -func (c *CmdStartDB) Run(log vlog.Printer) error { - vcc := vclusterops.VClusterCommands{ - Log: log.WithName(c.CommandType()), - } +func (c *CmdStartDB) Run(vcc vclusterops.VClusterCommands) error { vcc.Log.V(1).Info("Called method Run()") options := c.startDBOptions // load vdb info from the YAML config file // get config from vertica_cluster.yaml - config, err := options.GetDBConfig() + config, err := options.GetDBConfig(vcc) if err != nil { return err } @@ -151,6 +148,6 @@ func (c *CmdStartDB) Run(log vlog.Printer) error { return err } - vlog.LogPrintInfo("Successfully start the database %s\n", *options.DBName) + vcc.Log.PrintInfo("Successfully start the database %s\n", *options.DBName) return nil } diff --git a/commands/cmd_stop_db.go b/commands/cmd_stop_db.go index 29ff9d0..30860cb 100644 --- a/commands/cmd_stop_db.go +++ b/commands/cmd_stop_db.go @@ -85,9 +85,9 @@ func (c *CmdStopDB) CommandType() string { return "stop_db" } -func (c *CmdStopDB) Parse(inputArgv []string) error { +func (c *CmdStopDB) Parse(inputArgv []string, log vlog.Printer) error { c.argv = inputArgv - err := c.ValidateParseArgv(c.CommandType()) + err := c.ValidateParseArgv(c.CommandType(), log) if err != nil { return err } @@ -111,30 +111,27 @@ func (c *CmdStopDB) Parse(inputArgv []string) error { c.stopDBOptions.ConfigDirectory = nil } - return c.validateParse() + return c.validateParse(log) } // all validations of the arguments should go in here -func (c *CmdStopDB) validateParse() error { - vlog.LogInfo("[%s] Called validateParse()", c.CommandType()) +func (c *CmdStopDB) validateParse(log vlog.Printer) error { + log.Info("Called validateParse()") return c.ValidateParseBaseOptions(&c.stopDBOptions.DatabaseOptions) } -func (c *CmdStopDB) Analyze() error { - vlog.LogInfoln("Called method Analyze()") +func (c *CmdStopDB) Analyze(log vlog.Printer) error { + log.Info("Called method Analyze()") return nil } -func (c *CmdStopDB) Run(log vlog.Printer) error { - vcc := vclusterops.VClusterCommands{ - Log: log.WithName(c.CommandType()), - } +func (c *CmdStopDB) Run(vcc vclusterops.VClusterCommands) error { vcc.Log.Info("Called method Run()") options := c.stopDBOptions // get config from vertica_cluster.yaml - config, err := options.GetDBConfig() + config, err := options.GetDBConfig(vcc) if err != nil { return err } @@ -146,6 +143,6 @@ func (c *CmdStopDB) Run(log vlog.Printer) error { return err } - vlog.LogPrintInfo("Stopped a database with name %s", *options.DBName) + vcc.Log.PrintInfo("Stopped a database with name %s", *options.DBName) return nil } diff --git a/commands/init_and_config_test.go b/commands/init_and_config_test.go index 0b0bd0c..1a7666c 100644 --- a/commands/init_and_config_test.go +++ b/commands/init_and_config_test.go @@ -30,11 +30,11 @@ import ( func TestInitCmd(t *testing.T) { // no hosts provided, the case should fail c := makeCmdInit() - err := c.Parse([]string{}) + err := c.Parse([]string{}, vlog.Printer{}) assert.ErrorContains(t, err, "must provide the host list with --hosts") // hosts provided, the case should pass - err = c.Parse([]string{"--hosts", "vnode1,vnode2,vnode3"}) + err = c.Parse([]string{"--hosts", "vnode1,vnode2,vnode3"}, vlog.Printer{}) assert.Nil(t, err) // no directory provided, current directory will be used @@ -46,7 +46,7 @@ func TestInitCmd(t *testing.T) { c = makeCmdInit() err = c.Parse([]string{ "--hosts", "vnode1,vnode2,vnode3", - "--directory", configDir}) + "--directory", configDir}, vlog.Printer{}) assert.Nil(t, err) assert.Equal(t, "/opt/vertica/config", *c.directory) } @@ -57,39 +57,42 @@ func TestConfigCmd(t *testing.T) { log := vlog.Printer{ Log: buflogr.NewWithBuffer(&logStr), } - vlogger := vlog.GetGlobalLogger() + vlogger := vlog.Printer{} vlogger.Log = log.Log + vcc := vclusterops.VClusterCommands{ + Log: vlogger.WithName("initAndConfigTest"), + } // create a stub YAML file const yamlPath = vclusterops.ConfigFileName - const yamlStr = "hosts\n - vnode1\n - vnode2\n - vnode3" + const yamlStr = "hosts\\n - vnode1\\n - vnode2\\n - vnode3" _ = os.WriteFile(yamlPath, []byte(yamlStr), vclusterops.ConfigFilePerm) defer os.Remove(yamlPath) // if `--show` is not specified, the config content should not show c := makeCmdConfig() - err := c.Parse([]string{}) + err := c.Parse([]string{}, vcc.Log) assert.Nil(t, err) - err = c.Run(log) + err = c.Run(vcc) assert.Nil(t, err) assert.NotContains(t, logStr.String(), yamlStr) // if `--show` is specified, the config content should show c = makeCmdConfig() - err = c.Parse([]string{"--show"}) + err = c.Parse([]string{"--show"}, vcc.Log) assert.Nil(t, err) - err = c.Run(log) + err = c.Run(vcc) assert.Nil(t, err) assert.Contains(t, logStr.String(), yamlStr) // now run `init`, the command should fail // because the config file under the current directory already exists cmdInit := makeCmdInit() - err = cmdInit.Parse([]string{"--hosts", "vnode1,vnode2,vnode3"}) + err = cmdInit.Parse([]string{"--hosts", "vnode1,vnode2,vnode3"}, vlog.Printer{}) assert.Nil(t, err) - err = cmdInit.Run(log) + err = cmdInit.Run(vcc) assert.ErrorContains(t, err, vclusterops.ConfigFileName+" already exists") } diff --git a/rfc7807/errors.go b/rfc7807/errors.go index 6346db9..015e8e1 100644 --- a/rfc7807/errors.go +++ b/rfc7807/errors.go @@ -15,6 +15,11 @@ package rfc7807 +import ( + "net/http" + "path" +) + // List of all known RFC 7807 problems that vcluster may see. All are exported // from the package so they can be used by the NMA, vcluster, etc. // @@ -24,73 +29,127 @@ package rfc7807 // // In general, the title should be constant too. The only time we may want to // relax that is if they are changed for localization purposes. +const errorEndpointsPrefix = "https://integrators.vertica.com/rest/errors/" + var ( GenericBootstrapCatalogFailure = newProblemID( - "https://integrators.vertica.com/vcluster/errors/internal-bootstrap-catalog-failure", + path.Join(errorEndpointsPrefix, "internal-bootstrap-catalog-failure"), "Internal error while bootstraping the catalog", + http.StatusInternalServerError, ) CommunalStorageNotEmpty = newProblemID( - "https://integrators.vertica.com/vcluster/errors/communal-storage-not-empty", + path.Join(errorEndpointsPrefix, "communal-storage-not-empty"), "Communal storage is not empty", + http.StatusInternalServerError, ) CommunalStoragePathInvalid = newProblemID( - "https://integrators.vertica.com/vcluster/errors/communal-storage-path-invalid", + path.Join(errorEndpointsPrefix, "communal-storage-path-invalid"), "Communal storage is not a valid path for the file system", + http.StatusInternalServerError, ) CommunalRWAccessError = newProblemID( - "https://integrators.vertica.com/vcluster/errors/communal-read-write-access-error", + path.Join(errorEndpointsPrefix, "communal-read-write-access-error"), "Failed while testing read/write access to the communal storage", + http.StatusInternalServerError, ) CommunalAccessError = newProblemID( - "https://integrators.vertica.com/vcluster/errors/communal-access-error", + path.Join(errorEndpointsPrefix, "communal-access-error"), "Error accessing communal storage", + http.StatusInternalServerError, ) GenericLicenseCheckFailure = newProblemID( - "https://integrators.vertica.com/vcluster/errors/internal-license-check-failure", + path.Join(errorEndpointsPrefix, "internal-license-check-failure"), "Internal error while checking license file", + http.StatusInternalServerError, ) WrongRequestMethod = newProblemID( - "https://integrators.vertica.com/vcluster/errors/wrong-request-method", + path.Join(errorEndpointsPrefix, "wrong-request-method"), "Wrong request method used", + http.StatusMethodNotAllowed, ) BadRequest = newProblemID( - "https://integrators.vertica.com/vcluster/errors/bad-request", + path.Join(errorEndpointsPrefix, "bad-request"), "Bad request sent", + http.StatusBadRequest, + ) + GenericHTTPInternalServerError = newProblemID( + path.Join(errorEndpointsPrefix, "http-internal-server-error"), + "Internal server error", + http.StatusInternalServerError, ) GenericGetNodeInfoFailure = newProblemID( - "https://integrators.vertica.com/vcluster/errors/internal-get-node-info-failure", + path.Join(errorEndpointsPrefix, "internal-get-node-info-failure"), "Internal error while getting node information", + http.StatusInternalServerError, ) GenericLoadRemoteCatalogFailure = newProblemID( - "https://integrators.vertica.com/vcluster/errors/internal-load-remote-catalog-failure", + path.Join(errorEndpointsPrefix, "internal-load-remote-catalog-failure"), "Internal error while loading remote catalog", + http.StatusInternalServerError, ) GenericSpreadSecurityPersistenceFailure = newProblemID( - "https://integrators.vertica.com/vcluster/errors/spread-security-persistence-failure", + path.Join(errorEndpointsPrefix, "spread-security-persistence-failure"), "Internal error while persisting spread encryption key", + http.StatusInternalServerError, ) SubclusterNotFound = newProblemID( - "https://integrators.vertica.com/vcluster/errors/subcluster-not-found", + path.Join(errorEndpointsPrefix, "subcluster-not-found"), "Subcluster is not found", + http.StatusInternalServerError, ) GenericCatalogEditorFailure = newProblemID( - "https://integrators.vertica.com/vcluster/errors/internal-catalog-editor-failure", + path.Join(errorEndpointsPrefix, "internal-catalog-editor-failure"), "Internal error while running catalog editor", - ) -) - -// List of all known RFC 7807 problems that scrutinize may see. -var ( - ScrutinizeWrongRequestMethod = newProblemID( - "https://integrators.vertica.com/scrutinize/errors/wrong-request-method", - "Wrong request method used", - ) - ScrutinizeBadRequest = newProblemID( - "https://integrators.vertica.com/scrutinize/errors/bad-request", - "Bad request sent", - ) - ScrutinizeGenericError = newProblemID( - "https://integrators.vertica.com/scrutinize/errors/generic-error", - "Error while executing scrutinize operation", + http.StatusInternalServerError, + ) + GenericVerticaDownloadFileFailure = newProblemID( + path.Join(errorEndpointsPrefix, "general-vertica-download-file-failure"), + "General error while running Vertica download file", + http.StatusInternalServerError, + ) + InsufficientPrivilege = newProblemID( + path.Join(errorEndpointsPrefix, "insufficient-privilege"), + "Insufficient privilege", + http.StatusInternalServerError, + ) + UndefinedFile = newProblemID( + path.Join(errorEndpointsPrefix, "undefined-file"), + "Undefined file", + http.StatusInternalServerError, + ) + DuplicateFile = newProblemID( + path.Join(errorEndpointsPrefix, "duplicate-file"), + "Duplicate file", + http.StatusInternalServerError, + ) + WrongObjectType = newProblemID( + path.Join(errorEndpointsPrefix, "wrong-object-type"), + "Wrong object type", + http.StatusInternalServerError, + ) + DiskFull = newProblemID( + path.Join(errorEndpointsPrefix, "disk-full"), + "Disk full", + http.StatusInternalServerError, + ) + InsufficientResources = newProblemID( + path.Join(errorEndpointsPrefix, "insufficient-resources"), + "Insufficient resources", + http.StatusInternalServerError, + ) + IOError = newProblemID( + path.Join(errorEndpointsPrefix, "io-error"), + "IO error", + http.StatusInternalServerError, + ) + QueryCanceled = newProblemID( + path.Join(errorEndpointsPrefix, "query-canceled"), + "Query canceled", + http.StatusInternalServerError, + ) + InternalVerticaDownloadFileFailure = newProblemID( + path.Join(errorEndpointsPrefix, "internal-vertica-download-file-failure"), + "Internal error while running Vertica download file", + http.StatusInternalServerError, ) ) diff --git a/rfc7807/rfc7807.go b/rfc7807/rfc7807.go index dcd101c..059e41a 100644 --- a/rfc7807/rfc7807.go +++ b/rfc7807/rfc7807.go @@ -44,15 +44,15 @@ type ProblemID struct { // not change from occurrence to occurrence of the problem, except for // purposes of localization. Title string `json:"title"` + + // Status is the HTTP status code for this occurrence of the problem. + Status int `json:"status,omitempty"` } // VProblem is vertica's implementation of the RFC 7807 standard. type VProblem struct { ProblemID - // Status it the HTTP status code for this occurrence of the problem. - Status int `json:"status,omitempty"` - // A human-readable explanation specific to this occurrence of the problem. // Include any pertinent info in here to help them resolve the problem. Detail string `json:"detail,omitempty"` @@ -91,10 +91,11 @@ func GenerateErrorFromResponse(resp string) error { } // newProblemID will generate a ProblemID struct for use with VProblem -func newProblemID(errType, title string) ProblemID { +func newProblemID(errType, title string, status int) ProblemID { return ProblemID{ - Type: errType, - Title: title, + Type: errType, + Title: title, + Status: status, } } @@ -104,12 +105,6 @@ func (v *VProblem) WithDetail(d string) *VProblem { return v } -// WithStatus will set the http status code in the VProblem -func (v *VProblem) WithStatus(s int) *VProblem { - v.Status = s - return v -} - // WithHost will set the originating host in the VPrbolem. h can be a host name // or IP. func (v *VProblem) WithHost(h string) *VProblem { @@ -139,11 +134,10 @@ func (v *VProblem) SendError(w http.ResponseWriter) { fmt.Fprintln(w, string(respBytes)) } -func MakeProblem(problemID ProblemID, detail string, httpStatus int) Problem { +func MakeProblem(problemID ProblemID, detail string) Problem { hostname, _ := os.Hostname() return New(problemID). WithDetail(detail). - WithStatus(httpStatus). WithHost(hostname) } diff --git a/rfc7807/rfc7807_test.go b/rfc7807/rfc7807_test.go index ccb9dac..1459fe8 100644 --- a/rfc7807/rfc7807_test.go +++ b/rfc7807/rfc7807_test.go @@ -30,7 +30,6 @@ import ( func TestVProblemImplementsError(t *testing.T) { p := New(CommunalStorageNotEmpty). WithDetail("Path /communal needs to be empty"). - WithStatus(510). WithHost("pod-0") var err1 error var ExpectedErrorStr = fmt.Sprintf("%s on host pod-0", CommunalStorageNotEmpty.Title) @@ -43,7 +42,6 @@ func TestVProblemImplementsError(t *testing.T) { func TestWeCanTestProblemType(t *testing.T) { p := New(GenericBootstrapCatalogFailure). WithDetail("Internal error was hit during bootstrap catalog"). - WithStatus(501). WithHost("pod-1") assert.True(t, p.IsInstanceOf(GenericBootstrapCatalogFailure)) assert.False(t, p.IsInstanceOf(CommunalRWAccessError)) @@ -52,7 +50,6 @@ func TestWeCanTestProblemType(t *testing.T) { func TestHttpResponse(t *testing.T) { p := New(CommunalAccessError). WithDetail("communal endpoint is down"). - WithStatus(500). WithHost("pod-2") handler := func(w http.ResponseWriter, r *http.Request) { p.SendError(w) @@ -72,7 +69,6 @@ func TestHttpResponse(t *testing.T) { func TestProblemExtraction(t *testing.T) { origProblem := New(CommunalRWAccessError). WithDetail("could not read from communal storage"). - WithStatus(500). WithHost("pod-3") handler := func(w http.ResponseWriter, r *http.Request) { origProblem.SendError(w) diff --git a/vclusterops/adapter_pool.go b/vclusterops/adapter_pool.go index 69468f7..37b8003 100644 --- a/vclusterops/adapter_pool.go +++ b/vclusterops/adapter_pool.go @@ -23,6 +23,7 @@ import ( ) type AdapterPool struct { + log vlog.Printer // map from host to HTTPAdapter connections map[string]Adapter } @@ -33,22 +34,23 @@ var ( ) // return a singleton instance of the AdapterPool -func getPoolInstance() AdapterPool { +func getPoolInstance(log vlog.Printer) AdapterPool { /* if once.Do(f) is called multiple times, * only the first call will invoke f, * even if f has a different value in each invocation. * Reference: https://pkg.go.dev/sync#Once */ once.Do(func() { - poolInstance = makeAdapterPool() + poolInstance = makeAdapterPool(log) }) return poolInstance } -func makeAdapterPool() AdapterPool { +func makeAdapterPool(log vlog.Printer) AdapterPool { newAdapterPool := AdapterPool{} newAdapterPool.connections = make(map[string]Adapter) + newAdapterPool.log = log.WithName("AdapterPool") return newAdapterPool } @@ -58,7 +60,7 @@ type adapterToRequest struct { } func (pool *AdapterPool) sendRequest(clusterHTTPRequest *ClusterHTTPRequest) error { - vlog.LogInfoln("Adapter pool's sendRequest is called") + pool.log.Info("Adapter pool's sendRequest is called") // build a collection of adapter to request // we need this step as a host may not be in the pool // in that case, we should not proceed diff --git a/vclusterops/add_node.go b/vclusterops/add_node.go index 3f67f61..bbca681 100644 --- a/vclusterops/add_node.go +++ b/vclusterops/add_node.go @@ -20,6 +20,7 @@ import ( "strings" "github.com/vertica/vcluster/vclusterops/util" + "github.com/vertica/vcluster/vclusterops/vlog" ) // VAddNodeOptions are the option arguments for the VAddNode API @@ -78,9 +79,9 @@ func (o *VAddNodeOptions) validateExtraOptions() error { return util.ValidateRequiredAbsPath(o.DataPrefix, "data path") } -func (o *VAddNodeOptions) validateParseOptions() error { +func (o *VAddNodeOptions) validateParseOptions(log vlog.Printer) error { // batch 1: validate required parameters - err := o.ValidateBaseOptions("db_add_node") + err := o.ValidateBaseOptions("db_add_node", log) if err != nil { return err } @@ -109,8 +110,8 @@ func (o *VAddNodeOptions) analyzeOptions() (err error) { return nil } -func (o *VAddNodeOptions) validateAnalyzeOptions() error { - err := o.validateParseOptions() +func (o *VAddNodeOptions) validateAnalyzeOptions(log vlog.Printer) error { + err := o.validateParseOptions(log) if err != nil { return err } @@ -122,7 +123,7 @@ func (o *VAddNodeOptions) validateAnalyzeOptions() error { func (vcc *VClusterCommands) VAddNode(options *VAddNodeOptions) (VCoordinationDatabase, error) { vdb := MakeVCoordinationDatabase() - err := options.validateAnalyzeOptions() + err := options.validateAnalyzeOptions(vcc.Log) if err != nil { return vdb, err } @@ -190,7 +191,7 @@ func (vcc *VClusterCommands) VAddNode(options *VAddNodeOptions) (VCoordinationDa certs := HTTPSCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} clusterOpEngine := MakeClusterOpEngine(instructions, &certs) - if runError := clusterOpEngine.Run(); runError != nil { + if runError := clusterOpEngine.Run(vcc.Log); runError != nil { return vdb, fmt.Errorf("fail to complete add node operation, %w", runError) } return vdb, nil @@ -299,7 +300,7 @@ func (vcc *VClusterCommands) trimNodesInCatalog(vdb *VCoordinationDatabase, certs := HTTPSCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} clusterOpEngine := MakeClusterOpEngine(instructions, &certs) - err := clusterOpEngine.Run() + err := clusterOpEngine.Run(vcc.Log) if err != nil { vcc.Log.Error(err, "fail to trim nodes from catalog, %v") return err diff --git a/vclusterops/add_subcluster.go b/vclusterops/add_subcluster.go index ddf840f..6a50844 100644 --- a/vclusterops/add_subcluster.go +++ b/vclusterops/add_subcluster.go @@ -70,8 +70,8 @@ func (options *VAddSubclusterOptions) SetDefaultValues() { options.CloneSC = new(string) } -func (options *VAddSubclusterOptions) validateRequiredOptions() error { - err := options.ValidateBaseOptions("db_add_subcluster") +func (options *VAddSubclusterOptions) validateRequiredOptions(log vlog.Printer) error { + err := options.ValidateBaseOptions("db_add_subcluster", log) if err != nil { return err } @@ -94,7 +94,7 @@ func (options *VAddSubclusterOptions) validateEonOptions(config *ClusterConfig) return nil } -func (options *VAddSubclusterOptions) validateExtraOptions() error { +func (options *VAddSubclusterOptions) validateExtraOptions(log vlog.Printer) error { // control-set-size can only be -1 or [1 to 120] if !(*options.ControlSetSize == ControlSetSizeDefaultValue || (*options.ControlSetSize >= ControlSetSizeLowerBound && *options.ControlSetSize <= ControlSetSizeUpperBound)) { @@ -104,7 +104,7 @@ func (options *VAddSubclusterOptions) validateExtraOptions() error { if *options.CloneSC != "" { // TODO remove this log after we supported subcluster clone - vlog.LogPrintWarningln("option CloneSC is not implemented yet so it will be ignored") + log.PrintWarning("option CloneSC is not implemented yet so it will be ignored") } // verify the hosts of new subcluster does not exist in current database @@ -124,15 +124,15 @@ func (options *VAddSubclusterOptions) validateExtraOptions() error { } // TODO remove this log after we supported adding subcluster with nodes - vlog.LogPrintWarningln("options SCRawHosts and SCHosts are not implemented yet so they will be ignored") + log.PrintWarning("options SCRawHosts and SCHosts are not implemented yet so they will be ignored") } return nil } -func (options *VAddSubclusterOptions) validateParseOptions(config *ClusterConfig) error { +func (options *VAddSubclusterOptions) validateParseOptions(config *ClusterConfig, vcc *VClusterCommands) error { // batch 1: validate required parameters - err := options.validateRequiredOptions() + err := options.validateRequiredOptions(vcc.Log) if err != nil { return err } @@ -142,7 +142,7 @@ func (options *VAddSubclusterOptions) validateParseOptions(config *ClusterConfig return err } // batch 3: validate all other params - err = options.validateExtraOptions() + err = options.validateExtraOptions(vcc.Log) if err != nil { return err } @@ -169,8 +169,8 @@ func (options *VAddSubclusterOptions) analyzeOptions() (err error) { return nil } -func (options *VAddSubclusterOptions) ValidateAnalyzeOptions(config *ClusterConfig) error { - if err := options.validateParseOptions(config); err != nil { +func (options *VAddSubclusterOptions) ValidateAnalyzeOptions(config *ClusterConfig, vcc *VClusterCommands) error { + if err := options.validateParseOptions(config, vcc); err != nil { return err } return options.analyzeOptions() @@ -184,7 +184,7 @@ func (vcc *VClusterCommands) VAddSubcluster(options *VAddSubclusterOptions) erro * - Give the instructions to the VClusterOpEngine to run */ - err := options.ValidateAnalyzeOptions(options.Config) + err := options.ValidateAnalyzeOptions(options.Config, vcc) if err != nil { return err } @@ -214,7 +214,7 @@ func (vcc *VClusterCommands) VAddSubcluster(options *VAddSubclusterOptions) erro clusterOpEngine := MakeClusterOpEngine(instructions, &certs) // Give the instructions to the VClusterOpEngine to run - runError := clusterOpEngine.Run() + runError := clusterOpEngine.Run(vcc.Log) if runError != nil { return fmt.Errorf("fail to add subcluster %s, %w", addSubclusterInfo.SCName, runError) } @@ -241,7 +241,7 @@ func (vcc *VClusterCommands) produceAddSubclusterInstructions(addSubclusterInfo usePassword := false if addSubclusterInfo.Password != nil { usePassword = true - err := options.ValidateUserName(vcc) + err := options.ValidateUserName(vcc.Log) if err != nil { return instructions, err } diff --git a/vclusterops/cluster_config.go b/vclusterops/cluster_config.go index d484ba4..695ac00 100644 --- a/vclusterops/cluster_config.go +++ b/vclusterops/cluster_config.go @@ -37,18 +37,19 @@ const ConfigBackupName = "vertica_cluster.yaml.backup" type ClusterConfig map[string]DatabaseConfig type DatabaseConfig struct { - Nodes []NodeConfig `yaml:"nodes"` - CatalogPath string `yaml:"catalog_path"` - DataPath string `yaml:"data_path"` - DepotPath string `yaml:"depot_path"` - IsEon bool `yaml:"eon_mode"` - Ipv6 bool `yaml:"ipv6"` + Nodes []*NodeConfig `yaml:"nodes"` + IsEon bool `yaml:"eon_mode"` + CommunalStorageLocation string `yaml:"communal_storage_location"` + Ipv6 bool `yaml:"ipv6"` } type NodeConfig struct { - Name string `yaml:"name"` - Address string `yaml:"address"` - Subcluster string `yaml:"subcluster"` + Name string `yaml:"name"` + Address string `yaml:"address"` + Subcluster string `yaml:"subcluster"` + CatalogPath string `yaml:"catalog_path"` + DataPath string `yaml:"data_path"` + DepotPath string `yaml:"depot_path"` } func MakeClusterConfig() ClusterConfig { @@ -60,7 +61,7 @@ func MakeDatabaseConfig() DatabaseConfig { } // read config information from the YAML file -func ReadConfig(configDirectory string) (ClusterConfig, error) { +func ReadConfig(configDirectory string, log vlog.Printer) (ClusterConfig, error) { clusterConfig := ClusterConfig{} configFilePath := filepath.Join(configDirectory, ConfigFileName) @@ -74,7 +75,7 @@ func ReadConfig(configDirectory string) (ClusterConfig, error) { return clusterConfig, fmt.Errorf("fail to unmarshal config file, details: %w", err) } - vlog.LogPrintInfo("The content of cluster config: %+v\n", clusterConfig) + log.PrintInfo("The content of cluster config: %+v\n", clusterConfig) return clusterConfig, nil } @@ -92,6 +93,23 @@ func (c *ClusterConfig) WriteConfig(configFilePath string) error { return nil } +// GetPathPrefix returns catalog, data, and depot prefixes +func (c *ClusterConfig) GetPathPrefix(dbName string) (catalogPrefix string, + dataPrefix string, depotPrefix string, err error) { + dbConfig, ok := (*c)[dbName] + if !ok { + return "", "", "", cannotFindDBFromConfigErr(dbName) + } + + if len(dbConfig.Nodes) == 0 { + return "", "", "", + fmt.Errorf("no node was found from the config file of %s", dbName) + } + + return dbConfig.Nodes[0].CatalogPath, dbConfig.Nodes[0].DataPath, + dbConfig.Nodes[0].DepotPath, nil +} + func (c *DatabaseConfig) GetHosts() []string { var hostList []string @@ -102,7 +120,7 @@ func (c *DatabaseConfig) GetHosts() []string { return hostList } -func GetConfigFilePath(dbName string, inputConfigDir *string) (string, error) { +func GetConfigFilePath(dbName string, inputConfigDir *string, log vlog.Printer) (string, error) { var configParentPath string // if the input config directory is given and has write permission, @@ -119,7 +137,7 @@ func GetConfigFilePath(dbName string, inputConfigDir *string) (string, error) { // as /vertica_cluster.yaml currentDir, err := os.Getwd() if err != nil { - vlog.LogWarning("Fail to get current directory\n") + log.Info("Fail to get current directory\n") configParentPath = currentDir } @@ -134,13 +152,13 @@ func GetConfigFilePath(dbName string, inputConfigDir *string) (string, error) { return configFilePath, nil } -func BackupConfigFile(configFilePath string) error { +func BackupConfigFile(configFilePath string, log vlog.Printer) error { if util.CanReadAccessDir(configFilePath) == nil { // copy file to vertica_cluster.yaml.backup configDirPath := filepath.Dir(configFilePath) configFileBackup := filepath.Join(configDirPath, ConfigBackupName) - vlog.LogInfo("Config file exists at %s, creating a backup at %s", - configFilePath, configFileBackup) + log.Info("Config file exists and, creating a backup", "config file", configFilePath, + "backup file", configFileBackup) err := util.CopyFile(configFilePath, configFileBackup, ConfigFilePerm) if err != nil { return err @@ -150,19 +168,19 @@ func BackupConfigFile(configFilePath string) error { return nil } -func RemoveConfigFile(configDirectory string) error { +func RemoveConfigFile(configDirectory string, log vlog.Printer) error { configFilePath := filepath.Join(configDirectory, ConfigFileName) configBackupPath := filepath.Join(configDirectory, ConfigBackupName) err := os.RemoveAll(configFilePath) if err != nil { - vlog.LogPrintError("Fail to remove the config file %s, detail: %s", configFilePath, err) + log.PrintError("Fail to remove the config file %s, detail: %s", configFilePath, err) return err } err = os.RemoveAll(configBackupPath) if err != nil { - vlog.LogPrintError("Fail to remove the backup config file %s, detail: %s", configBackupPath, err) + log.PrintError("Fail to remove the backup config file %s, detail: %s", configBackupPath, err) return err } diff --git a/vclusterops/cluster_op.go b/vclusterops/cluster_op.go index 868ebbf..a22c8e3 100644 --- a/vclusterops/cluster_op.go +++ b/vclusterops/cluster_op.go @@ -105,14 +105,14 @@ func (hostResult *HostHTTPResult) IsSuccess() bool { } // check only password and certificate for start_db -func (hostResult *HostHTTPResult) IsPasswordAndCertificateError() bool { +func (hostResult *HostHTTPResult) IsPasswordAndCertificateError(log vlog.Printer) bool { if !hostResult.IsUnauthorizedRequest() { return false } resultString := fmt.Sprintf("%v", hostResult) for _, msg := range wrongCredentialErrMsg { if strings.Contains(resultString, msg) { - vlog.LogError("the user has provided %s", msg) + log.Error(errors.New(msg), "the user has provided") return true } } @@ -202,9 +202,9 @@ func (op *OpBase) getName() string { } func (op *OpBase) parseAndCheckResponse(host, responseContent string, responseObj any) error { - err := util.GetJSONLogErrors(responseContent, &responseObj, op.name) + err := util.GetJSONLogErrors(responseContent, &responseObj, op.name, op.log) if err != nil { - op.log.Error(err, "fail to parse response on host %s", host) + op.log.Error(err, "fail to parse response on host, detail", "host", host) return err } op.log.Info("JSON response", "host", host, "responseObj", responseObj) @@ -244,7 +244,7 @@ func (op *OpBase) logFinalize() { func (op *OpBase) runExecute(execContext *OpEngineExecContext) error { err := execContext.dispatcher.sendRequest(&op.clusterHTTPRequest) if err != nil { - op.log.Error(err, "Fail to dispatch request %v", op.clusterHTTPRequest) + op.log.Error(err, "Fail to dispatch request, detail", "dispatch request", op.clusterHTTPRequest) return err } return nil @@ -300,7 +300,7 @@ func (op *OpBase) hasQuorum(hostCount, primaryNodeCount uint) bool { func (op *OpBase) checkResponseStatusCode(resp httpsResponseStatus, host string) (err error) { if resp.StatusCode != respSuccStatusCode { err = fmt.Errorf(`[%s] fail to execute HTTPS request on host %s, status code in HTTPS response is %d`, op.name, host, resp.StatusCode) - vlog.LogError(err.Error()) + op.log.Error(err, "fail to execute HTTPS request, detail") return err } return nil diff --git a/vclusterops/cluster_op_engine.go b/vclusterops/cluster_op_engine.go index c03e2aa..3c7793b 100644 --- a/vclusterops/cluster_op_engine.go +++ b/vclusterops/cluster_op_engine.go @@ -17,6 +17,8 @@ package vclusterops import ( "fmt" + + "github.com/vertica/vcluster/vclusterops/vlog" ) type VClusterOpEngine struct { @@ -36,8 +38,8 @@ func (opEngine *VClusterOpEngine) shouldGetCertsFromOptions() bool { return (opEngine.certs.key != "" && opEngine.certs.cert != "" && opEngine.certs.caCert != "") } -func (opEngine *VClusterOpEngine) Run() error { - execContext := MakeOpEngineExecContext() +func (opEngine *VClusterOpEngine) Run(log vlog.Printer) error { + execContext := MakeOpEngineExecContext(log) opEngine.execContext = &execContext findCertsInOptions := opEngine.shouldGetCertsFromOptions() diff --git a/vclusterops/cluster_op_engine_context.go b/vclusterops/cluster_op_engine_context.go index be0d5bf..82c8028 100644 --- a/vclusterops/cluster_op_engine_context.go +++ b/vclusterops/cluster_op_engine_context.go @@ -15,6 +15,8 @@ package vclusterops +import "github.com/vertica/vcluster/vclusterops/vlog" + type OpEngineExecContext struct { dispatcher HTTPRequestDispatcher networkProfiles map[string]NetworkProfile @@ -27,9 +29,9 @@ type OpEngineExecContext struct { dbInfo string // store the db info that retrieved from communal storage } -func MakeOpEngineExecContext() OpEngineExecContext { +func MakeOpEngineExecContext(log vlog.Printer) OpEngineExecContext { newOpEngineExecContext := OpEngineExecContext{} - newOpEngineExecContext.dispatcher = MakeHTTPRequestDispatcher() + newOpEngineExecContext.dispatcher = MakeHTTPRequestDispatcher(log) return newOpEngineExecContext } diff --git a/vclusterops/cluster_op_engine_test.go b/vclusterops/cluster_op_engine_test.go index 45587e9..d871909 100644 --- a/vclusterops/cluster_op_engine_test.go +++ b/vclusterops/cluster_op_engine_test.go @@ -20,6 +20,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/vertica/vcluster/vclusterops/vlog" ) type mockOp struct { @@ -74,7 +75,7 @@ func TestSkipExecuteOp(t *testing.T) { instructions := []ClusterOp{&opWithSkipDisabled, &opWithSkipEnabled} certs := HTTPSCerts{key: "key", cert: "cert", caCert: "ca-cert"} opEngn := MakeClusterOpEngine(instructions, &certs) - err := opEngn.Run() + err := opEngn.Run(vlog.Printer{}) assert.Equal(t, nil, err) assert.True(t, opWithSkipDisabled.calledPrepare) assert.True(t, opWithSkipDisabled.calledExecute) diff --git a/vclusterops/coordinator_database.go b/vclusterops/coordinator_database.go index 5dde814..177dfe0 100644 --- a/vclusterops/coordinator_database.go +++ b/vclusterops/coordinator_database.go @@ -22,6 +22,7 @@ import ( "strings" "github.com/vertica/vcluster/vclusterops/util" + "github.com/vertica/vcluster/vclusterops/vlog" "golang.org/x/exp/maps" ) @@ -71,9 +72,9 @@ func MakeVCoordinationDatabase() VCoordinationDatabase { return VCoordinationDatabase{} } -func (vdb *VCoordinationDatabase) SetFromCreateDBOptions(options *VCreateDatabaseOptions) error { +func (vdb *VCoordinationDatabase) SetFromCreateDBOptions(options *VCreateDatabaseOptions, log vlog.Printer) error { // build after validating the options - err := options.ValidateAnalyzeOptions() + err := options.ValidateAnalyzeOptions(log) if err != nil { return err } @@ -122,6 +123,19 @@ func (vdb *VCoordinationDatabase) SetFromCreateDBOptions(options *VCreateDatabas return nil } +// addNode adds a given host to the VDB's HostList and HostNodeMap. +// Duplicate host will not be added. +func (vdb *VCoordinationDatabase) addNode(vnode *VCoordinationNode) error { + if _, exist := vdb.HostNodeMap[vnode.Address]; exist { + return fmt.Errorf("host %s has already been in the VDB's HostList", vnode.Address) + } + + vdb.HostNodeMap[vnode.Address] = vnode + vdb.HostList = append(vdb.HostList, vnode.Address) + + return nil +} + // addHosts adds a given list of hosts to the VDB's HostList // and HostNodeMap. func (vdb *VCoordinationDatabase) addHosts(hosts []string) error { @@ -138,9 +152,11 @@ func (vdb *VCoordinationDatabase) addHosts(hosts []string) error { Address: host, Name: name, } - vNode.SetFromNodeConfig(nodeConfig, vdb) - vdb.HostList = append(vdb.HostList, host) - vdb.HostNodeMap[host] = &vNode + vNode.SetFromNodeConfig(&nodeConfig, vdb) + err := vdb.addNode(&vNode) + if err != nil { + return err + } } return nil @@ -150,16 +166,22 @@ func (vdb *VCoordinationDatabase) SetFromClusterConfig(dbName string, clusterConfig *ClusterConfig) error { // we trust the information in the config file // so we do not perform validation here + vdb.Name = dbName + + catalogPrefix, dataPrefix, depotPrefix, err := clusterConfig.GetPathPrefix(dbName) + if err != nil { + return err + } + vdb.CatalogPrefix = catalogPrefix + vdb.DataPrefix = dataPrefix + vdb.DepotPrefix = depotPrefix + dbConfig, ok := (*clusterConfig)[dbName] if !ok { return cannotFindDBFromConfigErr(dbName) } - - vdb.Name = dbName - vdb.CatalogPrefix = dbConfig.CatalogPath - vdb.DataPrefix = dbConfig.DataPath - vdb.DepotPrefix = dbConfig.DepotPath vdb.IsEon = dbConfig.IsEon + vdb.CommunalStorageLocation = dbConfig.CommunalStorageLocation vdb.Ipv6 = dbConfig.Ipv6 if vdb.DepotPrefix != "" { vdb.UseDepot = true @@ -169,8 +191,10 @@ func (vdb *VCoordinationDatabase) SetFromClusterConfig(dbName string, for _, nodeConfig := range dbConfig.Nodes { vnode := VCoordinationNode{} vnode.SetFromNodeConfig(nodeConfig, vdb) - vdb.HostNodeMap[vnode.Address] = &vnode - vdb.HostList = append(vdb.HostList, vnode.Address) + err = vdb.addNode(&vnode) + if err != nil { + return err + } } return nil @@ -383,7 +407,7 @@ func (vnode *VCoordinationNode) SetFromCreateDBOptions( return fmt.Errorf("fail to set up vnode from options: host %s does not exist in options", host) } -func (vnode *VCoordinationNode) SetFromNodeConfig(nodeConfig NodeConfig, vdb *VCoordinationDatabase) { +func (vnode *VCoordinationNode) SetFromNodeConfig(nodeConfig *NodeConfig, vdb *VCoordinationDatabase) { // we trust the information in the config file // so we do not perform validation here vnode.Address = nodeConfig.Address @@ -402,13 +426,10 @@ func (vnode *VCoordinationNode) SetFromNodeConfig(nodeConfig NodeConfig, vdb *VC } // WriteClusterConfig writes config information to a yaml file. -func (vdb *VCoordinationDatabase) WriteClusterConfig(configDir *string) error { +func (vdb *VCoordinationDatabase) WriteClusterConfig(configDir *string, log vlog.Printer) error { /* build config information */ dbConfig := MakeDatabaseConfig() - dbConfig.CatalogPath = vdb.CatalogPrefix - dbConfig.DataPath = vdb.DataPrefix - dbConfig.DepotPath = vdb.DepotPrefix // loop over HostList is needed as we want to preserve the order for _, host := range vdb.HostList { vnode, ok := vdb.HostNodeMap[host] @@ -419,9 +440,13 @@ func (vdb *VCoordinationDatabase) WriteClusterConfig(configDir *string) error { nodeConfig.Name = vnode.Name nodeConfig.Address = vnode.Address nodeConfig.Subcluster = vnode.Subcluster - dbConfig.Nodes = append(dbConfig.Nodes, nodeConfig) + nodeConfig.CatalogPath = vdb.CatalogPrefix + nodeConfig.DataPath = vdb.DataPrefix + nodeConfig.DepotPath = vdb.DepotPrefix + dbConfig.Nodes = append(dbConfig.Nodes, &nodeConfig) } dbConfig.IsEon = vdb.IsEon + dbConfig.CommunalStorageLocation = vdb.CommunalStorageLocation dbConfig.Ipv6 = vdb.Ipv6 clusterConfig := MakeClusterConfig() @@ -429,14 +454,14 @@ func (vdb *VCoordinationDatabase) WriteClusterConfig(configDir *string) error { /* write config to a YAML file */ - configFilePath, err := GetConfigFilePath(vdb.Name, configDir) + configFilePath, err := GetConfigFilePath(vdb.Name, configDir, log) if err != nil { return err } // if the config file exists already // create its backup before overwriting it - err = BackupConfigFile(configFilePath) + err = BackupConfigFile(configFilePath, log) if err != nil { return err } diff --git a/vclusterops/create_db.go b/vclusterops/create_db.go index c475e2f..04ec7ba 100644 --- a/vclusterops/create_db.go +++ b/vclusterops/create_db.go @@ -180,12 +180,12 @@ func (opt *VCreateDatabaseOptions) CheckExtraNilPointerParams() error { return nil } -func (opt *VCreateDatabaseOptions) validateRequiredOptions() error { +func (opt *VCreateDatabaseOptions) validateRequiredOptions(log vlog.Printer) error { // validate required parameters with default values if opt.Password == nil { opt.Password = new(string) *opt.Password = "" - vlog.LogPrintInfoln("no password specified, using none") + log.Info("no password specified, using none") } if !util.StringInArray(*opt.Policy, util.RestartPolicyList) { @@ -313,7 +313,7 @@ func (opt *VCreateDatabaseOptions) validateExtraOptions() error { return nil } -func (opt *VCreateDatabaseOptions) validateParseOptions() error { +func (opt *VCreateDatabaseOptions) validateParseOptions(log vlog.Printer) error { // check nil pointers in the required options err := opt.CheckNilPointerParams() if err != nil { @@ -321,13 +321,13 @@ func (opt *VCreateDatabaseOptions) validateParseOptions() error { } // validate base options - err = opt.ValidateBaseOptions("create_db") + err = opt.ValidateBaseOptions("create_db", log) if err != nil { return err } // batch 1: validate required parameters without default values - err = opt.validateRequiredOptions() + err = opt.validateRequiredOptions(log) if err != nil { return err } @@ -366,8 +366,8 @@ func (opt *VCreateDatabaseOptions) analyzeOptions() error { return nil } -func (opt *VCreateDatabaseOptions) ValidateAnalyzeOptions() error { - if err := opt.validateParseOptions(); err != nil { +func (opt *VCreateDatabaseOptions) ValidateAnalyzeOptions(log vlog.Printer) error { + if err := opt.validateParseOptions(log); err != nil { return err } return opt.analyzeOptions() @@ -383,7 +383,7 @@ func (vcc *VClusterCommands) VCreateDatabase(options *VCreateDatabaseOptions) (V */ // Analyze to produce vdb info, for later create db use and for cache db info vdb := MakeVCoordinationDatabase() - err := vdb.SetFromCreateDBOptions(options) + err := vdb.SetFromCreateDBOptions(options, vcc.Log) if err != nil { return vdb, err } @@ -399,9 +399,9 @@ func (vcc *VClusterCommands) VCreateDatabase(options *VCreateDatabaseOptions) (V clusterOpEngine := MakeClusterOpEngine(instructions, &certs) // Give the instructions to the VClusterOpEngine to run - err = clusterOpEngine.Run() + err = clusterOpEngine.Run(vcc.Log) if err != nil { - vcc.Log.Error(err, "fail to create database, %s") + vcc.Log.Error(err, "fail to create database") return vdb, err } return vdb, nil @@ -469,7 +469,7 @@ func (vcc *VClusterCommands) produceCreateDBBootstrapInstructions( nmaVerticaVersionOp := makeNMAVerticaVersionOp(vcc.Log, hosts, true) // need username for https operations - err := options.ValidateUserName(vcc) + err := options.ValidateUserName(vcc.Log) if err != nil { return instructions, err } diff --git a/vclusterops/create_db_test.go b/vclusterops/create_db_test.go index e55f746..0d3cbe7 100644 --- a/vclusterops/create_db_test.go +++ b/vclusterops/create_db_test.go @@ -24,6 +24,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/vertica/vcluster/vclusterops/util" + "github.com/vertica/vcluster/vclusterops/vlog" ) const defaultPath = "/data" @@ -78,7 +79,7 @@ func TestWriteClusterConfig(t *testing.T) { } vdb.IsEon = true - err := vdb.WriteClusterConfig(nil) + err := vdb.WriteClusterConfig(nil, vlog.Printer{}) assert.NoError(t, err) // compare the generated file with expected output @@ -88,7 +89,7 @@ func TestWriteClusterConfig(t *testing.T) { // now write the config file again // a backup file should be generated - err = vdb.WriteClusterConfig(nil) + err = vdb.WriteClusterConfig(nil, vlog.Printer{}) assert.NoError(t, err) err = util.CanReadAccessDir(dbName + "/" + ConfigBackupName) assert.NoError(t, err) diff --git a/vclusterops/drop_db.go b/vclusterops/drop_db.go index 7f32601..62f12c8 100644 --- a/vclusterops/drop_db.go +++ b/vclusterops/drop_db.go @@ -85,7 +85,7 @@ func (vcc *VClusterCommands) VDropDatabase(options *VDropDatabaseOptions) error configDir = currentDir } - clusterConfig, err := ReadConfig(configDir) + clusterConfig, err := ReadConfig(configDir, vcc.Log) if err != nil { return err } @@ -105,14 +105,14 @@ func (vcc *VClusterCommands) VDropDatabase(options *VDropDatabaseOptions) error clusterOpEngine := MakeClusterOpEngine(instructions, &certs) // give the instructions to the VClusterOpEngine to run - runError := clusterOpEngine.Run() + runError := clusterOpEngine.Run(vcc.Log) if runError != nil { return fmt.Errorf("fail to drop database: %w", runError) } // if the database is successfully dropped, the config file will be removed // if failed to remove it, we will ask users to manually do it - err = RemoveConfigFile(configDir) + err = RemoveConfigFile(configDir, vcc.Log) if err != nil { vcc.Log.PrintWarning("Fail to remove the config file(s), please manually clean up under directory %s", configDir) } @@ -136,7 +136,7 @@ func (vcc *VClusterCommands) produceDropDBInstructions(vdb *VCoordinationDatabas usePassword := false if options.Password != nil { usePassword = true - err := options.ValidateUserName(vcc) + err := options.ValidateUserName(vcc.Log) if err != nil { return instructions, err } diff --git a/vclusterops/fetch_node_state.go b/vclusterops/fetch_node_state.go index 30b03a9..7570fbc 100644 --- a/vclusterops/fetch_node_state.go +++ b/vclusterops/fetch_node_state.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/vertica/vcluster/vclusterops/util" - "github.com/vertica/vcluster/vclusterops/vlog" ) type VFetchNodeStateOptions struct { @@ -19,13 +18,13 @@ func VFetchNodeStateOptionsFactory() VFetchNodeStateOptions { return opt } -func (options *VFetchNodeStateOptions) validateParseOptions() error { +func (options *VFetchNodeStateOptions) validateParseOptions(vcc *VClusterCommands) error { if len(options.RawHosts) == 0 { return fmt.Errorf("must specify a host or host list") } if options.Password == nil { - vlog.LogPrintInfoln("no password specified, using none") + vcc.Log.PrintInfo("no password specified, using none") } return nil @@ -41,8 +40,8 @@ func (options *VFetchNodeStateOptions) analyzeOptions() error { return nil } -func (options *VFetchNodeStateOptions) ValidateAnalyzeOptions() error { - if err := options.validateParseOptions(); err != nil { +func (options *VFetchNodeStateOptions) ValidateAnalyzeOptions(vcc *VClusterCommands) error { + if err := options.validateParseOptions(vcc); err != nil { return err } return options.analyzeOptions() @@ -56,7 +55,7 @@ func (vcc *VClusterCommands) VFetchNodeState(options *VFetchNodeStateOptions) ([ * - Give the instructions to the VClusterOpEngine to run */ - err := options.ValidateAnalyzeOptions() + err := options.ValidateAnalyzeOptions(vcc) if err != nil { return nil, err } @@ -74,7 +73,7 @@ func (vcc *VClusterCommands) VFetchNodeState(options *VFetchNodeStateOptions) ([ clusterOpEngine := MakeClusterOpEngine(instructions, &certs) // Give the instructions to the VClusterOpEngine to run - runError := clusterOpEngine.Run() + runError := clusterOpEngine.Run(vcc.Log) nodeStates := clusterOpEngine.execContext.nodesInfo return nodeStates, runError @@ -92,7 +91,7 @@ func (vcc *VClusterCommands) produceListAllNodesInstructions(options *VFetchNode usePassword := false if options.Password != nil { usePassword = true - err := options.ValidateUserName(vcc) + err := options.ValidateUserName(vcc.Log) if err != nil { return instructions, err } diff --git a/vclusterops/helpers.go b/vclusterops/helpers.go index 2be5e7b..b997023 100644 --- a/vclusterops/helpers.go +++ b/vclusterops/helpers.go @@ -102,7 +102,7 @@ func getInitiatorHost(primaryUpNodes, hostsToSkip []string) (string, error) { // getVDBFromRunningDB will retrieve db configurations by calling https endpoints of a running db func (vcc *VClusterCommands) getVDBFromRunningDB(vdb *VCoordinationDatabase, options *DatabaseOptions) error { - err := options.SetUsePassword(vcc) + err := options.SetUsePassword(vcc.Log) if err != nil { return fmt.Errorf("fail to set userPassword while retrieving database configurations, %w", err) } @@ -124,7 +124,7 @@ func (vcc *VClusterCommands) getVDBFromRunningDB(vdb *VCoordinationDatabase, opt certs := HTTPSCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} clusterOpEngine := MakeClusterOpEngine(instructions, &certs) - err = clusterOpEngine.Run() + err = clusterOpEngine.Run(vcc.Log) if err != nil { return fmt.Errorf("fail to retrieve database configurations, %w", err) } diff --git a/vclusterops/http_adapter.go b/vclusterops/http_adapter.go index 89bf19e..3f0ea71 100644 --- a/vclusterops/http_adapter.go +++ b/vclusterops/http_adapter.go @@ -33,11 +33,15 @@ import ( ) type HTTPAdapter struct { + OpBase host string } -func MakeHTTPAdapter() HTTPAdapter { - return HTTPAdapter{} +func MakeHTTPAdapter(log vlog.Printer) HTTPAdapter { + newHTTPAdapter := HTTPAdapter{} + newHTTPAdapter.name = "HTTPAdapter" + newHTTPAdapter.log = log.WithName(newHTTPAdapter.name) + return newHTTPAdapter } const ( @@ -70,7 +74,7 @@ func (adapter *HTTPAdapter) sendRequest(request *HostHTTPRequest, resultChannel port, request.Endpoint, queryParams) - vlog.LogInfo("Request URL %s\n", requestURL) + adapter.log.Info("Request URL", "URL", requestURL) // whether use password (for HTTPS endpoints only) usePassword, err := whetherUsePassword(request) diff --git a/vclusterops/http_adapter_test.go b/vclusterops/http_adapter_test.go index 23a1fa4..bc51292 100644 --- a/vclusterops/http_adapter_test.go +++ b/vclusterops/http_adapter_test.go @@ -146,7 +146,6 @@ func TestHandleSuccessResponseCodes(t *testing.T) { func TestHandleRFC7807Response(t *testing.T) { adapter := HTTPAdapter{} rfcErr := rfc7807.New(rfc7807.CommunalAccessError). - WithStatus(500). WithDetail("Cannot access communal storage") b, err := json.Marshal(rfcErr) assert.Equal(t, err, nil) diff --git a/vclusterops/http_request_dispatcher.go b/vclusterops/http_request_dispatcher.go index a4ffb4e..1a20d3c 100644 --- a/vclusterops/http_request_dispatcher.go +++ b/vclusterops/http_request_dispatcher.go @@ -18,27 +18,30 @@ package vclusterops import "github.com/vertica/vcluster/vclusterops/vlog" type HTTPRequestDispatcher struct { + OpBase pool AdapterPool } -func MakeHTTPRequestDispatcher() HTTPRequestDispatcher { +func MakeHTTPRequestDispatcher(log vlog.Printer) HTTPRequestDispatcher { newHTTPRequestDispatcher := HTTPRequestDispatcher{} + newHTTPRequestDispatcher.name = "HTTPRequestDispatcher" + newHTTPRequestDispatcher.log = log.WithName(newHTTPRequestDispatcher.name) return newHTTPRequestDispatcher } // set up the pool connection for each host func (dispatcher *HTTPRequestDispatcher) Setup(hosts []string) { - dispatcher.pool = getPoolInstance() + dispatcher.pool = getPoolInstance(dispatcher.log) for _, host := range hosts { - adapter := MakeHTTPAdapter() + adapter := MakeHTTPAdapter(dispatcher.log) adapter.host = host dispatcher.pool.connections[host] = &adapter } } func (dispatcher *HTTPRequestDispatcher) sendRequest(clusterHTTPRequest *ClusterHTTPRequest) error { - vlog.LogInfoln("HTTP request dispatcher's sendRequest is called") + dispatcher.log.Info("HTTP request dispatcher's sendRequest is called") return dispatcher.pool.sendRequest(clusterHTTPRequest) } diff --git a/vclusterops/https_check_node_state_op.go b/vclusterops/https_check_node_state_op.go index 54fad70..abf2588 100644 --- a/vclusterops/https_check_node_state_op.go +++ b/vclusterops/https_check_node_state_op.go @@ -103,7 +103,7 @@ func (op *HTTPCheckNodeStateOp) processResult(execContext *OpEngineExecContext) if !result.isPassing() { // for any error, we continue to the next node if result.IsInternalError() { - vlog.LogPrintError("[%s] internal error of the /nodes endpoint: %s", op.name, result.content) + op.log.PrintError("[%s] internal error of the /nodes endpoint: %s", op.name, result.content) // At internal error originated from the server, so its a // response, just not a successful one. respondingNodeCount++ diff --git a/vclusterops/https_create_node_op.go b/vclusterops/https_create_node_op.go index 2e0b5ca..7388990 100644 --- a/vclusterops/https_create_node_op.go +++ b/vclusterops/https_create_node_op.go @@ -29,7 +29,7 @@ type HTTPSCreateNodeOp struct { RequestParams map[string]string } -func makeHTTPSCreateNodeOp(log vlog.Printer, hosts []string, bootstrapHost []string, +func makeHTTPSCreateNodeOp(log vlog.Printer, newNodeHosts []string, bootstrapHost []string, useHTTPPassword bool, userName string, httpsPassword *string, vdb *VCoordinationDatabase, scName string) (HTTPSCreateNodeOp, error) { createNodeOp := HTTPSCreateNodeOp{} @@ -40,7 +40,7 @@ func makeHTTPSCreateNodeOp(log vlog.Printer, hosts []string, bootstrapHost []str // HTTPS create node endpoint requires passing everything before node name createNodeOp.RequestParams["catalog-prefix"] = vdb.CatalogPrefix + "/" + vdb.Name createNodeOp.RequestParams["data-prefix"] = vdb.DataPrefix + "/" + vdb.Name - createNodeOp.RequestParams["hosts"] = util.ArrayToString(hosts, ",") + createNodeOp.RequestParams["hosts"] = util.ArrayToString(newNodeHosts, ",") if scName != "" { createNodeOp.RequestParams["subcluster"] = scName } diff --git a/vclusterops/https_get_nodes_info_op.go b/vclusterops/https_get_nodes_info_op.go index 4b9090e..2cd4a48 100644 --- a/vclusterops/https_get_nodes_info_op.go +++ b/vclusterops/https_get_nodes_info_op.go @@ -101,13 +101,13 @@ func (op *httpsGetNodesInfoOp) processResult(_ *OpEngineExecContext) error { } // save nodes info to vdb op.vdb.HostNodeMap = makeVHostNodeMap() + op.vdb.HostList = []string{} for _, node := range nodesStateInfo.NodeList { if node.Database != op.dbName { err = fmt.Errorf(`[%s] database %s is running on host %s, rather than database %s`, op.name, node.Database, host, op.dbName) allErrs = errors.Join(allErrs, err) return appendHTTPSFailureError(allErrs) } - op.vdb.HostList = append(op.vdb.HostList, node.Address) vNode := MakeVCoordinationNode() vNode.Name = node.Name vNode.Address = node.Address @@ -118,7 +118,11 @@ func (op *httpsGetNodesInfoOp) processResult(_ *OpEngineExecContext) error { if node.IsPrimary && node.State == util.NodeUpState { op.vdb.PrimaryUpNodes = append(op.vdb.PrimaryUpNodes, node.Address) } - op.vdb.HostNodeMap[node.Address] = &vNode + err := op.vdb.addNode(&vNode) + if err != nil { + allErrs = errors.Join(allErrs, err) + return appendHTTPSFailureError(allErrs) + } // extract catalog prefix from node's catalog path // catalog prefix is preceding db name dbPath := "/" + node.Database diff --git a/vclusterops/https_poll_node_state_op.go b/vclusterops/https_poll_node_state_op.go index 8b3cad6..7f0fdf9 100644 --- a/vclusterops/https_poll_node_state_op.go +++ b/vclusterops/https_poll_node_state_op.go @@ -157,7 +157,7 @@ func (op *HTTPSPollNodeStateOp) processResult(execContext *OpEngineExecContext) sort.Strings(op.notUpHosts) msg := fmt.Sprintf("The following hosts are not up after %d seconds: %v, details: %s", op.timeout, op.notUpHosts, err) - vlog.LogPrintError(msg) + op.log.PrintError(msg) return errors.New(msg) } return nil @@ -189,7 +189,7 @@ func (op *HTTPSPollNodeStateOp) shouldStopPolling() (bool, error) { // We don't need to wait until timeout to determine if all nodes are up or not. // If we find the wrong password for the HTTPS service on any hosts, we should fail immediately. // We also need to let user know to wait until all nodes are up - if result.IsPasswordAndCertificateError() { + if result.IsPasswordAndCertificateError(op.log) { switch op.cmdType { case StartDBCmd, RestartNodeCmd: op.log.PrintError("[%s] The credentials are incorrect. 'Catalog Sync' will not be executed.", diff --git a/vclusterops/https_poll_node_state_op_test.go b/vclusterops/https_poll_node_state_op_test.go index 866634c..203cd64 100644 --- a/vclusterops/https_poll_node_state_op_test.go +++ b/vclusterops/https_poll_node_state_op_test.go @@ -35,6 +35,6 @@ func TestTimeoutCase(t *testing.T) { certs := HTTPSCerts{} clusterOpEngine := MakeClusterOpEngine(instructions, &certs) - err = clusterOpEngine.Run() + err = clusterOpEngine.Run(vlog.Printer{}) assert.ErrorContains(t, err, "[HTTPSPollNodeStateOp] cannot connect to host 192.0.2.1, please check if the host is still alive") } diff --git a/vclusterops/https_poll_subscription_state_op.go b/vclusterops/https_poll_subscription_state_op.go index 5aa9348..db7ed06 100644 --- a/vclusterops/https_poll_subscription_state_op.go +++ b/vclusterops/https_poll_subscription_state_op.go @@ -133,7 +133,7 @@ func (op *httpsPollSubscriptionStateOp) shouldStopPolling() (bool, error) { for host, result := range op.clusterHTTPRequest.ResultCollection { op.logResponse(host, result) - if result.IsPasswordAndCertificateError() { + if result.IsPasswordAndCertificateError(op.log) { return true, fmt.Errorf("[%s] wrong password/certificate for https service on host %s", op.name, host) } diff --git a/vclusterops/nma_bootstrap_catalog_op.go b/vclusterops/nma_bootstrap_catalog_op.go index ed1d559..c11b6c4 100644 --- a/vclusterops/nma_bootstrap_catalog_op.go +++ b/vclusterops/nma_bootstrap_catalog_op.go @@ -140,7 +140,7 @@ func (op *NMABootstrapCatalogOp) updateRequestBody(execContext *OpEngineExecCont maskedData.maskSensitiveInfo() maskedRequestBodyMap[host] = maskedData } - op.log.Info("request data", "opName", op.name, "bodyMap", maskedRequestBodyMap) + op.log.Info("request data", "op name", op.name, "bodyMap", maskedRequestBodyMap) return nil } diff --git a/vclusterops/nma_download_file_op.go b/vclusterops/nma_download_file_op.go index f41a263..badedf0 100644 --- a/vclusterops/nma_download_file_op.go +++ b/vclusterops/nma_download_file_op.go @@ -237,8 +237,7 @@ func (op *NMADownloadFileOp) processResult(execContext *OpEngineExecContext) err } // save descFileContent in vdb - op.buildVDBFromClusterConfig(descFileContent) - return nil + return op.buildVDBFromClusterConfig(descFileContent) } httpsErr := errors.Join(fmt.Errorf("[%s] HTTPS call failed on host %s", op.name, host), result.err) @@ -249,10 +248,9 @@ func (op *NMADownloadFileOp) processResult(execContext *OpEngineExecContext) err } // buildVDBFromClusterConfig can build a vdb using cluster_config.json -func (op *NMADownloadFileOp) buildVDBFromClusterConfig(descFileContent fileContent) { +func (op *NMADownloadFileOp) buildVDBFromClusterConfig(descFileContent fileContent) error { op.vdb.HostNodeMap = makeVHostNodeMap() for _, node := range descFileContent.NodeList { - op.vdb.HostList = append(op.vdb.HostList, node.Address) vNode := MakeVCoordinationNode() vNode.Name = node.Name vNode.Address = node.Address @@ -284,8 +282,13 @@ func (op *NMADownloadFileOp) buildVDBFromClusterConfig(descFileContent fileConte } } - op.vdb.HostNodeMap[node.Address] = &vNode + err := op.vdb.addNode(&vNode) + if err != nil { + return err + } } + + return nil } func (op *NMADownloadFileOp) clusterLeaseCheck(clusterLeaseExpiration string) error { diff --git a/vclusterops/nma_prepare_directories_op.go b/vclusterops/nma_prepare_directories_op.go index 28a8e6e..c2a7aec 100644 --- a/vclusterops/nma_prepare_directories_op.go +++ b/vclusterops/nma_prepare_directories_op.go @@ -79,7 +79,7 @@ func (op *NMAPrepareDirectoriesOp) setupRequestBody(hostNodeMap vHostNodeMap) er op.hostRequestBodyMap[host] = string(dataBytes) } - op.log.Info("request data", "opName", op.name, "hostRequestBodyMap", op.hostRequestBodyMap) + op.log.Info("request data", "op name", op.name, "hostRequestBodyMap", op.hostRequestBodyMap) return nil } diff --git a/vclusterops/nma_re_ip_op.go b/vclusterops/nma_re_ip_op.go index 7454015..f4884dc 100644 --- a/vclusterops/nma_re_ip_op.go +++ b/vclusterops/nma_re_ip_op.go @@ -70,7 +70,7 @@ func (op *NMAReIPOp) updateRequestBody(_ *OpEngineExecContext) error { op.hostRequestBodyMap[host] = string(dataBytes) } - op.log.Info("request data", "opName", op.name, "hostRequestBodyMap", op.hostRequestBodyMap) + op.log.Info("request data", "op name", op.name, "hostRequestBodyMap", op.hostRequestBodyMap) return nil } diff --git a/vclusterops/nma_vertica_version_op.go b/vclusterops/nma_vertica_version_op.go index 5b5dc7c..a2a8518 100644 --- a/vclusterops/nma_vertica_version_op.go +++ b/vclusterops/nma_vertica_version_op.go @@ -81,7 +81,7 @@ func (op *NMAVerticaVersionOp) parseAndCheckResponse(host, resultContent string) // example result: // {"vertica_version": "Vertica Analytic Database v12.0.3"} var responseObj NMAVerticaVersionOpResponse - err := util.GetJSONLogErrors(resultContent, &responseObj, op.name) + err := util.GetJSONLogErrors(resultContent, &responseObj, op.name, op.log) if err != nil { return err } diff --git a/vclusterops/re_ip.go b/vclusterops/re_ip.go index e9d4d5e..8708f05 100644 --- a/vclusterops/re_ip.go +++ b/vclusterops/re_ip.go @@ -22,6 +22,7 @@ import ( "os" "github.com/vertica/vcluster/vclusterops/util" + "github.com/vertica/vcluster/vclusterops/vlog" ) type VReIPOptions struct { @@ -38,7 +39,7 @@ func VReIPFactory() VReIPOptions { return opt } -func (opt *VReIPOptions) validateParseOptions() error { +func (opt *VReIPOptions) validateParseOptions(log vlog.Printer) error { err := util.ValidateRequiredAbsPath(opt.CatalogPrefix, "catalog path") if err != nil { return err @@ -48,7 +49,7 @@ func (opt *VReIPOptions) validateParseOptions() error { return util.ValidateCommunalStorageLocation(*opt.CommunalStorageLocation) } - return opt.ValidateBaseOptions("re_ip") + return opt.ValidateBaseOptions("re_ip", log) } func (opt *VReIPOptions) analyzeOptions() error { @@ -61,8 +62,8 @@ func (opt *VReIPOptions) analyzeOptions() error { return nil } -func (opt *VReIPOptions) ValidateAnalyzeOptions() error { - if err := opt.validateParseOptions(); err != nil { +func (opt *VReIPOptions) ValidateAnalyzeOptions(log vlog.Printer) error { + if err := opt.validateParseOptions(log); err != nil { return err } if err := opt.analyzeOptions(); err != nil { @@ -114,7 +115,7 @@ func (vcc *VClusterCommands) VReIP(options *VReIPOptions) error { * - Give the instructions to the VClusterOpEngine to run */ - err := options.ValidateAnalyzeOptions() + err := options.ValidateAnalyzeOptions(vcc.Log) if err != nil { return err } @@ -148,7 +149,7 @@ func (vcc *VClusterCommands) VReIP(options *VReIPOptions) error { clusterOpEngine := MakeClusterOpEngine(instructions, &certs) // give the instructions to the VClusterOpEngine to run - runError := clusterOpEngine.Run() + runError := clusterOpEngine.Run(vcc.Log) if runError != nil { return fmt.Errorf("fail to re-ip: %w", runError) } diff --git a/vclusterops/re_ip_test.go b/vclusterops/re_ip_test.go index 2af6ec7..4066a21 100644 --- a/vclusterops/re_ip_test.go +++ b/vclusterops/re_ip_test.go @@ -20,28 +20,29 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/vertica/vcluster/vclusterops/vlog" "github.com/vertica/vcluster/vclusterops/vstruct" ) func TestReIPOptions(t *testing.T) { opt := VReIPFactory() - err := opt.ValidateAnalyzeOptions() + err := opt.ValidateAnalyzeOptions(vlog.Printer{}) assert.Error(t, err) *opt.DBName = "test_db" opt.RawHosts = []string{"192.168.1.101", "192.168.1.102"} - err = opt.ValidateAnalyzeOptions() + err = opt.ValidateAnalyzeOptions(vlog.Printer{}) assert.ErrorContains(t, err, "must specify an absolute catalog path") *opt.CatalogPrefix = "/data" - err = opt.ValidateAnalyzeOptions() + err = opt.ValidateAnalyzeOptions(vlog.Printer{}) assert.ErrorContains(t, err, "the re-ip list is not provided") var info ReIPInfo info.NodeAddress = "192.168.1.102" info.TargetAddress = "192.168.1.103" opt.ReIPList = append(opt.ReIPList, info) - err = opt.ValidateAnalyzeOptions() + err = opt.ValidateAnalyzeOptions(vlog.Printer{}) assert.NoError(t, err) } diff --git a/vclusterops/remove_node.go b/vclusterops/remove_node.go index 61e6ab9..51a4597 100644 --- a/vclusterops/remove_node.go +++ b/vclusterops/remove_node.go @@ -21,6 +21,7 @@ import ( "strings" "github.com/vertica/vcluster/vclusterops/util" + "github.com/vertica/vcluster/vclusterops/vlog" ) // VRemoveNodeOptions are the option arguments for the VRemoveNode API @@ -63,8 +64,8 @@ func (o *VRemoveNodeOptions) ParseHostToRemoveList(hosts string) error { return nil } -func (o *VRemoveNodeOptions) validateRequiredOptions() error { - err := o.ValidateBaseOptions("db_remove_node") +func (o *VRemoveNodeOptions) validateRequiredOptions(log vlog.Printer) error { + err := o.ValidateBaseOptions("db_remove_node", log) if err != nil { return err } @@ -79,9 +80,9 @@ func (o *VRemoveNodeOptions) validateExtraOptions() error { return util.ValidateRequiredAbsPath(o.DataPrefix, "data path") } -func (o *VRemoveNodeOptions) validateParseOptions() error { +func (o *VRemoveNodeOptions) validateParseOptions(log vlog.Printer) error { // batch 1: validate required params - err := o.validateRequiredOptions() + err := o.validateRequiredOptions(log) if err != nil { return err } @@ -107,22 +108,22 @@ func (o *VRemoveNodeOptions) analyzeOptions() (err error) { return nil } -func (o *VRemoveNodeOptions) validateAnalyzeOptions(vcc *VClusterCommands) error { - if err := o.validateParseOptions(); err != nil { +func (o *VRemoveNodeOptions) validateAnalyzeOptions(log vlog.Printer) error { + if err := o.validateParseOptions(log); err != nil { return err } err := o.analyzeOptions() if err != nil { return err } - return o.SetUsePassword(vcc) + return o.SetUsePassword(log) } func (vcc *VClusterCommands) VRemoveNode(options *VRemoveNodeOptions) (VCoordinationDatabase, error) { vdb := MakeVCoordinationDatabase() // validate and analyze options - err := options.validateAnalyzeOptions(vcc) + err := options.validateAnalyzeOptions(vcc.Log) if err != nil { return vdb, err } @@ -169,7 +170,7 @@ func (vcc *VClusterCommands) VRemoveNode(options *VRemoveNodeOptions) (VCoordina certs := HTTPSCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} clusterOpEngine := MakeClusterOpEngine(instructions, &certs) - if runError := clusterOpEngine.Run(); runError != nil { + if runError := clusterOpEngine.Run(vcc.Log); runError != nil { return vdb, fmt.Errorf("fail to complete remove node operation, %w", runError) } diff --git a/vclusterops/remove_subcluster.go b/vclusterops/remove_subcluster.go index cd435bd..65b3a8c 100644 --- a/vclusterops/remove_subcluster.go +++ b/vclusterops/remove_subcluster.go @@ -21,6 +21,7 @@ import ( "github.com/vertica/vcluster/rfc7807" "github.com/vertica/vcluster/vclusterops/util" + "github.com/vertica/vcluster/vclusterops/vlog" ) // VRemoveScOptions are the option arguments for the VRemoveSubcluster API @@ -45,8 +46,8 @@ func (o *VRemoveScOptions) setDefaultValues() { o.ForceDelete = new(bool) } -func (o *VRemoveScOptions) validateRequiredOptions() error { - err := o.ValidateBaseOptions("db_remove_subcluster") +func (o *VRemoveScOptions) validateRequiredOptions(log vlog.Printer) error { + err := o.ValidateBaseOptions("db_remove_subcluster", log) if err != nil { return err } @@ -70,8 +71,8 @@ func (o *VRemoveScOptions) validatePathOptions() error { return util.ValidateRequiredAbsPath(o.DepotPrefix, "depot path") } -func (o *VRemoveScOptions) validateParseOptions() error { - err := o.validateRequiredOptions() +func (o *VRemoveScOptions) validateParseOptions(log vlog.Printer) error { + err := o.validateRequiredOptions(log) if err != nil { return err } @@ -92,15 +93,15 @@ func (o *VRemoveScOptions) analyzeOptions() (err error) { return nil } -func (o *VRemoveScOptions) validateAnalyzeOptions(vcc *VClusterCommands) error { - if err := o.validateParseOptions(); err != nil { +func (o *VRemoveScOptions) validateAnalyzeOptions(log vlog.Printer) error { + if err := o.validateParseOptions(log); err != nil { return err } err := o.analyzeOptions() if err != nil { return err } - return o.SetUsePassword(vcc) + return o.SetUsePassword(log) } /* @@ -115,7 +116,7 @@ func (vcc *VClusterCommands) VRemoveSubcluster(removeScOpt *VRemoveScOptions) (V // VER-88594: read config file (may move this part to cmd_remove_subcluster) // validate and analyze options - err := removeScOpt.validateAnalyzeOptions(vcc) + err := removeScOpt.validateAnalyzeOptions(vcc.Log) if err != nil { return vdb, err } @@ -212,7 +213,7 @@ func (vcc *VClusterCommands) removeScPreCheck(vdb *VCoordinationDatabase, option certs := HTTPSCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} clusterOpEngine := MakeClusterOpEngine(instructions, &certs) - err = clusterOpEngine.Run() + err = clusterOpEngine.Run(vcc.Log) if err != nil { // VER-88585 will improve this rfc error flow if strings.Contains(err.Error(), "does not exist in the database") { @@ -262,7 +263,7 @@ func (vcc *VClusterCommands) dropSubcluster(vdb *VCoordinationDatabase, options certs := HTTPSCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} clusterOpEngine := MakeClusterOpEngine(instructions, &certs) - err = clusterOpEngine.Run() + err = clusterOpEngine.Run(vcc.Log) if err != nil { vcc.Log.Error(err, "fail to drop subcluster, details: %v", dropScErrMsg) return err diff --git a/vclusterops/remove_subcluster_test.go b/vclusterops/remove_subcluster_test.go index 2de8bef..c39c8cc 100644 --- a/vclusterops/remove_subcluster_test.go +++ b/vclusterops/remove_subcluster_test.go @@ -19,6 +19,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/vertica/vcluster/vclusterops/vlog" ) const dbName = "test_db" @@ -30,27 +31,27 @@ func TestRemoveSubcluster(t *testing.T) { options.Password = new(string) // options without db name, sc name, data path, and depot path - err := options.validateParseOptions() + err := options.validateParseOptions(vlog.Printer{}) assert.ErrorContains(t, err, "must specify a database name") // input db name *options.DBName = dbName - err = options.validateParseOptions() + err = options.validateParseOptions(vlog.Printer{}) assert.ErrorContains(t, err, "must specify a subcluster name") // input sc name options.SubclusterToRemove = new(string) *options.SubclusterToRemove = "sc1" - err = options.validateParseOptions() + err = options.validateParseOptions(vlog.Printer{}) assert.ErrorContains(t, err, "must specify an absolute data path") // input data path *options.DataPrefix = defaultPath - err = options.validateParseOptions() + err = options.validateParseOptions(vlog.Printer{}) assert.ErrorContains(t, err, "must specify an absolute depot path") // input depot path *options.DepotPrefix = defaultPath - err = options.validateParseOptions() + err = options.validateParseOptions(vlog.Printer{}) assert.NoError(t, err) } diff --git a/vclusterops/restart_node.go b/vclusterops/restart_node.go index ce3b36b..f4dbc68 100644 --- a/vclusterops/restart_node.go +++ b/vclusterops/restart_node.go @@ -55,8 +55,8 @@ func (options *VRestartNodesOptions) setDefaultValues() { options.DatabaseOptions.SetDefaultValues() } -func (options *VRestartNodesOptions) validateRequiredOptions() error { - err := options.ValidateBaseOptions("restart_node") +func (options *VRestartNodesOptions) validateRequiredOptions(log vlog.Printer) error { + err := options.ValidateBaseOptions("restart_node", log) if err != nil { return err } @@ -67,8 +67,8 @@ func (options *VRestartNodesOptions) validateRequiredOptions() error { return nil } -func (options *VRestartNodesOptions) validateParseOptions() error { - return options.validateRequiredOptions() +func (options *VRestartNodesOptions) validateParseOptions(log vlog.Printer) error { + return options.validateRequiredOptions(log) } // analyzeOptions will modify some options based on what is chosen @@ -102,8 +102,8 @@ func (options *VRestartNodesOptions) ParseNodesList(nodeListStr string) error { return nil } -func (options *VRestartNodesOptions) ValidateAnalyzeOptions() error { - if err := options.validateParseOptions(); err != nil { +func (options *VRestartNodesOptions) ValidateAnalyzeOptions(log vlog.Printer) error { + if err := options.validateParseOptions(log); err != nil { return err } return options.analyzeOptions() @@ -120,7 +120,7 @@ func (vcc *VClusterCommands) VRestartNodes(options *VRestartNodesOptions) error */ // validate and analyze options - err := options.ValidateAnalyzeOptions() + err := options.ValidateAnalyzeOptions(vcc.Log) if err != nil { return err } @@ -155,7 +155,7 @@ func (vcc *VClusterCommands) VRestartNodes(options *VRestartNodesOptions) error for nodename, newIP := range options.Nodes { oldIP, ok := hostNodeNameMap[nodename] if !ok { - vlog.LogPrintError("fail to provide a non-existent node name %s", nodename) + vcc.Log.PrintError("fail to provide a non-existent node name %s", nodename) return fmt.Errorf("the node with the provided name %s does not exist", nodename) } // if the IP that is given is different than the IP in the catalog, a re-ip is necessary @@ -184,7 +184,7 @@ func (vcc *VClusterCommands) VRestartNodes(options *VRestartNodesOptions) error clusterOpEngine := MakeClusterOpEngine(instructions, &certs) // Give the instructions to the VClusterOpEngine to run - err = clusterOpEngine.Run() + err = clusterOpEngine.Run(vcc.Log) if err != nil { return fmt.Errorf("fail to restart node, %w", err) } @@ -217,7 +217,7 @@ func (vcc *VClusterCommands) produceRestartNodesInstructions(restartNodeInfo *VR // require to have the same vertica version nmaVerticaVersionOp := makeNMAVerticaVersionOp(vcc.Log, options.Hosts, true) // need username for https operations - err := options.SetUsePassword(vcc) + err := options.SetUsePassword(vcc.Log) if err != nil { return instructions, err } diff --git a/vclusterops/revive_db.go b/vclusterops/revive_db.go index ba67473..f07c440 100644 --- a/vclusterops/revive_db.go +++ b/vclusterops/revive_db.go @@ -125,7 +125,7 @@ func (vcc *VClusterCommands) VReviveDatabase(options *VReviveDatabaseOptions) (d certs := HTTPSCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} // feed the pre-revive db instructions to the VClusterOpEngine clusterOpEngine := MakeClusterOpEngine(preReviveDBInstructions, &certs) - err = clusterOpEngine.Run() + err = clusterOpEngine.Run(vcc.Log) if err != nil { return dbInfo, fmt.Errorf("fail to collect the information of database in revive_db %w", err) } @@ -143,7 +143,7 @@ func (vcc *VClusterCommands) VReviveDatabase(options *VReviveDatabaseOptions) (d // feed revive db instructions to the VClusterOpEngine clusterOpEngine = MakeClusterOpEngine(reviveDBInstructions, &certs) - err = clusterOpEngine.Run() + err = clusterOpEngine.Run(vcc.Log) if err != nil { return dbInfo, fmt.Errorf("fail to revive database %w", err) } diff --git a/vclusterops/start_db.go b/vclusterops/start_db.go index c026846..1aae413 100644 --- a/vclusterops/start_db.go +++ b/vclusterops/start_db.go @@ -19,6 +19,7 @@ import ( "fmt" "github.com/vertica/vcluster/vclusterops/util" + "github.com/vertica/vcluster/vclusterops/vlog" ) // Normal strings are easier and safer to use in Go. @@ -41,8 +42,8 @@ func (options *VStartDatabaseOptions) SetDefaultValues() { options.DatabaseOptions.SetDefaultValues() } -func (options *VStartDatabaseOptions) validateRequiredOptions() error { - err := options.ValidateBaseOptions("start_db") +func (options *VStartDatabaseOptions) validateRequiredOptions(log vlog.Printer) error { + err := options.ValidateBaseOptions("start_db", log) if err != nil { return err } @@ -65,9 +66,9 @@ func (options *VStartDatabaseOptions) validateEonOptions() error { return nil } -func (options *VStartDatabaseOptions) validateParseOptions() error { +func (options *VStartDatabaseOptions) validateParseOptions(log vlog.Printer) error { // batch 1: validate required parameters - err := options.validateRequiredOptions() + err := options.validateRequiredOptions(log) if err != nil { return err } @@ -87,8 +88,8 @@ func (options *VStartDatabaseOptions) analyzeOptions() (err error) { return nil } -func (options *VStartDatabaseOptions) ValidateAnalyzeOptions() error { - if err := options.validateParseOptions(); err != nil { +func (options *VStartDatabaseOptions) ValidateAnalyzeOptions(log vlog.Printer) error { + if err := options.validateParseOptions(log); err != nil { return err } return options.analyzeOptions() @@ -101,7 +102,7 @@ func (vcc *VClusterCommands) VStartDatabase(options *VStartDatabaseOptions) erro * - Give the instructions to the VClusterOpEngine to run */ - err := options.ValidateAnalyzeOptions() + err := options.ValidateAnalyzeOptions(vcc.Log) if err != nil { return err } @@ -159,7 +160,7 @@ func (vcc *VClusterCommands) VStartDatabase(options *VStartDatabaseOptions) erro clusterOpEngine := MakeClusterOpEngine(instructions, &certs) // Give the instructions to the VClusterOpEngine to run - runError := clusterOpEngine.Run() + runError := clusterOpEngine.Run(vcc.Log) if runError != nil { return fmt.Errorf("fail to start database: %w", runError) } @@ -187,7 +188,7 @@ func (vcc *VClusterCommands) produceStartDBInstructions(options *VStartDatabaseO // require to have the same vertica version nmaVerticaVersionOp := makeNMAVerticaVersionOp(vcc.Log, options.Hosts, true) // need username for https operations - err := options.SetUsePassword(vcc) + err := options.SetUsePassword(vcc.Log) if err != nil { return instructions, err } diff --git a/vclusterops/stop_db.go b/vclusterops/stop_db.go index a97de13..53fe56c 100644 --- a/vclusterops/stop_db.go +++ b/vclusterops/stop_db.go @@ -56,8 +56,8 @@ func (options *VStopDatabaseOptions) SetDefaultValues() { options.ForceKill = new(bool) } -func (options *VStopDatabaseOptions) validateRequiredOptions() error { - err := options.ValidateBaseOptions("stop_db") +func (options *VStopDatabaseOptions) validateRequiredOptions(log vlog.Printer) error { + err := options.ValidateBaseOptions("stop_db", log) if err != nil { return err } @@ -65,7 +65,7 @@ func (options *VStopDatabaseOptions) validateRequiredOptions() error { return nil } -func (options *VStopDatabaseOptions) validateEonOptions(config *ClusterConfig) error { +func (options *VStopDatabaseOptions) validateEonOptions(config *ClusterConfig, log vlog.Printer) error { // if db is enterprise db and we see --drain-seconds, we will ignore it isEon, err := options.IsEonMode(config) if err != nil { @@ -74,7 +74,7 @@ func (options *VStopDatabaseOptions) validateEonOptions(config *ClusterConfig) e if !isEon { if options.DrainSeconds != nil { - vlog.LogPrintInfoln("Notice: --drain-seconds option will be ignored because database is in enterprise mode." + + log.PrintInfo("Notice: --drain-seconds option will be ignored because database is in enterprise mode." + " Connection draining is only available in eon mode.") } options.DrainSeconds = nil @@ -90,14 +90,14 @@ func (options *VStopDatabaseOptions) validateExtraOptions() error { return nil } -func (options *VStopDatabaseOptions) validateParseOptions(config *ClusterConfig) error { +func (options *VStopDatabaseOptions) validateParseOptions(config *ClusterConfig, log vlog.Printer) error { // batch 1: validate required parameters - err := options.validateRequiredOptions() + err := options.validateRequiredOptions(log) if err != nil { return err } // batch 2: validate eon params - err = options.validateEonOptions(config) + err = options.validateEonOptions(config, log) if err != nil { return err } @@ -123,8 +123,8 @@ func (options *VStopDatabaseOptions) analyzeOptions() (err error) { return nil } -func (options *VStopDatabaseOptions) ValidateAnalyzeOptions(config *ClusterConfig) error { - if err := options.validateParseOptions(config); err != nil { +func (options *VStopDatabaseOptions) ValidateAnalyzeOptions(config *ClusterConfig, log vlog.Printer) error { + if err := options.validateParseOptions(config, log); err != nil { return err } return options.analyzeOptions() @@ -137,7 +137,7 @@ func (vcc *VClusterCommands) VStopDatabase(options *VStopDatabaseOptions) error * - Give the instructions to the VClusterOpEngine to run */ - err := options.ValidateAnalyzeOptions(options.Config) + err := options.ValidateAnalyzeOptions(options.Config, vcc.Log) if err != nil { return err } @@ -167,7 +167,7 @@ func (vcc *VClusterCommands) VStopDatabase(options *VStopDatabaseOptions) error clusterOpEngine := MakeClusterOpEngine(instructions, &certs) // Give the instructions to the VClusterOpEngine to run - runError := clusterOpEngine.Run() + runError := clusterOpEngine.Run(vcc.Log) if runError != nil { return fmt.Errorf("fail to stop database: %w", runError) } @@ -193,7 +193,7 @@ func (vcc *VClusterCommands) produceStopDBInstructions(stopDBInfo *VStopDatabase usePassword := false if stopDBInfo.Password != nil { usePassword = true - err := options.ValidateUserName(vcc) + err := options.ValidateUserName(vcc.Log) if err != nil { return instructions, err } @@ -213,7 +213,7 @@ func (vcc *VClusterCommands) produceStopDBInstructions(stopDBInfo *VStopDatabase } instructions = append(instructions, &httpsSyncCatalogOp) } else { - vlog.LogPrintInfoln("Skipping sync catalog for an enterprise database") + vcc.Log.PrintInfo("Skipping sync catalog for an enterprise database") } httpsStopDBOp, err := makeHTTPSStopDBOp(vcc.Log, usePassword, *options.UserName, stopDBInfo.Password, stopDBInfo.DrainSeconds) diff --git a/vclusterops/test_data/vertica_cluster.yaml b/vclusterops/test_data/vertica_cluster.yaml index f0453c9..929f02b 100644 --- a/vclusterops/test_data/vertica_cluster.yaml +++ b/vclusterops/test_data/vertica_cluster.yaml @@ -3,14 +3,21 @@ practice_db: - name: node_name_1 address: ip_1 subcluster: default_subcluster + catalog_path: /data + data_path: /data + depot_path: /data - name: node_name_2 address: ip_2 subcluster: default_subcluster + catalog_path: /data + data_path: /data + depot_path: /data - name: node_name_3 address: ip_3 subcluster: default_subcluster - catalog_path: /data - data_path: /data - depot_path: /data + catalog_path: /data + data_path: /data + depot_path: /data eon_mode: true + communal_storage_location: "" ipv6: false diff --git a/vclusterops/util/util.go b/vclusterops/util/util.go index 411a548..50a8666 100644 --- a/vclusterops/util/util.go +++ b/vclusterops/util/util.go @@ -43,7 +43,7 @@ const ( AWSAuthKey = "awsauth" ) -func GetJSONLogErrors(responseContent string, responseObj any, opName string) error { +func GetJSONLogErrors(responseContent string, responseObj any, opName string, logger vlog.Printer) error { err := json.Unmarshal([]byte(responseContent), responseObj) if err != nil { opTag := "" @@ -51,7 +51,7 @@ func GetJSONLogErrors(responseContent string, responseObj any, opName string) er opTag = fmt.Sprintf("[%s] ", opName) } - vlog.LogError(opTag+"fail to unmarshal the response content, details: %v\n", err) + logger.Error(err, "op name", opTag, "fail to unmarshal the response content") return err } diff --git a/vclusterops/util/util_test.go b/vclusterops/util/util_test.go index e738fe0..fc5c782 100644 --- a/vclusterops/util/util_test.go +++ b/vclusterops/util/util_test.go @@ -28,14 +28,14 @@ import ( type NMAHealthOpResponse map[string]string -func redirectLog() *bytes.Buffer { +func redirectLog() (*bytes.Buffer, vlog.Printer) { // redirect log to a local bytes.Buffer var logBuffer bytes.Buffer log := buflogr.NewWithBuffer(&logBuffer) - vlogger := vlog.GetGlobalLogger() + vlogger := vlog.Printer{} vlogger.Log = log - return &logBuffer + return &logBuffer, vlogger } func TestGetJSONLogErrors(t *testing.T) { @@ -45,24 +45,26 @@ func TestGetJSONLogErrors(t *testing.T) { var responseObj NMAHealthOpResponse expectedResponseObj := NMAHealthOpResponse{"healthy": "true"} - err := GetJSONLogErrors(resultContent, &responseObj, "") + err := GetJSONLogErrors(resultContent, &responseObj, "", vlog.Printer{}) assert.Nil(t, err) assert.Equal(t, responseObj, expectedResponseObj) /* netative case */ - logBuffer := redirectLog() + logBuffer, log := redirectLog() resultContent = `{"healthy": 123}` - err = GetJSONLogErrors(resultContent, &responseObj, "") + err = GetJSONLogErrors(resultContent, &responseObj, "", log) assert.NotNil(t, err) - assert.Contains(t, logBuffer.String(), "ERROR fail to unmarshal the response content") + assert.Contains(t, logBuffer.String(), + "ERROR json: cannot unmarshal number into Go value of type string op name fail to unmarshal the response content") - err = GetJSONLogErrors(resultContent, &responseObj, "NMAHealthOp") + err = GetJSONLogErrors(resultContent, &responseObj, "NMAHealthOp", log) assert.NotNil(t, err) - assert.Contains(t, logBuffer.String(), "ERROR [NMAHealthOp] fail to unmarshal the response content") + assert.Contains(t, logBuffer.String(), + "ERROR json: cannot unmarshal number into Go value of type string op name [NMAHealthOp] fail to unmarshal the response content") } func TestStringInArray(t *testing.T) { diff --git a/vclusterops/vcluster_database_options.go b/vclusterops/vcluster_database_options.go index de57ee5..c7d0a7d 100644 --- a/vclusterops/vcluster_database_options.go +++ b/vclusterops/vcluster_database_options.go @@ -103,7 +103,9 @@ func (opt *DatabaseOptions) CheckNilPointerParams() error { return nil } -func (opt *DatabaseOptions) ValidateBaseOptions(commandName string) error { +func (opt *DatabaseOptions) ValidateBaseOptions(commandName string, log vlog.Printer) error { + // get vcluster commands + log.WithName(commandName) // database name if *opt.DBName == "" { return fmt.Errorf("must specify a database name") @@ -114,7 +116,7 @@ func (opt *DatabaseOptions) ValidateBaseOptions(commandName string) error { } // raw hosts and password - err = opt.ValidateHostsAndPwd(commandName) + err = opt.ValidateHostsAndPwd(commandName, log) if err != nil { return err } @@ -141,7 +143,7 @@ func (opt *DatabaseOptions) ValidateBaseOptions(commandName string) error { } // ValidateHostsAndPwd will validate raw hosts and password -func (opt *DatabaseOptions) ValidateHostsAndPwd(commandName string) error { +func (opt *DatabaseOptions) ValidateHostsAndPwd(commandName string, log vlog.Printer) error { // when we create db, we need hosts and set password to "" if user did not provide one if commandName == commandCreateDB { // raw hosts @@ -152,18 +154,18 @@ func (opt *DatabaseOptions) ValidateHostsAndPwd(commandName string) error { if opt.Password == nil { opt.Password = new(string) *opt.Password = "" - vlog.LogPrintInfoln("no password specified, using none") + log.PrintInfo("no password specified, using none") } } else { // for other commands, we validate hosts when HonorUserInput is set, otherwise we use hosts in config file if *opt.HonorUserInput { if len(opt.RawHosts) == 0 { - vlog.LogPrintInfo("no hosts specified, try to use the hosts in %s", ConfigFileName) + log.PrintInfo("no hosts specified, try to use the hosts in %s", ConfigFileName) } } // for other commands, we will not use "" as password if opt.Password == nil { - vlog.LogPrintInfoln("no password specified, using none") + log.PrintInfo("no password specified, using none") } } return nil @@ -235,7 +237,7 @@ func (opt *DatabaseOptions) ParseHostList(hosts string) error { return nil } -func (opt *DatabaseOptions) ValidateUserName(vcc *VClusterCommands) error { +func (opt *DatabaseOptions) ValidateUserName(log vlog.Printer) error { if *opt.UserName == "" { username, err := util.GetCurrentUsername() if err != nil { @@ -243,18 +245,18 @@ func (opt *DatabaseOptions) ValidateUserName(vcc *VClusterCommands) error { } *opt.UserName = username } - vcc.Log.Info("Current username", "username", *opt.UserName) + log.Info("Current username", "username", *opt.UserName) return nil } -func (opt *DatabaseOptions) SetUsePassword(vcc *VClusterCommands) error { +func (opt *DatabaseOptions) SetUsePassword(log vlog.Printer) error { // when password is specified, // we will use username/password to call https endpoints opt.usePassword = false if opt.Password != nil { opt.usePassword = true - err := opt.ValidateUserName(vcc) + err := opt.ValidateUserName(log) if err != nil { return err } @@ -332,19 +334,19 @@ func (opt *DatabaseOptions) GetHosts(config *ClusterConfig) (hosts []string, err } // GetCatalogPrefix can choose the right catalog prefix from user input and config file -func (opt *DatabaseOptions) GetCatalogPrefix(config *ClusterConfig) (catalogPrefix *string, err error) { +func (opt *DatabaseOptions) GetCatalogPrefix(clusterConfig *ClusterConfig) (catalogPrefix *string, err error) { // when config file is not available, we use user input // HonorUserInput must be true at this time, otherwise vcluster has stopped when it cannot find the config file - if config == nil { + if clusterConfig == nil { return opt.CatalogPrefix, nil } - dbConfig, ok := (*config)[*opt.DBName] - if !ok { - return nil, cannotFindDBFromConfigErr(*opt.DBName) + catalogPrefix = new(string) + *catalogPrefix, _, _, err = clusterConfig.GetPathPrefix(*opt.DBName) + if err != nil { + return catalogPrefix, err } - catalogPrefix = &dbConfig.CatalogPath // if HonorUserInput is set, we choose the user input if *opt.CatalogPrefix != "" && *opt.HonorUserInput { catalogPrefix = opt.CatalogPrefix @@ -354,18 +356,16 @@ func (opt *DatabaseOptions) GetCatalogPrefix(config *ClusterConfig) (catalogPref // getDepotAndDataPrefix chooses the right depot/data prefix from user input and config file. func (opt *DatabaseOptions) getDepotAndDataPrefix( - config *ClusterConfig) (depotPrefix, dataPrefix string, err error) { - if config == nil { + clusterConfig *ClusterConfig) (depotPrefix, dataPrefix string, err error) { + if clusterConfig == nil { return *opt.DepotPrefix, *opt.DataPrefix, nil } - dbConfig, ok := (*config)[*opt.DBName] - if !ok { - return depotPrefix, dataPrefix, cannotFindDBFromConfigErr(*opt.DBName) + _, dataPrefix, depotPrefix, err = clusterConfig.GetPathPrefix(*opt.DBName) + if err != nil { + return "", "", err } - depotPrefix = dbConfig.DepotPath - dataPrefix = dbConfig.DataPath // if HonorUserInput is set, we choose the user input if !*opt.HonorUserInput { return depotPrefix, dataPrefix, nil @@ -380,7 +380,7 @@ func (opt *DatabaseOptions) getDepotAndDataPrefix( } // GetDBConfig can read database configurations from vertica_cluster.yaml to the struct ClusterConfig -func (opt *DatabaseOptions) GetDBConfig() (config *ClusterConfig, e error) { +func (opt *DatabaseOptions) GetDBConfig(vcc VClusterCommands) (config *ClusterConfig, e error) { var configDir string if opt.ConfigDirectory == nil && !*opt.HonorUserInput { @@ -398,13 +398,13 @@ func (opt *DatabaseOptions) GetDBConfig() (config *ClusterConfig, e error) { } if configDir != "" { - configContent, err := ReadConfig(configDir) + configContent, err := ReadConfig(configDir, vcc.Log) config = &configContent if err != nil { // when we cannot read config file, config points to an empty ClusterConfig with default values // we want to reset config to nil so we will use user input later rather than those default values config = nil - vlog.LogPrintWarningln("Failed to read " + filepath.Join(configDir, ConfigFileName)) + vcc.Log.PrintWarning("Failed to read " + filepath.Join(configDir, ConfigFileName)) // when the customer wants to use user input, we can ignore config file error if !*opt.HonorUserInput { return config, err @@ -451,9 +451,9 @@ func (opt *DatabaseOptions) getVDBWhenDBIsDown(vcc *VClusterCommands) (vdb VCoor certs := HTTPSCerts{key: opt.Key, cert: opt.Cert, caCert: opt.CaCert} clusterOpEngine := MakeClusterOpEngine(instructions1, &certs) - err = clusterOpEngine.Run() + err = clusterOpEngine.Run(vcc.Log) if err != nil { - vlog.LogPrintError("fail to retrieve node names from NMA /nodes: %v", err) + vcc.Log.PrintError("fail to retrieve node names from NMA /nodes: %v", err) return vdb, err } @@ -469,9 +469,9 @@ func (opt *DatabaseOptions) getVDBWhenDBIsDown(vcc *VClusterCommands) (vdb VCoor instructions2 = append(instructions2, &nmaDownLoadFileOp) clusterOpEngine = MakeClusterOpEngine(instructions2, &certs) - err = clusterOpEngine.Run() + err = clusterOpEngine.Run(vcc.Log) if err != nil { - vlog.LogPrintError("fail to retrieve node details from %s: %v", descriptionFileName, err) + vcc.Log.PrintError("fail to retrieve node details from %s: %v", descriptionFileName, err) return vdb, err } diff --git a/vclusterops/vlog/printer.go b/vclusterops/vlog/printer.go index f9cd688..8cefbd1 100644 --- a/vclusterops/vlog/printer.go +++ b/vclusterops/vlog/printer.go @@ -17,8 +17,19 @@ package vlog import ( "fmt" + "os" + "strings" "github.com/go-logr/logr" + "github.com/go-logr/zapr" + "go.uber.org/zap" +) + +const ( + InfoLog = "[INFO] " + WarningLog = "[WARNING] " + ErrorLog = "[ERROR] " + DebugLog = "[DEBUG] " ) // Printer is a wrapper for the logger API that handles dual logging to the log @@ -62,7 +73,8 @@ func (p *Printer) Info(msg string, keysAndValues ...any) { // stdout, it will repeat the message to the console. func (p *Printer) PrintInfo(msg string, v ...any) { fmsg := fmt.Sprintf(msg, v...) - p.Log.Info(fmsg) + escapedFmsg := escapeSpecialCharacters(fmsg) + p.Log.Info(escapedFmsg) p.printlnCond(InfoLog, fmsg) } @@ -70,7 +82,8 @@ func (p *Printer) PrintInfo(msg string, v ...any) { // logging to stdout, it will repeat the message to the console. func (p *Printer) PrintError(msg string, v ...any) { fmsg := fmt.Sprintf(msg, v...) - p.Log.Error(nil, fmsg) + escapedFmsg := escapeSpecialCharacters(fmsg) + p.Log.Error(nil, escapedFmsg) p.printlnCond(ErrorLog, fmsg) } @@ -78,10 +91,20 @@ func (p *Printer) PrintError(msg string, v ...any) { // logging to stdout, it will repeat the message to the console. func (p *Printer) PrintWarning(msg string, v ...any) { fmsg := fmt.Sprintf(msg, v...) - p.Log.Info(fmsg) + escapedFmsg := escapeSpecialCharacters(fmsg) + p.Log.Info(escapedFmsg) p.printlnCond(WarningLog, fmsg) } +// escapeSpecialCharacters will escape special characters (tabs or newlines) in the message. +// Messages that are typically meant for the console could have tabs and newlines for alignment. +// We want to escape those when writing the message to the log so that each log entry is exactly one line long. +func escapeSpecialCharacters(message string) string { + message = strings.ReplaceAll(message, "\n", "\\n") + message = strings.ReplaceAll(message, "\t", "\\t") + return message +} + // printlnCond will conditonally print a message to the console if logging to a file func (p *Printer) printlnCond(label, msg string) { // Message is only printed if we are logging to a file only. Otherwise, it @@ -90,3 +113,93 @@ func (p *Printer) printlnCond(label, msg string) { fmt.Printf("%s%s\n", label, msg) } } + +// log functions for specific cases. +func (p *Printer) LogArgParse(inputArgv *[]string) { + fmsg := fmt.Sprintf("Called method Parse with args: %q.", *inputArgv) + p.Log.Info(fmsg) +} + +// log functions with masked params +func (p *Printer) LogMaskedArgParse(inputArgv []string) { + maskedPairs := logMaskedArgParseHelper(inputArgv) + fmsg := fmt.Sprintf("Called method Parse with args: %q.", maskedPairs) + p.Log.Info(fmsg) +} + +func logMaskedArgParseHelper(inputArgv []string) (maskedPairs []string) { + sensitiveKeyParams := map[string]bool{ + "awsauth": true, + "awssessiontoken": true, + "gcsauth": true, + "azurestoragecredentials": true, + } + const ( + expectedParts = 2 + maskedValue = "******" + ) + // We need to mask any parameters containing sensitive information + targetMaskedArg := map[string]bool{ + "--config-param": true, + } + for i := 0; i < len(inputArgv); i++ { + if targetMaskedArg[inputArgv[i]] && i+1 < len(inputArgv) { + pairs := strings.Split(inputArgv[i+1], ",") + for _, pair := range pairs { + keyValue := strings.Split(pair, "=") + if len(keyValue) == expectedParts { + key := keyValue[0] + value := keyValue[1] + keyLowerCase := strings.ToLower(key) + if sensitiveKeyParams[keyLowerCase] { + value = maskedValue + } + maskedPairs = append(maskedPairs, inputArgv[i], key+"="+value) + } else { + // Handle invalid format + maskedPairs = append(maskedPairs, pair) + } + } + i++ // Skip the next arg since it has been masked + } else { + maskedPairs = append(maskedPairs, inputArgv[i]) + } + } + return maskedPairs +} + +// setupOrDie will setup the logging for vcluster CLI. One exit, p.Log will +// be set. +func (p *Printer) SetupOrDie(logFile string) { + // The vcluster library uses logr as the logging API. We use Uber's zap + // package to implement the logging API. + EncoderConfigWithoutCaller := zap.NewDevelopmentEncoderConfig() + EncoderConfigWithoutCaller.EncodeCaller = nil // Set EncodeCaller to nil to exclude caller information + cfg := zap.Config{ + Level: zap.NewAtomicLevelAt(zap.InfoLevel), + Development: false, + // Sampling is enabled at 100:100, meaning that after the first 100 log + // entries with the same level and message in the same second, it will + // log every 100th entry with the same level and message in the same second. + Sampling: &zap.SamplingConfig{ + Initial: 100, + Thereafter: 100, + }, + Encoding: "console", + EncoderConfig: EncoderConfigWithoutCaller, + OutputPaths: []string{"stderr"}, + ErrorOutputPaths: []string{"stderr"}, + } + // If no log file is given, we just log to standard output + if logFile != "" { + p.LogToFileOnly = true + cfg.OutputPaths = []string{logFile} + } + zapLg, err := cfg.Build() + if err != nil { + fmt.Printf("Failed to setup the logger: %s", err.Error()) + os.Exit(1) + } + p.Log = zapr.NewLogger(zapLg) + p.Log.Info("Successfully started logger", "logFile", logFile) +} diff --git a/vclusterops/vlog/vLog.go b/vclusterops/vlog/vLog.go deleted file mode 100644 index 1872369..0000000 --- a/vclusterops/vlog/vLog.go +++ /dev/null @@ -1,361 +0,0 @@ -/* - (c) Copyright [2023] Open Text. - Licensed under the Apache License, Version 2.0 (the "License"); - You may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package vlog - -import ( - "fmt" - "log" - "os" - "strings" - "sync" - - "runtime/debug" - - "github.com/go-logr/zapr" - "go.uber.org/zap" -) - -const ( - DefaultLogPath = "/opt/vertica/log/vcluster.log" - LogPermission = 0644 - - InfoLog = "[INFO] " - WarningLog = "[WARNING] " - ErrorLog = "[ERROR] " - DebugLog = "[DEBUG] " -) - -type Vlogger struct { - LogPath string - Printer // Logging API to use for all logging calls -} - -var ( - logInstance Vlogger - once sync.Once -) - -// return a singleton instance of the GlobalLogger -func GetGlobalLogger() *Vlogger { - /* if once.Do(f) is called multiple times, - * only the first call will invoke f, - * even if f has a different value in each invocation. - * Reference: https://pkg.go.dev/sync#Once - */ - once.Do(func() { - logInstance = makeGlobalLogger() - }) - - return &logInstance -} - -func makeGlobalLogger() Vlogger { - newGlobalLogger := Vlogger{} - return newGlobalLogger -} - -func ParseLogPathArg(argInput []string, defaultPath string) string { - logger := GetGlobalLogger() - return logger.parseLogPathArg(argInput, defaultPath) -} -func (logger *Vlogger) parseLogPathArg(argInput []string, defaultPath string) string { - checkLogDir := true - for idx, arg := range argInput { - if arg == "--log-path" { - logger.LogPath = argInput[idx+1] - checkLogDir = false - } - } - if checkLogDir { - logger.LogPath = defaultPath - } - return logger.LogPath -} - -func SetupOrDie(logFile string) { - logger := GetGlobalLogger() - logger.setupOrDie(logFile) -} - -// setupOrDie will setup the logging for vcluster CLI. On exit, logger.Log will -// be set. -func (logger *Vlogger) setupOrDie(logFile string) { - // The vcluster library uses logr as the logging API. We use Uber's zap - // package to implement the logging API. - cfg := zap.Config{ - Level: zap.NewAtomicLevelAt(zap.InfoLevel), - Development: false, - // Sampling is enabled at 100:100, meaning that after the first 100 log - // entries with the same level and message in the same second, it will - // log every 100th entry with the same level and message in the same second. - Sampling: &zap.SamplingConfig{ - Initial: 100, - Thereafter: 100, - }, - Encoding: "console", - EncoderConfig: zap.NewDevelopmentEncoderConfig(), - OutputPaths: []string{"stderr"}, - ErrorOutputPaths: []string{"stderr"}, - } - // If no log file is given, we just log to standard output - if logFile != "" { - logger.LogToFileOnly = true - cfg.OutputPaths = []string{logFile} - } - var err error - zapLg, err := cfg.Build() - if err != nil { - logger.logFatal(err) - } - logger.Log = zapr.NewLogger(zapLg) - logger.Log.Info("Successfully started logger", "logFile", logFile) -} - -func LogStartupMessage() error { - logger := GetGlobalLogger() - return logger.logStartupMessage() -} - -func (logger *Vlogger) logStartupMessage() error { - // all INFO level log - logger.logInfo("New log for process %d", os.Getpid()) - logger.logInfo("Called with args %s", os.Args) - hostname, err := os.Hostname() - if err != nil { - return err - } - - logger.logInfo("Hostname %s, User id %d", hostname, os.Getuid()) - return nil -} - -func LogFatal(err error) { - logger := GetGlobalLogger() - logger.logFatal(err) -} - -func (logger *Vlogger) logFatal(err error) { - if err == nil { - return - } - stackBytes := debug.Stack() - logger.logInfo("Fatal error occurred. Backtrace:\n%s\n", string(stackBytes)) - log.Fatal(err) -} - -func LogInfoln(info string) { - logger := GetGlobalLogger() - logger.logInfoln(info) -} - -// basic log functions starts here: log plain string -// following log.Println naming convention -func (logger *Vlogger) logInfoln(info string) { - logger.Log.V(0).Info(info) -} - -func LogWarningln(info string) { - logger := GetGlobalLogger() - logger.logWarningln(info) -} - -// log Warning -func (logger *Vlogger) logWarningln(info string) { - logger.Log.V(0).Info(info) -} - -func LogErrorln(info string) { - logger := GetGlobalLogger() - logger.logErrorln(info) -} - -// log error -func (logger *Vlogger) logErrorln(info string) { - logger.Log.Error(nil, info) -} - -func LogInfo(info string, v ...any) { - logger := GetGlobalLogger() - logger.logInfo(info, v...) -} - -// log info with formatting -func (logger *Vlogger) logInfo(info string, v ...any) { - msg := fmt.Sprintf(info, v...) - logger.Log.V(0).Info(msg) -} - -func LogWarning(info string, v ...any) { - logger := GetGlobalLogger() - logger.logWarning(info, v...) -} -func (logger *Vlogger) logWarning(info string, v ...any) { - msg := fmt.Sprintf(info, v...) - logger.Log.V(0).Info(msg) -} - -func LogError(info string, v ...any) { - logger := GetGlobalLogger() - logger.logError(info, v...) -} -func (logger *Vlogger) logError(info string, v ...any) { - msg := fmt.Sprintf(info, v...) - logger.Log.Error(nil, msg) -} - -func LogDebug(info string, v ...any) { - logger := GetGlobalLogger() - logger.logDebug(info, v...) -} -func (logger *Vlogger) logDebug(info string, v ...any) { - msg := fmt.Sprintf(info, v...) - logger.Log.V(1).Info(msg) -} - -// LogPrintInfo will write an info message to stdout and the logger. The -// message can contain format specifiers. -func LogPrintInfo(msg string, v ...any) { - logger := GetGlobalLogger() - logger.logPrintInfo(msg, v...) -} -func (logger *Vlogger) logPrintInfo(msg string, v ...any) { - completeMsg := fmt.Sprintf(InfoLog+msg, v...) - logger.logPrintInfoln(completeMsg) -} - -// LogPrintError will write an error message to stdout and the logger. The -// message can contain format specifiers. -func LogPrintError(msg string, v ...any) { - logger := GetGlobalLogger() - logger.logPrintError(msg, v...) -} -func (logger *Vlogger) logPrintError(msg string, v ...any) { - completeMsg := fmt.Sprintf(msg, v...) - logger.logPrintErrorln(completeMsg) -} - -// LogPrintDebug will write a debug message to stdout and the logger. The -// message can contain format specifiers. -func LogPrintDebug(msg string, v ...any) { - logger := GetGlobalLogger() - logger.logPrintDebug(msg, v...) -} -func (logger *Vlogger) logPrintDebug(msg string, v ...any) { - completeMsg := fmt.Sprintf(DebugLog+msg, v...) - fmt.Println(completeMsg) - logger.Log.V(1).Info(completeMsg) -} - -// LogPrintWarning will write a warning message to stdout and the logger. The -// message can contain format specifiers. -func LogPrintWarning(msg string, v ...any) { - logger := GetGlobalLogger() - logger.logPrintWarning(msg, v...) -} -func (logger *Vlogger) logPrintWarning(msg string, v ...any) { - completeMsg := fmt.Sprintf(WarningLog+msg, v...) - logger.logPrintWarningln(completeMsg) -} - -// LogPrintInfoln will write an info message to stdout and the logger -func LogPrintInfoln(msg string) { - logger := GetGlobalLogger() - logger.logPrintInfoln(msg) -} -func (logger *Vlogger) logPrintInfoln(msg string) { - fmt.Println(msg) - logger.Log.V(0).Info(msg) -} - -// LogPrintWarningln will write a warning message to stdout and the logger -func LogPrintWarningln(msg string) { - logger := GetGlobalLogger() - logger.logPrintWarningln(msg) -} -func (logger *Vlogger) logPrintWarningln(msg string) { - fmt.Println(msg) - logger.Log.V(0).Info(msg) -} - -// LogPrintErrorln will write an error message to stdout and the logger -func LogPrintErrorln(msg string) { - logger := GetGlobalLogger() - logger.logPrintErrorln(msg) -} -func (logger *Vlogger) logPrintErrorln(msg string) { - fmt.Println(msg) - logger.Log.Error(nil, msg) -} - -func LogArgParse(inputArgv *[]string) { - logger := GetGlobalLogger() - logger.logArgParse(inputArgv) -} - -// log functions for specific cases -func (logger *Vlogger) logArgParse(inputArgv *[]string) { - inputArgMsg := fmt.Sprintf("Called method Parse with args: %q.", *inputArgv) - logger.logInfoln(inputArgMsg) -} - -func LogMaskedArgParse(inputArgv []string) { - logger := GetGlobalLogger() - logger.logMaskedArgParse(inputArgv) -} - -// log functions with masked params -func (logger *Vlogger) logMaskedArgParse(inputArgv []string) { - var maskedPairs []string - sensitiveKeyParams := map[string]bool{ - "awsauth": true, - "awssessiontoken": true, - "gcsauth": true, - "azurestoragecredentials": true, - } - const ( - expectedParts = 2 - maskedValue = "******" - ) - // We need to mask any parameters containing sensitive information - targetMaskedArg := map[string]bool{ - "--config-param": true, - } - for i := 0; i < len(inputArgv); i++ { - if targetMaskedArg[inputArgv[i]] && i+1 < len(inputArgv) { - pairs := strings.Split(inputArgv[i+1], ",") - for _, pair := range pairs { - keyValue := strings.Split(pair, "=") - if len(keyValue) == expectedParts { - key := keyValue[0] - value := keyValue[1] - keyLowerCase := strings.ToLower(key) - if sensitiveKeyParams[keyLowerCase] { - value = maskedValue - } - maskedPairs = append(maskedPairs, inputArgv[i], key+"="+value) - } else { - // Handle invalid format - maskedPairs = append(maskedPairs, pair) - } - } - i++ // Skip the next arg since it has been masked - } else { - maskedPairs = append(maskedPairs, inputArgv[i]) - } - } - inputArgMsg := fmt.Sprintf("Called method Parse with args: %q.", maskedPairs) - logger.logInfoln(inputArgMsg) -}