From bda295c5e7af8454fb2c8503956ffe0fd9fbade2 Mon Sep 17 00:00:00 2001 From: roypaulin Date: Thu, 16 Nov 2023 06:26:51 -0500 Subject: [PATCH] Sync from server repo (88b0283) --- commands/cluster_command.go | 4 +- commands/cluster_command_launcher.go | 10 +- commands/cmd_add_node.go | 10 +- commands/cmd_add_subcluster.go | 14 +-- commands/cmd_base.go | 8 +- commands/cmd_config.go | 10 +- commands/cmd_create_db.go | 14 +-- commands/cmd_drop_db.go | 12 +- commands/cmd_help.go | 10 +- commands/cmd_init.go | 14 +-- commands/cmd_list_all_nodes.go | 10 +- commands/cmd_re_ip.go | 12 +- commands/cmd_remove_node.go | 10 +- commands/cmd_remove_subcluster.go | 10 +- commands/cmd_restart_node.go | 14 +-- commands/cmd_revive_db.go | 14 +-- commands/cmd_scrutinize.go | 32 +++--- commands/cmd_start_db.go | 14 +-- commands/cmd_stop_db.go | 14 +-- rfc7807/rfc7807.go | 2 +- vclusterops/adapter_pool.go | 14 +-- vclusterops/add_node.go | 8 +- vclusterops/add_subcluster.go | 10 +- vclusterops/cluster_config.go | 18 +-- vclusterops/cluster_op.go | 35 +++--- vclusterops/cluster_op_engine.go | 6 +- vclusterops/cluster_op_engine_context.go | 4 +- vclusterops/coordinator_database.go | 12 +- vclusterops/create_db.go | 14 +-- vclusterops/helpers.go | 10 +- vclusterops/http_adapter.go | 18 +-- vclusterops/http_request_dispatcher.go | 14 +-- vclusterops/https_add_subcluster_op.go | 4 +- vclusterops/https_check_db_running_op.go | 82 +++++++------- vclusterops/https_check_node_state_op.go | 8 +- vclusterops/https_check_subcluster_op.go | 4 +- vclusterops/https_create_cluster_depot_op.go | 4 +- vclusterops/https_create_node_op.go | 4 +- vclusterops/https_create_nodes_depot_op.go | 4 +- vclusterops/https_drop_node_op.go | 4 +- vclusterops/https_find_subcluster_op.go | 8 +- vclusterops/https_get_cluster_info_op.go | 4 +- vclusterops/https_get_nodes_info_op.go | 6 +- vclusterops/https_get_up_nodes_op.go | 40 +++++-- vclusterops/https_install_packages_op.go | 6 +- vclusterops/https_mark_design_ksafe_op.go | 6 +- vclusterops/https_mark_nodes_ephemeral_op.go | 4 +- vclusterops/https_poll_node_state_op.go | 35 +++--- .../https_poll_subscription_state_op.go | 12 +- vclusterops/https_rebalance_cluster_op.go | 4 +- .../https_rebalance_subcluster_shards_op.go | 4 +- vclusterops/https_reload_spread_op.go | 8 +- vclusterops/https_spread_remove_node_op.go | 4 +- vclusterops/https_startup_command_op.go | 4 +- vclusterops/https_stop_db_op.go | 4 +- vclusterops/https_sync_catalog_op.go | 10 +- vclusterops/nma_bootstrap_catalog_op.go | 8 +- vclusterops/nma_delete_dir_op.go | 6 +- vclusterops/nma_download_config.go | 8 +- vclusterops/nma_download_file_op.go | 14 +-- vclusterops/nma_get_healthy_nodes_op.go | 12 +- vclusterops/nma_get_nodes_info_op.go | 14 +-- vclusterops/nma_get_scrutinize_tar_op.go | 39 ++----- vclusterops/nma_health_op.go | 4 +- vclusterops/nma_load_remote_catalog_op.go | 6 +- vclusterops/nma_network_profile_op.go | 4 +- vclusterops/nma_prepare_directories_op.go | 6 +- vclusterops/nma_re_ip_op.go | 12 +- vclusterops/nma_read_catalog_editor_op.go | 10 +- vclusterops/nma_spread_security_op.go | 10 +- vclusterops/nma_stage_error_report_op.go | 105 ++++++++++++++++++ vclusterops/nma_stage_vertica_logs_op.go | 93 +++++----------- vclusterops/nma_start_node_op.go | 8 +- vclusterops/nma_upload_config.go | 6 +- vclusterops/nma_vertica_version_op.go | 26 ++--- vclusterops/re_ip.go | 8 +- vclusterops/remove_subcluster.go | 14 +-- vclusterops/restart_node.go | 12 +- vclusterops/scrutinize.go | 101 ++++++++++++----- vclusterops/scrutinize_op.go | 84 ++++++++++++++ vclusterops/start_db.go | 15 ++- vclusterops/vlog/printer.go | 7 ++ 82 files changed, 756 insertions(+), 550 deletions(-) create mode 100644 vclusterops/nma_stage_error_report_op.go create mode 100644 vclusterops/scrutinize_op.go diff --git a/commands/cluster_command.go b/commands/cluster_command.go index c312e63..0514923 100644 --- a/commands/cluster_command.go +++ b/commands/cluster_command.go @@ -22,14 +22,14 @@ import ( type ClusterCommand interface { CommandType() string - Parse(argv []string, log vlog.Printer) error + Parse(argv []string, logger 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(log vlog.Printer) error + Analyze(logger 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 e1315c8..f5492cf 100644 --- a/commands/cluster_command_launcher.go +++ b/commands/cluster_command_launcher.go @@ -62,11 +62,11 @@ const defaultLogPath = "/opt/vertica/log/vcluster.log" func MakeClusterCommandLauncher() (ClusterCommandLauncher, vclusterops.VClusterCommands) { // setup logs for command launcher initialization userCommandString := os.Args[1] - log := vlog.Printer{ForCli: true} + logger := vlog.Printer{ForCli: true} logPath := parseLogPathArg(os.Args, defaultLogPath) - log.SetupOrDie(logPath) + logger.SetupOrDie(logPath) vcc := vclusterops.VClusterCommands{ - Log: log.WithName(userCommandString), + Log: logger.WithName(userCommandString), } vcc.Log.Info("New vcluster command initialization") newLauncher := ClusterCommandLauncher{} @@ -175,14 +175,14 @@ func (c ClusterCommandLauncher) Run(inputArgv []string, vcc vclusterops.VCluster } func identifySubcommand(commands map[string]ClusterCommand, userCommandString string, - log vlog.Printer) (ClusterCommand, error) { + logger vlog.Printer) (ClusterCommand, error) { command, ok := commands[userCommandString] if !ok { return nil, fmt.Errorf("unrecognized command '%s'", userCommandString) } - log.Log.Info("Recognized command", "cmd", userCommandString) + logger.Log.Info("Recognized command", "cmd", userCommandString) return command, nil } diff --git a/commands/cmd_add_node.go b/commands/cmd_add_node.go index 9274a3e..55103cf 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, log vlog.Printer) error { +func (c *CmdAddNode) Parse(inputArgv []string, logger vlog.Printer) error { c.argv = inputArgv - err := c.ValidateParseArgv(c.CommandType(), log) + err := c.ValidateParseArgv(c.CommandType(), logger) if err != nil { return err } @@ -105,11 +105,11 @@ func (c *CmdAddNode) Parse(inputArgv []string, log vlog.Printer) error { if !util.IsOptionSet(c.parser, "eon-mode") { c.CmdBase.isEon = nil } - return c.validateParse(log) + return c.validateParse(logger) } -func (c *CmdAddNode) validateParse(log vlog.Printer) error { - log.Info("Called validateParse()") +func (c *CmdAddNode) validateParse(logger vlog.Printer) error { + logger.Info("Called validateParse()") err := c.parseNewHostList() if err != nil { diff --git a/commands/cmd_add_subcluster.go b/commands/cmd_add_subcluster.go index f656517..9203b14 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, log vlog.Printer) error { +func (c *CmdAddSubcluster) Parse(inputArgv []string, logger vlog.Printer) error { c.argv = inputArgv - err := c.ValidateParseArgv(c.CommandType(), log) + err := c.ValidateParseArgv(c.CommandType(), logger) if err != nil { return err } @@ -110,17 +110,17 @@ func (c *CmdAddSubcluster) Parse(inputArgv []string, log vlog.Printer) error { c.addSubclusterOptions.ConfigDirectory = nil } - return c.validateParse(log) + return c.validateParse(logger) } // all validations of the arguments should go in here -func (c *CmdAddSubcluster) validateParse(log vlog.Printer) error { - log.Info("Called validateParse()") +func (c *CmdAddSubcluster) validateParse(logger vlog.Printer) error { + logger.Info("Called validateParse()") return c.ValidateParseBaseOptions(&c.addSubclusterOptions.DatabaseOptions) } -func (c *CmdAddSubcluster) Analyze(log vlog.Printer) error { - log.Info("Called method Analyze()") +func (c *CmdAddSubcluster) Analyze(logger vlog.Printer) error { + logger.Info("Called method Analyze()") return nil } diff --git a/commands/cmd_base.go b/commands/cmd_base.go index 9435236..2964e92 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, log vlog.Printer) error { - log.LogArgParse(&c.argv) +func (c *CmdBase) ValidateParseArgv(commandType string, logger vlog.Printer) error { + logger.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, log vlog.Printer) error { - log.LogMaskedArgParse(c.argv) +func (c *CmdBase) ValidateParseMaskedArgv(commandType string, logger vlog.Printer) error { + logger.LogMaskedArgParse(c.argv) return c.ValidateParseArgvHelper(commandType) } diff --git a/commands/cmd_config.go b/commands/cmd_config.go index 38898e6..7cb2c57 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, log vlog.Printer) error { - log.LogArgParse(&inputArgv) +func (c *CmdConfig) Parse(inputArgv []string, logger vlog.Printer) error { + logger.LogArgParse(&inputArgv) if c.parser == nil { return fmt.Errorf("unexpected nil - the parser was nil") @@ -68,11 +68,11 @@ func (c *CmdConfig) Parse(inputArgv []string, log vlog.Printer) error { return err } - return c.validateParse(log) + return c.validateParse(logger) } -func (c *CmdConfig) validateParse(log vlog.Printer) error { - log.Info("Called validateParse()") +func (c *CmdConfig) validateParse(logger vlog.Printer) error { + logger.Info("Called validateParse()") // if directory is not provided, then use the current directory return c.validateDirectory() } diff --git a/commands/cmd_create_db.go b/commands/cmd_create_db.go index fe470dd..76dfd97 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, log vlog.Printer) error { +func (c *CmdCreateDB) Parse(inputArgv []string, logger vlog.Printer) error { c.argv = inputArgv - err := c.ValidateParseMaskedArgv(c.CommandType(), log) + err := c.ValidateParseMaskedArgv(c.CommandType(), logger) if err != nil { return err } @@ -139,12 +139,12 @@ func (c *CmdCreateDB) Parse(inputArgv []string, log vlog.Printer) error { c.createDBOptions.IsEon = vstruct.False } - return c.validateParse(log) + return c.validateParse(logger) } // all validations of the arguments should go in here -func (c *CmdCreateDB) validateParse(log vlog.Printer) error { - log.Info("Called validateParse()") +func (c *CmdCreateDB) validateParse(logger vlog.Printer) error { + logger.Info("Called validateParse()") // parse raw host str input into a []string of createDBOptions err := c.createDBOptions.ParseHostList(*c.hostListStr) @@ -166,8 +166,8 @@ func (c *CmdCreateDB) validateParse(log vlog.Printer) error { return nil } -func (c *CmdCreateDB) Analyze(log vlog.Printer) error { - log.Info("Called method Analyze()") +func (c *CmdCreateDB) Analyze(logger vlog.Printer) error { + logger.Info("Called method Analyze()") return nil } diff --git a/commands/cmd_drop_db.go b/commands/cmd_drop_db.go index e4079bc..063d14a 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, log vlog.Printer) error { +func (c *CmdDropDB) Parse(inputArgv []string, logger vlog.Printer) error { c.argv = inputArgv - err := c.ValidateParseArgv(c.CommandType(), log) + err := c.ValidateParseArgv(c.CommandType(), logger) if err != nil { return err } @@ -64,14 +64,14 @@ func (c *CmdDropDB) Parse(inputArgv []string, log vlog.Printer) error { c.CmdBase.ipv6 = nil } - return c.validateParse(log) + return c.validateParse(logger) } -func (c *CmdDropDB) validateParse(log vlog.Printer) error { +func (c *CmdDropDB) validateParse(logger vlog.Printer) error { if !util.IsOptionSet(c.parser, "password") { c.dropDBOptions.Password = nil } - log.Info("Called validateParse()") + logger.Info("Called validateParse()") return c.ValidateParseBaseOptions(&c.dropDBOptions.DatabaseOptions) } @@ -88,6 +88,6 @@ func (c *CmdDropDB) Run(vcc vclusterops.VClusterCommands) error { return err } - vcc.Log.PrintInfo("Successfully dropped database %s\n", *c.dropDBOptions.DBName) + vcc.Log.PrintInfo("Successfully dropped database %s", *c.dropDBOptions.DBName) return nil } diff --git a/commands/cmd_help.go b/commands/cmd_help.go index 7325547..7e48b2d 100644 --- a/commands/cmd_help.go +++ b/commands/cmd_help.go @@ -48,8 +48,8 @@ func (c CmdHelp) CommandType() string { return "help" } -func (c *CmdHelp) Parse(inputArgv []string, log vlog.Printer) error { - log.LogArgParse(&inputArgv) +func (c *CmdHelp) Parse(inputArgv []string, logger vlog.Printer) error { + logger.LogArgParse(&inputArgv) if c.parser == nil { return fmt.Errorf("unexpected nil - the parser was nil") @@ -61,11 +61,11 @@ func (c *CmdHelp) Parse(inputArgv []string, log vlog.Printer) error { return err } - return c.validateParse(log) + return c.validateParse(logger) } -func (c *CmdHelp) validateParse(log vlog.Printer) error { - log.Info("Called validateParse()") +func (c *CmdHelp) validateParse(logger vlog.Printer) error { + logger.Info("Called validateParse()") return nil } diff --git a/commands/cmd_init.go b/commands/cmd_init.go index c693444..de23c8a 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, log vlog.Printer) error { - log.LogArgParse(&inputArgv) +func (c *CmdInit) Parse(inputArgv []string, logger vlog.Printer) error { + logger.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, log vlog.Printer) error { return err } - return c.validateParse(log) + return c.validateParse(logger) } -func (c *CmdInit) validateParse(log vlog.Printer) error { - log.Info("Called validateParse()") +func (c *CmdInit) validateParse(logger vlog.Printer) error { + logger.Info("Called validateParse()") // if directory is not provided, then use the current directory err := c.validateDirectory() @@ -91,8 +91,8 @@ func (c *CmdInit) validateParse(log vlog.Printer) error { return nil } -func (c *CmdInit) Analyze(log vlog.Printer) error { - log.Info("Called method Analyze()") +func (c *CmdInit) Analyze(logger vlog.Printer) error { + logger.Info("Called method Analyze()") return nil } diff --git a/commands/cmd_list_all_nodes.go b/commands/cmd_list_all_nodes.go index 7954113..e785a5d 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, log vlog.Printer) error { +func (c *CmdListAllNodes) Parse(inputArgv []string, logger vlog.Printer) error { c.argv = inputArgv - err := c.ValidateParseArgv(c.CommandType(), log) + err := c.ValidateParseArgv(c.CommandType(), logger) if err != nil { return err } @@ -53,11 +53,11 @@ func (c *CmdListAllNodes) Parse(inputArgv []string, log vlog.Printer) error { c.fetchNodeStateOptions.Password = nil } - return c.validateParse(log) + return c.validateParse(logger) } -func (c *CmdListAllNodes) validateParse(log vlog.Printer) error { - log.Info("Called validateParse()") +func (c *CmdListAllNodes) validateParse(logger vlog.Printer) error { + logger.Info("Called validateParse()") // parse raw host str input into a []string err := c.parseHostList(&c.fetchNodeStateOptions.DatabaseOptions) diff --git a/commands/cmd_re_ip.go b/commands/cmd_re_ip.go index 06a8eb5..fc0ad54 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, log vlog.Printer) error { - log.LogArgParse(&inputArgv) +func (c *CmdReIP) Parse(inputArgv []string, logger vlog.Printer) error { + logger.LogArgParse(&inputArgv) if c.parser == nil { return fmt.Errorf("unexpected nil - the parser was nil") } c.argv = inputArgv - err := c.ValidateParseArgv(c.CommandType(), log) + err := c.ValidateParseArgv(c.CommandType(), logger) if err != nil { return err } - return c.validateParse(log) + return c.validateParse(logger) } -func (c *CmdReIP) validateParse(log vlog.Printer) error { - log.Info("Called validateParse()") +func (c *CmdReIP) validateParse(logger vlog.Printer) error { + logger.Info("Called validateParse()") // parse raw host str input into a []string err := c.parseHostList(&c.reIPOptions.DatabaseOptions) diff --git a/commands/cmd_remove_node.go b/commands/cmd_remove_node.go index d12c9e4..9d417fc 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, log vlog.Printer) error { +func (c *CmdRemoveNode) Parse(inputArgv []string, logger vlog.Printer) error { c.argv = inputArgv - err := c.ValidateParseArgv(c.CommandType(), log) + err := c.ValidateParseArgv(c.CommandType(), logger) if err != nil { return err } @@ -89,11 +89,11 @@ func (c *CmdRemoveNode) Parse(inputArgv []string, log vlog.Printer) error { if !util.IsOptionSet(c.parser, "password") { c.removeNodeOptions.Password = nil } - return c.validateParse(log) + return c.validateParse(logger) } -func (c *CmdRemoveNode) validateParse(log vlog.Printer) error { - log.Info("Called validateParse()") +func (c *CmdRemoveNode) validateParse(logger vlog.Printer) error { + logger.Info("Called validateParse()") err := c.removeNodeOptions.ParseHostToRemoveList(*c.hostToRemoveListStr) if err != nil { diff --git a/commands/cmd_remove_subcluster.go b/commands/cmd_remove_subcluster.go index 0376bf5..0c50990 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, log vlog.Printer) error { +func (c *CmdRemoveSubcluster) Parse(inputArgv []string, logger vlog.Printer) error { c.argv = inputArgv - err := c.ValidateParseArgv(c.CommandType(), log) + err := c.ValidateParseArgv(c.CommandType(), logger) if err != nil { return err } @@ -84,11 +84,11 @@ func (c *CmdRemoveSubcluster) Parse(inputArgv []string, log vlog.Printer) error if !util.IsOptionSet(c.parser, "password") { c.removeScOptions.Password = nil } - return c.validateParse(log) + return c.validateParse(logger) } -func (c *CmdRemoveSubcluster) validateParse(log vlog.Printer) error { - log.Info("Called validateParse()") +func (c *CmdRemoveSubcluster) validateParse(logger vlog.Printer) error { + logger.Info("Called validateParse()") return c.ValidateParseBaseOptions(&c.removeScOptions.DatabaseOptions) } diff --git a/commands/cmd_restart_node.go b/commands/cmd_restart_node.go index 9d2d568..3bb2fe1 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, log vlog.Printer) error { +func (c *CmdRestartNodes) Parse(inputArgv []string, logger vlog.Printer) error { if c.parser == nil { return fmt.Errorf("unexpected nil - the parser was nil") } c.argv = inputArgv - err := c.ValidateParseArgv(c.CommandType(), log) + err := c.ValidateParseArgv(c.CommandType(), logger) if err != nil { return err } @@ -82,11 +82,11 @@ func (c *CmdRestartNodes) Parse(inputArgv []string, log vlog.Printer) error { c.CmdBase.ipv6 = nil } - return c.validateParse(log) + return c.validateParse(logger) } -func (c *CmdRestartNodes) validateParse(log vlog.Printer) error { - log.Info("Called validateParse()") +func (c *CmdRestartNodes) validateParse(logger vlog.Printer) error { + logger.Info("Called validateParse()") err := c.restartNodesOptions.ParseNodesList(*c.vnodeListStr) if err != nil { return err @@ -94,9 +94,9 @@ func (c *CmdRestartNodes) validateParse(log vlog.Printer) error { return c.ValidateParseBaseOptions(&c.restartNodesOptions.DatabaseOptions) } -func (c *CmdRestartNodes) Analyze(log vlog.Printer) error { +func (c *CmdRestartNodes) Analyze(logger vlog.Printer) error { // Analyze() is needed to fulfill an interface - log.Info("Called method Analyze()") + logger.Info("Called method Analyze()") return nil } diff --git a/commands/cmd_revive_db.go b/commands/cmd_revive_db.go index 3867e80..a86d094 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, log vlog.Printer) error { +func (c *CmdReviveDB) Parse(inputArgv []string, logger vlog.Printer) error { c.argv = inputArgv - err := c.ValidateParseMaskedArgv(c.CommandType(), log) + err := c.ValidateParseMaskedArgv(c.CommandType(), logger) if err != nil { return err } @@ -74,11 +74,11 @@ func (c *CmdReviveDB) Parse(inputArgv []string, log vlog.Printer) error { c.CmdBase.ipv6 = nil } - return c.validateParse(log) + return c.validateParse(logger) } -func (c *CmdReviveDB) validateParse(log vlog.Printer) error { - log.Info("Called validateParse()") +func (c *CmdReviveDB) validateParse(logger vlog.Printer) error { + logger.Info("Called validateParse()") // check the format of configuration params string, and parse it into configParams configurationParams, err := util.ParseConfigParams(*c.configurationParams) @@ -100,8 +100,8 @@ func (c *CmdReviveDB) validateParse(log vlog.Printer) error { return c.ValidateParseBaseOptions(&c.reviveDBOptions.DatabaseOptions) } -func (c *CmdReviveDB) Analyze(log vlog.Printer) error { - log.Info("Called method Analyze()") +func (c *CmdReviveDB) Analyze(logger vlog.Printer) error { + logger.Info("Called method Analyze()") return nil } diff --git a/commands/cmd_scrutinize.go b/commands/cmd_scrutinize.go index e7f6800..3638457 100644 --- a/commands/cmd_scrutinize.go +++ b/commands/cmd_scrutinize.go @@ -99,10 +99,10 @@ func (c *CmdScrutinize) CommandType() string { return vclusterops.VScrutinizeTypeName } -func (c *CmdScrutinize) Parse(inputArgv []string, log vlog.Printer) error { - log.PrintInfo("Parsing scrutinize command input") +func (c *CmdScrutinize) Parse(inputArgv []string, logger vlog.Printer) error { + logger.PrintInfo("Parsing scrutinize command input") c.argv = inputArgv - err := c.ValidateParseMaskedArgv(c.CommandType(), log) + err := c.ValidateParseMaskedArgv(c.CommandType(), logger) if err != nil { return err } @@ -125,20 +125,20 @@ func (c *CmdScrutinize) Parse(inputArgv []string, log vlog.Printer) error { } // parses host list and ipv6 - eon is irrelevant but handled - return c.validateParse(log) + return c.validateParse(logger) } // all validations of the arguments should go in here -func (c *CmdScrutinize) validateParse(log vlog.Printer) error { - log.Info("Called validateParse()") +func (c *CmdScrutinize) validateParse(logger vlog.Printer) error { + logger.Info("Called validateParse()") return c.ValidateParseBaseOptions(&c.sOptions.DatabaseOptions) } -func (c *CmdScrutinize) Analyze(log vlog.Printer) error { - log.Info("Called method Analyze()") +func (c *CmdScrutinize) Analyze(logger vlog.Printer) error { + logger.Info("Called method Analyze()") // set cert/key values from env k8s - err := c.updateCertTextsFromk8s(log) + err := c.updateCertTextsFromk8s(logger) if err != nil { return err } @@ -146,13 +146,13 @@ func (c *CmdScrutinize) Analyze(log vlog.Printer) error { var allErrs error port, found := os.LookupEnv(kubernetesPort) if found && port != "" && *c.sOptions.HonorUserInput { - log.Info(kubernetesPort, " is set, k8s environment detected", found) + logger.Info(kubernetesPort, " is set, k8s environment detected", found) dbName, found := os.LookupEnv(databaseName) if !found || dbName == "" { allErrs = errors.Join(allErrs, fmt.Errorf("unable to get database name from environment variable. ")) } else { c.sOptions.DBName = &dbName - log.Info("Setting database name from env as", "DBName", *c.sOptions.DBName) + logger.Info("Setting database name from env as", "DBName", *c.sOptions.DBName) } catPrefix, found := os.LookupEnv(catalogPathPref) @@ -160,7 +160,7 @@ func (c *CmdScrutinize) Analyze(log vlog.Printer) error { allErrs = errors.Join(allErrs, fmt.Errorf("unable to get catalog path from environment variable. ")) } else { c.sOptions.CatalogPrefix = &catPrefix - log.Info("Setting catalog path from env as", "CatalogPrefix", *c.sOptions.CatalogPrefix) + logger.Info("Setting catalog path from env as", "CatalogPrefix", *c.sOptions.CatalogPrefix) } if allErrs != nil { return allErrs @@ -226,12 +226,12 @@ func (k8sSecretRetrieverStruct) RetrieveSecret(namespace, secretName string) (ca // updateCertTextsFromk8s retrieves PEM-encoded text of CA certs, the server cert, and // the server key from kubernetes. -func (c *CmdScrutinize) updateCertTextsFromk8s(log vlog.Printer) error { +func (c *CmdScrutinize) updateCertTextsFromk8s(logger vlog.Printer) error { _, portSet := os.LookupEnv(kubernetesPort) if !portSet { return nil } - log.Info("K8s environment") + logger.Info("K8s environment") secretNameSpace, nameSpaceSet := os.LookupEnv(secretNameSpaceEnvVar) secretName, nameSet := os.LookupEnv(secretNameEnvVar) @@ -244,7 +244,7 @@ func (c *CmdScrutinize) updateCertTextsFromk8s(log vlog.Printer) error { } if !nameSpaceSet { - log.Info("Secret name not set in env. Failback to other cert retieval methods.") + logger.Info("Secret name not set in env. Failback to other cert retieval methods.") return nil } @@ -253,7 +253,7 @@ func (c *CmdScrutinize) updateCertTextsFromk8s(log vlog.Printer) error { return fmt.Errorf("failed to read certs from k8s secret %s in namespace %s: %w", secretName, secretNameSpace, err) } if len(caCert) != 0 && len(cert) != 0 && len(key) != 0 { - log.Info("Successfully read cert from k8s secret ", "secretName", secretName, "secretNameSpace", secretNameSpace) + logger.Info("Successfully read cert from k8s secret ", "secretName", secretName, "secretNameSpace", secretNameSpace) } else { return fmt.Errorf("failed to read CA, cert or key (sizes = %d/%d/%d)", len(caCert), len(cert), len(key)) diff --git a/commands/cmd_start_db.go b/commands/cmd_start_db.go index 0798e01..bdc6c3b 100644 --- a/commands/cmd_start_db.go +++ b/commands/cmd_start_db.go @@ -80,13 +80,13 @@ func (c *CmdStartDB) CommandType() string { return "start_db" } -func (c *CmdStartDB) Parse(inputArgv []string, log vlog.Printer) error { +func (c *CmdStartDB) Parse(inputArgv []string, logger vlog.Printer) error { if c.parser == nil { return fmt.Errorf("unexpected nil - the parser was nil") } c.argv = inputArgv - err := c.ValidateParseArgv(c.CommandType(), log) + err := c.ValidateParseArgv(c.CommandType(), logger) if err != nil { return err } @@ -106,11 +106,11 @@ func (c *CmdStartDB) Parse(inputArgv []string, log vlog.Printer) error { c.startDBOptions.ConfigDirectory = nil } - return c.validateParse(log) + return c.validateParse(logger) } -func (c *CmdStartDB) validateParse(log vlog.Printer) error { - log.Info("Called validateParse()", "command", c.CommandType()) +func (c *CmdStartDB) validateParse(logger vlog.Printer) error { + logger.Info("Called validateParse()", "command", c.CommandType()) // check the format of configuration params string, and parse it into configParams configurationParams, err := util.ParseConfigParams(*c.configurationParams) @@ -124,9 +124,9 @@ func (c *CmdStartDB) validateParse(log vlog.Printer) error { return c.ValidateParseBaseOptions(&c.startDBOptions.DatabaseOptions) } -func (c *CmdStartDB) Analyze(log vlog.Printer) error { +func (c *CmdStartDB) Analyze(logger vlog.Printer) error { // Analyze() is needed to fulfill an interface - log.Info("Called method Analyze()") + logger.Info("Called method Analyze()") return nil } diff --git a/commands/cmd_stop_db.go b/commands/cmd_stop_db.go index 30860cb..244c3e8 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, log vlog.Printer) error { +func (c *CmdStopDB) Parse(inputArgv []string, logger vlog.Printer) error { c.argv = inputArgv - err := c.ValidateParseArgv(c.CommandType(), log) + err := c.ValidateParseArgv(c.CommandType(), logger) if err != nil { return err } @@ -111,17 +111,17 @@ func (c *CmdStopDB) Parse(inputArgv []string, log vlog.Printer) error { c.stopDBOptions.ConfigDirectory = nil } - return c.validateParse(log) + return c.validateParse(logger) } // all validations of the arguments should go in here -func (c *CmdStopDB) validateParse(log vlog.Printer) error { - log.Info("Called validateParse()") +func (c *CmdStopDB) validateParse(logger vlog.Printer) error { + logger.Info("Called validateParse()") return c.ValidateParseBaseOptions(&c.stopDBOptions.DatabaseOptions) } -func (c *CmdStopDB) Analyze(log vlog.Printer) error { - log.Info("Called method Analyze()") +func (c *CmdStopDB) Analyze(logger vlog.Printer) error { + logger.Info("Called method Analyze()") return nil } diff --git a/rfc7807/rfc7807.go b/rfc7807/rfc7807.go index 059e41a..b1b861c 100644 --- a/rfc7807/rfc7807.go +++ b/rfc7807/rfc7807.go @@ -64,7 +64,7 @@ type VProblem struct { // Error implement this function so that VProblem can be passed around with Go's // error interface. func (v *VProblem) Error() string { - return fmt.Sprintf("%s on host %s", v.Title, v.Host) + return fmt.Sprintf("%s on host %s, detail: %s", v.Title, v.Host, v.Detail) } // New will return a new VProblem object. Each occurrence must have the diff --git a/vclusterops/adapter_pool.go b/vclusterops/adapter_pool.go index da256e9..8b043d5 100644 --- a/vclusterops/adapter_pool.go +++ b/vclusterops/adapter_pool.go @@ -25,7 +25,7 @@ import ( ) type AdapterPool struct { - log vlog.Printer + logger vlog.Printer // map from host to HTTPAdapter connections map[string]Adapter } @@ -36,23 +36,23 @@ var ( ) // return a singleton instance of the AdapterPool -func getPoolInstance(log vlog.Printer) AdapterPool { +func getPoolInstance(logger 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(log) + poolInstance = makeAdapterPool(logger) }) return poolInstance } -func makeAdapterPool(log vlog.Printer) AdapterPool { +func makeAdapterPool(logger vlog.Printer) AdapterPool { newAdapterPool := AdapterPool{} newAdapterPool.connections = make(map[string]Adapter) - newAdapterPool.log = log.WithName("AdapterPool") + newAdapterPool.logger = logger.WithName("AdapterPool") return newAdapterPool } @@ -82,7 +82,7 @@ func (pool *AdapterPool) sendRequest(clusterHTTPRequest *ClusterHTTPRequest) err resultChannel := make(chan HostHTTPResult, hostCount) // only track the progress of HTTP requests for vcluster CLI - if pool.log.ForCli { + if pool.logger.ForCli { // use context to check whether a step has completed ctx, cancelCtx := context.WithCancel(context.Background()) go progressCheck(ctx, clusterHTTPRequest.Name) @@ -131,7 +131,7 @@ func progressCheck(ctx context.Context, name string) { return case tickTime := <-ticker.C: elapsedTime := tickTime.Sub(startTime) - fmt.Printf("[%s] is still running. %.f seconds spent at this step.\n", + vlog.PrintWithIndent("[%s] is still running. %.f seconds spent at this step.", name, elapsedTime.Seconds()) } } diff --git a/vclusterops/add_node.go b/vclusterops/add_node.go index dc69e55..f8d9634 100644 --- a/vclusterops/add_node.go +++ b/vclusterops/add_node.go @@ -79,9 +79,9 @@ func (o *VAddNodeOptions) validateExtraOptions() error { return util.ValidateRequiredAbsPath(o.DataPrefix, "data path") } -func (o *VAddNodeOptions) validateParseOptions(log vlog.Printer) error { +func (o *VAddNodeOptions) validateParseOptions(logger vlog.Printer) error { // batch 1: validate required parameters - err := o.validateBaseOptions("db_add_node", log) + err := o.validateBaseOptions("db_add_node", logger) if err != nil { return err } @@ -110,8 +110,8 @@ func (o *VAddNodeOptions) analyzeOptions() (err error) { return nil } -func (o *VAddNodeOptions) validateAnalyzeOptions(log vlog.Printer) error { - err := o.validateParseOptions(log) +func (o *VAddNodeOptions) validateAnalyzeOptions(logger vlog.Printer) error { + err := o.validateParseOptions(logger) if err != nil { return err } diff --git a/vclusterops/add_subcluster.go b/vclusterops/add_subcluster.go index d9a6fd0..50da669 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(log vlog.Printer) error { - err := options.validateBaseOptions("db_add_subcluster", log) +func (options *VAddSubclusterOptions) validateRequiredOptions(logger vlog.Printer) error { + err := options.validateBaseOptions("db_add_subcluster", logger) if err != nil { return err } @@ -94,7 +94,7 @@ func (options *VAddSubclusterOptions) validateEonOptions(config *ClusterConfig) return nil } -func (options *VAddSubclusterOptions) validateExtraOptions(log vlog.Printer) error { +func (options *VAddSubclusterOptions) validateExtraOptions(logger 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(log vlog.Printer) err if *options.CloneSC != "" { // TODO remove this log after we supported subcluster clone - log.PrintWarning("option CloneSC is not implemented yet so it will be ignored") + logger.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,7 +124,7 @@ func (options *VAddSubclusterOptions) validateExtraOptions(log vlog.Printer) err } // TODO remove this log after we supported adding subcluster with nodes - log.PrintWarning("options SCRawHosts and SCHosts are not implemented yet so they will be ignored") + logger.PrintWarning("options SCRawHosts and SCHosts are not implemented yet so they will be ignored") } return nil diff --git a/vclusterops/cluster_config.go b/vclusterops/cluster_config.go index 3c444d4..5c6661a 100644 --- a/vclusterops/cluster_config.go +++ b/vclusterops/cluster_config.go @@ -67,7 +67,7 @@ func MakeDatabaseConfig() DatabaseConfig { } // read config information from the YAML file -func ReadConfig(configDirectory string, log vlog.Printer) (ClusterConfig, error) { +func ReadConfig(configDirectory string, logger vlog.Printer) (ClusterConfig, error) { configFilePath := filepath.Join(configDirectory, ConfigFileName) configBytes, err := os.ReadFile(configFilePath) if err != nil { @@ -99,7 +99,7 @@ func ReadConfig(configDirectory string, log vlog.Printer) (ClusterConfig, error) */ clusterConfig := config.Databases - log.PrintInfo("The content of cluster config: %+v\n", clusterConfig) + logger.PrintInfo("The content of cluster config: %+v", clusterConfig) return clusterConfig, nil } @@ -148,7 +148,7 @@ func (c *DatabaseConfig) getHosts() []string { return hostList } -func getConfigFilePath(dbName string, inputConfigDir *string, log vlog.Printer) (string, error) { +func getConfigFilePath(dbName string, inputConfigDir *string, logger vlog.Printer) (string, error) { var configParentPath string // if the input config directory is given and has write permission, @@ -165,7 +165,7 @@ func getConfigFilePath(dbName string, inputConfigDir *string, log vlog.Printer) // as /vertica_cluster.yaml currentDir, err := os.Getwd() if err != nil { - log.Info("Fail to get current directory\n") + logger.Info("Fail to get current directory\n") configParentPath = currentDir } @@ -180,12 +180,12 @@ func getConfigFilePath(dbName string, inputConfigDir *string, log vlog.Printer) return configFilePath, nil } -func backupConfigFile(configFilePath string, log vlog.Printer) error { +func backupConfigFile(configFilePath string, logger vlog.Printer) error { if util.CanReadAccessDir(configFilePath) == nil { // copy file to vertica_cluster.yaml.backup configDirPath := filepath.Dir(configFilePath) configFileBackup := filepath.Join(configDirPath, ConfigBackupName) - log.Info("Config file exists and, creating a backup", "config file", configFilePath, + logger.Info("Config file exists and, creating a backup", "config file", configFilePath, "backup file", configFileBackup) err := util.CopyFile(configFilePath, configFileBackup, ConfigFilePerm) if err != nil { @@ -196,19 +196,19 @@ func backupConfigFile(configFilePath string, log vlog.Printer) error { return nil } -func removeConfigFile(configDirectory string, log vlog.Printer) error { +func removeConfigFile(configDirectory string, logger vlog.Printer) error { configFilePath := filepath.Join(configDirectory, ConfigFileName) configBackupPath := filepath.Join(configDirectory, ConfigBackupName) err := os.RemoveAll(configFilePath) if err != nil { - log.PrintError("Fail to remove the config file %s, detail: %s", configFilePath, err) + logger.PrintError("Fail to remove the config file %s, detail: %s", configFilePath, err) return err } err = os.RemoveAll(configBackupPath) if err != nil { - log.PrintError("Fail to remove the backup config file %s, detail: %s", configBackupPath, err) + logger.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 70b5ea8..aaaaaa0 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(log vlog.Printer) bool { +func (hostResult *HostHTTPResult) isPasswordAndCertificateError(logger vlog.Printer) bool { if !hostResult.isUnauthorizedRequest() { return false } resultString := fmt.Sprintf("%v", hostResult) for _, msg := range wrongCredentialErrMsg { if strings.Contains(resultString, msg) { - log.Error(errors.New(msg), "the user has provided") + logger.Error(errors.New(msg), "the user has provided") return true } } @@ -189,7 +189,7 @@ type ClusterOp interface { // OpBase defines base fields and implements basic functions // for all ops type OpBase struct { - log vlog.Printer + logger vlog.Printer name string hosts []string clusterHTTPRequest ClusterHTTPRequest @@ -203,12 +203,12 @@ func (op *OpBase) getName() string { } func (op *OpBase) parseAndCheckResponse(host, responseContent string, responseObj any) error { - err := util.GetJSONLogErrors(responseContent, &responseObj, op.name, op.log) + err := util.GetJSONLogErrors(responseContent, &responseObj, op.name, op.logger) if err != nil { - op.log.Error(err, "fail to parse response on host, detail", "host", host) + op.logger.Error(err, "fail to parse response on host, detail", "host", host) return err } - op.log.Info("JSON response", "host", host, "responseObj", responseObj) + op.logger.Info("JSON response", "host", host, "responseObj", responseObj) return nil } @@ -235,27 +235,32 @@ func (op *OpBase) setupBasicInfo() { } func (op *OpBase) logResponse(host string, result HostHTTPResult) { - op.log.PrintInfo("[%s] result from host %s summary %s, details: %+v", - op.name, host, result.status.getStatusString(), result) + if result.err != nil { + op.logger.PrintError("[%s] result from host %s summary %s, details: %+v", + op.name, host, result.status.getStatusString(), result.err) + } else { + op.logger.Log.Info("Request succeeded", + "op name", op.name, "host", host, "details", result) + } } func (op *OpBase) logPrepare() { - op.log.Info("Prepare() called", "name", op.name) + op.logger.Info("Prepare() called", "name", op.name) } func (op *OpBase) logExecute() { - op.log.Info("Execute() called", "name", op.name) - op.log.PrintInfo("[%s] is running", op.name) + op.logger.Info("Execute() called", "name", op.name) + op.logger.PrintInfo("[%s] is running", op.name) } func (op *OpBase) logFinalize() { - op.log.Info("Finalize() called", "name", op.name) + op.logger.Info("Finalize() called", "name", op.name) } func (op *OpBase) runExecute(execContext *OpEngineExecContext) error { err := execContext.dispatcher.sendRequest(&op.clusterHTTPRequest) if err != nil { - op.log.Error(err, "Fail to dispatch request, detail", "dispatch request", op.clusterHTTPRequest) + op.logger.Error(err, "Fail to dispatch request, detail", "dispatch request", op.clusterHTTPRequest) return err } return nil @@ -297,7 +302,7 @@ func (op *OpBase) isSkipExecute() bool { func (op *OpBase) hasQuorum(hostCount, primaryNodeCount uint) bool { quorumCount := (primaryNodeCount + 1) / 2 if hostCount < quorumCount { - op.log.PrintError("[%s] Quorum check failed: "+ + op.logger.PrintError("[%s] Quorum check failed: "+ "number of hosts with latest catalog (%d) is not "+ "greater than or equal to 1/2 of number of the primary nodes (%d)\n", op.name, hostCount, primaryNodeCount) @@ -311,7 +316,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) - op.log.Error(err, "fail to execute HTTPS request, detail") + op.logger.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 1611cee..a101576 100644 --- a/vclusterops/cluster_op_engine.go +++ b/vclusterops/cluster_op_engine.go @@ -38,8 +38,8 @@ func (opEngine *VClusterOpEngine) shouldGetCertsFromOptions() bool { return (opEngine.certs.key != "" && opEngine.certs.cert != "" && opEngine.certs.caCert != "") } -func (opEngine *VClusterOpEngine) run(log vlog.Printer) error { - execContext := makeOpEngineExecContext(log) +func (opEngine *VClusterOpEngine) run(logger vlog.Printer) error { + execContext := makeOpEngineExecContext(logger) opEngine.execContext = &execContext findCertsInOptions := opEngine.shouldGetCertsFromOptions() @@ -71,6 +71,8 @@ func (opEngine *VClusterOpEngine) run(log vlog.Printer) error { if err != nil { return fmt.Errorf("finalize failed %w", err) } + + vlog.PrintWithIndent("[%s] is successfully completed", op.getName()) } return nil diff --git a/vclusterops/cluster_op_engine_context.go b/vclusterops/cluster_op_engine_context.go index ef01323..2a7128e 100644 --- a/vclusterops/cluster_op_engine_context.go +++ b/vclusterops/cluster_op_engine_context.go @@ -29,9 +29,9 @@ type OpEngineExecContext struct { dbInfo string // store the db info that retrieved from communal storage } -func makeOpEngineExecContext(log vlog.Printer) OpEngineExecContext { +func makeOpEngineExecContext(logger vlog.Printer) OpEngineExecContext { newOpEngineExecContext := OpEngineExecContext{} - newOpEngineExecContext.dispatcher = makeHTTPRequestDispatcher(log) + newOpEngineExecContext.dispatcher = makeHTTPRequestDispatcher(logger) return newOpEngineExecContext } diff --git a/vclusterops/coordinator_database.go b/vclusterops/coordinator_database.go index 957636e..ee71aba 100644 --- a/vclusterops/coordinator_database.go +++ b/vclusterops/coordinator_database.go @@ -72,9 +72,9 @@ func makeVCoordinationDatabase() VCoordinationDatabase { return VCoordinationDatabase{} } -func (vdb *VCoordinationDatabase) setFromCreateDBOptions(options *VCreateDatabaseOptions, log vlog.Printer) error { +func (vdb *VCoordinationDatabase) setFromCreateDBOptions(options *VCreateDatabaseOptions, logger vlog.Printer) error { // build after validating the options - err := options.validateAnalyzeOptions(log) + err := options.validateAnalyzeOptions(logger) if err != nil { return err } @@ -428,7 +428,7 @@ func (vnode *VCoordinationNode) setFromNodeConfig(nodeConfig *NodeConfig, vdb *V } // WriteClusterConfig updates the yaml config file with the given vdb information -func (vdb *VCoordinationDatabase) WriteClusterConfig(configDir *string, log vlog.Printer) error { +func (vdb *VCoordinationDatabase) WriteClusterConfig(configDir *string, logger vlog.Printer) error { /* build config information */ dbConfig := MakeDatabaseConfig() @@ -454,7 +454,7 @@ func (vdb *VCoordinationDatabase) WriteClusterConfig(configDir *string, log vlog // update cluster config with the given database info clusterConfig := MakeClusterConfig() if checkConfigFileExist(configDir) { - c, err := ReadConfig(*configDir, log) + c, err := ReadConfig(*configDir, logger) if err != nil { return err } @@ -464,14 +464,14 @@ func (vdb *VCoordinationDatabase) WriteClusterConfig(configDir *string, log vlog /* write config to a YAML file */ - configFilePath, err := getConfigFilePath(vdb.Name, configDir, log) + configFilePath, err := getConfigFilePath(vdb.Name, configDir, logger) if err != nil { return err } // if the config file exists already // create its backup before overwriting it - err = backupConfigFile(configFilePath, log) + err = backupConfigFile(configFilePath, logger) if err != nil { return err } diff --git a/vclusterops/create_db.go b/vclusterops/create_db.go index 2bce2cf..ec40685 100644 --- a/vclusterops/create_db.go +++ b/vclusterops/create_db.go @@ -182,12 +182,12 @@ func (opt *VCreateDatabaseOptions) checkExtraNilPointerParams() error { return nil } -func (opt *VCreateDatabaseOptions) validateRequiredOptions(log vlog.Printer) error { +func (opt *VCreateDatabaseOptions) validateRequiredOptions(logger vlog.Printer) error { // validate required parameters with default values if opt.Password == nil { opt.Password = new(string) *opt.Password = "" - log.Info("no password specified, using none") + logger.Info("no password specified, using none") } if !util.StringInArray(*opt.Policy, util.RestartPolicyList) { @@ -315,7 +315,7 @@ func (opt *VCreateDatabaseOptions) validateExtraOptions() error { return nil } -func (opt *VCreateDatabaseOptions) validateParseOptions(log vlog.Printer) error { +func (opt *VCreateDatabaseOptions) validateParseOptions(logger vlog.Printer) error { // check nil pointers in the required options err := opt.checkNilPointerParams() if err != nil { @@ -323,13 +323,13 @@ func (opt *VCreateDatabaseOptions) validateParseOptions(log vlog.Printer) error } // validate base options - err = opt.validateBaseOptions("create_db", log) + err = opt.validateBaseOptions("create_db", logger) if err != nil { return err } // batch 1: validate required parameters without default values - err = opt.validateRequiredOptions(log) + err = opt.validateRequiredOptions(logger) if err != nil { return err } @@ -368,8 +368,8 @@ func (opt *VCreateDatabaseOptions) analyzeOptions() error { return nil } -func (opt *VCreateDatabaseOptions) validateAnalyzeOptions(log vlog.Printer) error { - if err := opt.validateParseOptions(log); err != nil { +func (opt *VCreateDatabaseOptions) validateAnalyzeOptions(logger vlog.Printer) error { + if err := opt.validateParseOptions(logger); err != nil { return err } return opt.analyzeOptions() diff --git a/vclusterops/helpers.go b/vclusterops/helpers.go index 852bb97..9825786 100644 --- a/vclusterops/helpers.go +++ b/vclusterops/helpers.go @@ -35,18 +35,18 @@ const ( // produceTransferConfigOps generates instructions to transfert some config // files from a sourceConfig node to target nodes. -func produceTransferConfigOps(log vlog.Printer, instructions *[]ClusterOp, sourceConfigHost, +func produceTransferConfigOps(logger vlog.Printer, instructions *[]ClusterOp, sourceConfigHost, targetHosts []string, vdb *VCoordinationDatabase) { var verticaConfContent string nmaDownloadVerticaConfigOp := makeNMADownloadConfigOp( - log, "NMADownloadVerticaConfigOp", sourceConfigHost, "config/vertica", &verticaConfContent, vdb) + logger, "NMADownloadVerticaConfigOp", sourceConfigHost, "config/vertica", &verticaConfContent, vdb) nmaUploadVerticaConfigOp := makeNMAUploadConfigOp( - log, "NMAUploadVerticaConfigOp", sourceConfigHost, targetHosts, "config/vertica", &verticaConfContent, vdb) + logger, "NMAUploadVerticaConfigOp", sourceConfigHost, targetHosts, "config/vertica", &verticaConfContent, vdb) var spreadConfContent string nmaDownloadSpreadConfigOp := makeNMADownloadConfigOp( - log, "NMADownloadSpreadConfigOp", sourceConfigHost, "config/spread", &spreadConfContent, vdb) + logger, "NMADownloadSpreadConfigOp", sourceConfigHost, "config/spread", &spreadConfContent, vdb) nmaUploadSpreadConfigOp := makeNMAUploadConfigOp( - log, "NMAUploadSpreadConfigOp", sourceConfigHost, targetHosts, "config/spread", &spreadConfContent, vdb) + logger, "NMAUploadSpreadConfigOp", sourceConfigHost, targetHosts, "config/spread", &spreadConfContent, vdb) *instructions = append(*instructions, &nmaDownloadVerticaConfigOp, &nmaUploadVerticaConfigOp, diff --git a/vclusterops/http_adapter.go b/vclusterops/http_adapter.go index 9f4886d..c5ce070 100644 --- a/vclusterops/http_adapter.go +++ b/vclusterops/http_adapter.go @@ -38,22 +38,22 @@ type HTTPAdapter struct { respBodyHandler responseBodyHandler } -func makeHTTPAdapter(log vlog.Printer) HTTPAdapter { +func makeHTTPAdapter(logger vlog.Printer) HTTPAdapter { newHTTPAdapter := HTTPAdapter{} newHTTPAdapter.name = "HTTPAdapter" - newHTTPAdapter.log = log.WithName(newHTTPAdapter.name) + newHTTPAdapter.logger = logger.WithName(newHTTPAdapter.name) newHTTPAdapter.respBodyHandler = &responseBodyReader{} return newHTTPAdapter } -// makeHTTPDownloadAdapter creates an HTTP adaptor which will +// makeHTTPDownloadAdapter creates an HTTP adapter which will // download a response body to a file via streaming read and // buffered write, rather than copying the body to memory. -func makeHTTPDownloadAdapter(log vlog.Printer, +func makeHTTPDownloadAdapter(logger vlog.Printer, destFilePath string) HTTPAdapter { - newHTTPAdapter := makeHTTPAdapter(log) + newHTTPAdapter := makeHTTPAdapter(logger) newHTTPAdapter.respBodyHandler = &responseBodyDownloader{ - log, + logger, destFilePath, } return newHTTPAdapter @@ -68,7 +68,7 @@ type responseBodyReader struct{} // for downloading response body to file instead of reading into memory type responseBodyDownloader struct { - log vlog.Printer + logger vlog.Printer destFilePath string } @@ -102,7 +102,7 @@ func (adapter *HTTPAdapter) sendRequest(request *HostHTTPRequest, resultChannel port, request.Endpoint, queryParams) - adapter.log.Info("Request URL", "URL", requestURL) + adapter.logger.Info("Request URL", "URL", requestURL) // whether use password (for HTTPS endpoints only) usePassword, err := whetherUsePassword(request) @@ -182,7 +182,7 @@ func (downloader *responseBodyDownloader) readResponseBody(resp *http.Response) if err != nil { err = fmt.Errorf("fail to stream the response body to file %s: %w", downloader.destFilePath, err) } else { - downloader.log.Info("File downloaded", "File", downloader.destFilePath, "Bytes", bytesWritten) + downloader.logger.Info("File downloaded", "File", downloader.destFilePath, "Bytes", bytesWritten) } return "", err } diff --git a/vclusterops/http_request_dispatcher.go b/vclusterops/http_request_dispatcher.go index a3d4c0d..90dfc66 100644 --- a/vclusterops/http_request_dispatcher.go +++ b/vclusterops/http_request_dispatcher.go @@ -22,21 +22,21 @@ type HTTPRequestDispatcher struct { pool AdapterPool } -func makeHTTPRequestDispatcher(log vlog.Printer) HTTPRequestDispatcher { +func makeHTTPRequestDispatcher(logger vlog.Printer) HTTPRequestDispatcher { newHTTPRequestDispatcher := HTTPRequestDispatcher{} newHTTPRequestDispatcher.name = "HTTPRequestDispatcher" - newHTTPRequestDispatcher.log = log.WithName(newHTTPRequestDispatcher.name) + newHTTPRequestDispatcher.logger = logger.WithName(newHTTPRequestDispatcher.name) return newHTTPRequestDispatcher } // set up the pool connection for each host func (dispatcher *HTTPRequestDispatcher) setup(hosts []string) { - dispatcher.pool = getPoolInstance(dispatcher.log) + dispatcher.pool = getPoolInstance(dispatcher.logger) dispatcher.pool.connections = make(map[string]Adapter) for _, host := range hosts { - adapter := makeHTTPAdapter(dispatcher.log) + adapter := makeHTTPAdapter(dispatcher.logger) adapter.host = host dispatcher.pool.connections[host] = &adapter } @@ -45,16 +45,16 @@ func (dispatcher *HTTPRequestDispatcher) setup(hosts []string) { // set up the pool connection for each host to download a file func (dispatcher *HTTPRequestDispatcher) setupForDownload(hosts []string, hostToFilePathsMap map[string]string) { - dispatcher.pool = getPoolInstance(dispatcher.log) + dispatcher.pool = getPoolInstance(dispatcher.logger) for _, host := range hosts { - adapter := makeHTTPDownloadAdapter(dispatcher.log, hostToFilePathsMap[host]) + adapter := makeHTTPDownloadAdapter(dispatcher.logger, hostToFilePathsMap[host]) adapter.host = host dispatcher.pool.connections[host] = &adapter } } func (dispatcher *HTTPRequestDispatcher) sendRequest(clusterHTTPRequest *ClusterHTTPRequest) error { - dispatcher.log.Info("HTTP request dispatcher's sendRequest is called") + dispatcher.logger.Info("HTTP request dispatcher's sendRequest is called") return dispatcher.pool.sendRequest(clusterHTTPRequest) } diff --git a/vclusterops/https_add_subcluster_op.go b/vclusterops/https_add_subcluster_op.go index 441ee01..2b26997 100644 --- a/vclusterops/https_add_subcluster_op.go +++ b/vclusterops/https_add_subcluster_op.go @@ -33,12 +33,12 @@ type HTTPSAddSubclusterOp struct { ctlSetSize int } -func makeHTTPSAddSubclusterOp(log vlog.Printer, useHTTPPassword bool, userName string, httpsPassword *string, +func makeHTTPSAddSubclusterOp(logger vlog.Printer, useHTTPPassword bool, userName string, httpsPassword *string, scName string, isPrimary bool, ctlSetSize int) (HTTPSAddSubclusterOp, error) { httpsAddSubclusterOp := HTTPSAddSubclusterOp{} httpsAddSubclusterOp.name = "HTTPSAddSubclusterOp" httpsAddSubclusterOp.scName = scName - httpsAddSubclusterOp.log = log.WithName(httpsAddSubclusterOp.name) + httpsAddSubclusterOp.logger = logger.WithName(httpsAddSubclusterOp.name) httpsAddSubclusterOp.isSecondary = !isPrimary httpsAddSubclusterOp.ctlSetSize = ctlSetSize diff --git a/vclusterops/https_check_db_running_op.go b/vclusterops/https_check_db_running_op.go index ab10785..fb2c238 100644 --- a/vclusterops/https_check_db_running_op.go +++ b/vclusterops/https_check_db_running_op.go @@ -54,13 +54,13 @@ type HTTPCheckRunningDBOp struct { opType OpType } -func makeHTTPCheckRunningDBOp(log vlog.Printer, hosts []string, +func makeHTTPCheckRunningDBOp(logger vlog.Printer, hosts []string, useHTTPPassword bool, userName string, httpsPassword *string, opType OpType, ) (HTTPCheckRunningDBOp, error) { runningDBChecker := HTTPCheckRunningDBOp{} runningDBChecker.name = "HTTPCheckDBRunningOp" - runningDBChecker.log = log.WithName(runningDBChecker.name) + runningDBChecker.logger = logger.WithName(runningDBChecker.name) runningDBChecker.hosts = hosts runningDBChecker.useHTTPPassword = useHTTPPassword @@ -91,7 +91,7 @@ func (op *HTTPCheckRunningDBOp) setupClusterHTTPRequest(hosts []string) error { } func (op *HTTPCheckRunningDBOp) logPrepare() { - op.log.Info("prepare() called", "opType", op.opType) + op.logger.Info("prepare() called", "opType", op.opType) } func (op *HTTPCheckRunningDBOp) prepare(execContext *OpEngineExecContext) error { @@ -101,26 +101,32 @@ func (op *HTTPCheckRunningDBOp) prepare(execContext *OpEngineExecContext) error } /* HTTPNodeStateResponse example: - {'details':[] - 'node_list':[{'name': 'v_test_db_running_node0001', - 'node_id':'45035996273704982', - 'address': '192.168.1.101', - 'state' : 'UP' - 'database' : 'test_db', - 'is_primary' : true, - 'is_readonly' : false, - 'catalog_path' : "\/data\/test_db\/v_test_db_node0001_catalog\/Catalog" - 'subcluster_name' : '' - 'last_msg_from_node_at':'2023-01-23T15:18:18.44866" - 'down_since' : null - 'build_info' : "v12.0.4-7142c8b01f373cc1aa60b1a8feff6c40bfb7afe8" - }]} + { + "details": null, + "node_list":[{ + "name": "v_test_db_running_node0001", + "node_id": 45035996273704982, + "address": "192.168.1.101", + "state" : "UP", + "database" : "test_db", + "is_primary" : true, + "is_readonly" : false, + "catalog_path" : "\/data\/test_db\/v_test_db_node0001_catalog\/Catalog", + "data_path": ["\/data\/test_db\/v_test_db_node0001_data"], + "depot_path": "\/data\/test_db\/v_test_db_node0001_depot", + "subcluster_name" : "default_subcluster", + "last_msg_from_node_at":"2023-01-23T15:18:18.44866", + "down_since" : null, + "build_info" : "v12.0.4-7142c8b01f373cc1aa60b1a8feff6c40bfb7afe8" + }] + } */ // // or a message if the endpoint doesn't return a well-structured JSON, examples: -// {'message': 'Local node has not joined cluster yet, HTTP server will accept connections when the node has joined the cluster\n'} +// {"message": "Local node has not joined cluster yet, HTTP server will accept connections when the node has joined the cluster\n"} // {"message": "Wrong password\n"} -type HTTPNodeStateResponse map[string][]map[string]string +type NodeList []map[string]any +type HTTPNodeStateResponse map[string]NodeList func (op *HTTPCheckRunningDBOp) isDBRunningOnHost(host string, responseObj HTTPNodeStateResponse) (running bool, msg string, err error) { @@ -171,20 +177,6 @@ func (op *HTTPCheckRunningDBOp) processResult(_ *OpEngineExecContext) error { // print msg msg := "" for host, result := range op.clusterHTTPRequest.ResultCollection { - resSummaryStr := SuccessResult - // VER-87303: it's possible that there's a DB running with a different password - if !result.isHTTPRunning() { - resSummaryStr = FailureResult - } - - if result.err != nil { - op.log.PrintInfo("[%s] result from host %s summary %s, error: %+v", - op.name, host, resSummaryStr, result.err) - } else { - op.log.PrintInfo("[%s] result from host %s summary %s, details: %+v.", - op.name, host, resSummaryStr, result) - } - if !result.isPassing() { allErrs = errors.Join(allErrs, result.err) } @@ -214,35 +206,37 @@ func (op *HTTPCheckRunningDBOp) processResult(_ *OpEngineExecContext) error { return fmt.Errorf("[%s] error happened during checking DB running on host %s, details: %w", op.name, host, err) } - op.log.Info("DB running", "host", host, "dbRunning", dbRunning, "checkMsg", checkMsg) + op.logger.Info("DB running", "host", host, "dbRunning", dbRunning, "checkMsg", checkMsg) // return at least one check msg to user msg = checkMsg } // log info - op.log.Info("check db running results", "up hosts", upHosts, "down hosts", downHosts, "hosts with status unknown", exceptionHosts) + op.logger.Info("check db running results", "up hosts", upHosts, "down hosts", downHosts, "hosts with status unknown", exceptionHosts) // no DB is running on hosts, return a passed result if len(upHosts) == 0 { return nil } - op.log.PrintInfo("%s\n", msg) + op.logger.Info("Check DB running", "detail", msg) switch op.opType { case CreateDB: - op.log.PrintInfo("Aborting database creation\n") + op.logger.PrintInfo("Aborting database creation") case StopDB: - op.log.PrintInfo("The database has not been down yet\n") + op.logger.PrintInfo("The database has not been down yet") case StartDB: - op.log.PrintInfo("Aborting database start\n") + op.logger.PrintInfo("Aborting database start") case ReviveDB: - op.log.PrintInfo("Aborting database revival\n") + op.logger.PrintInfo("Aborting database revival") } - return allErrs + + // when db is running, append an error to allErrs for stopping VClusterOpEngine + return errors.Join(allErrs, errors.New(msg)) } func (op *HTTPCheckRunningDBOp) execute(execContext *OpEngineExecContext) error { - op.log.Info("Execute() called", "opType", op.opType) + op.logger.Info("Execute() called", "opType", op.opType) switch op.opType { case CreateDB, StartDB, ReviveDB: return op.checkDBConnection(execContext) @@ -285,7 +279,7 @@ func (op *HTTPCheckRunningDBOp) pollForDBDown(execContext *OpEngineExecContext) // request again. We are waiting for all nodes to be down, which is a // success result from processContext. if err != nil { - op.log.Info("failure when checking node status", "err", err) + op.logger.Info("failure when checking node status", "err", err) } else { return nil } @@ -293,7 +287,7 @@ func (op *HTTPCheckRunningDBOp) pollForDBDown(execContext *OpEngineExecContext) } // timeout msg := fmt.Sprintf("the DB is still up after %s seconds", timeoutSecondStr) - op.log.PrintWarning(msg) + op.logger.PrintWarning(msg) return errors.New(msg) } diff --git a/vclusterops/https_check_node_state_op.go b/vclusterops/https_check_node_state_op.go index bf05855..c251d38 100644 --- a/vclusterops/https_check_node_state_op.go +++ b/vclusterops/https_check_node_state_op.go @@ -28,13 +28,13 @@ type HTTPCheckNodeStateOp struct { OpHTTPSBase } -func makeHTTPCheckNodeStateOp(log vlog.Printer, hosts []string, +func makeHTTPCheckNodeStateOp(logger vlog.Printer, hosts []string, useHTTPPassword bool, userName string, httpsPassword *string, ) (HTTPCheckNodeStateOp, error) { nodeStateChecker := HTTPCheckNodeStateOp{} - nodeStateChecker.log = log.WithName(nodeStateChecker.name) + nodeStateChecker.logger = logger.WithName(nodeStateChecker.name) nodeStateChecker.name = "HTTPCheckNodeStateOp" // The hosts are the ones we are going to talk to. // They can be a subset of the actual host information that we return, @@ -89,7 +89,7 @@ func (op *HTTPCheckNodeStateOp) processResult(execContext *OpEngineExecContext) op.logResponse(host, result) if result.isUnauthorizedRequest() { - op.log.PrintError("[%s] unauthorized request: %s", op.name, result.content) + op.logger.PrintError("[%s] unauthorized request: %s", op.name, result.content) // return here because we assume that // we will get the same error across other nodes allErrs = errors.Join(allErrs, result.err) @@ -99,7 +99,7 @@ func (op *HTTPCheckNodeStateOp) processResult(execContext *OpEngineExecContext) if !result.isPassing() { // for any error, we continue to the next node if result.isInternalError() { - op.log.PrintError("[%s] internal error of the /nodes endpoint: %s", op.name, result.content) + op.logger.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_check_subcluster_op.go b/vclusterops/https_check_subcluster_op.go index 974db5d..811b4db 100644 --- a/vclusterops/https_check_subcluster_op.go +++ b/vclusterops/https_check_subcluster_op.go @@ -30,11 +30,11 @@ type HTTPSCheckSubclusterOp struct { ctlSetSize int } -func makeHTTPSCheckSubclusterOp(log vlog.Printer, useHTTPPassword bool, userName string, httpsPassword *string, +func makeHTTPSCheckSubclusterOp(logger vlog.Printer, useHTTPPassword bool, userName string, httpsPassword *string, scName string, isPrimary bool, ctlSetSize int) (HTTPSCheckSubclusterOp, error) { httpsCheckSubclusterOp := HTTPSCheckSubclusterOp{} httpsCheckSubclusterOp.name = "HTTPSCheckSubclusterOp" - httpsCheckSubclusterOp.log = log.WithName(httpsCheckSubclusterOp.name) + httpsCheckSubclusterOp.logger = logger.WithName(httpsCheckSubclusterOp.name) httpsCheckSubclusterOp.scName = scName httpsCheckSubclusterOp.isSecondary = !isPrimary httpsCheckSubclusterOp.ctlSetSize = ctlSetSize diff --git a/vclusterops/https_create_cluster_depot_op.go b/vclusterops/https_create_cluster_depot_op.go index 4d3b532..d2bab70 100644 --- a/vclusterops/https_create_cluster_depot_op.go +++ b/vclusterops/https_create_cluster_depot_op.go @@ -32,11 +32,11 @@ type HTTPSCreateDepotOp struct { RequestParams map[string]string } -func makeHTTPSCreateClusterDepotOp(log vlog.Printer, vdb *VCoordinationDatabase, hosts []string, +func makeHTTPSCreateClusterDepotOp(logger vlog.Printer, vdb *VCoordinationDatabase, hosts []string, useHTTPPassword bool, userName string, httpsPassword *string) (HTTPSCreateDepotOp, error) { httpsCreateDepotOp := HTTPSCreateDepotOp{} httpsCreateDepotOp.name = "HTTPSCreateDepotOp" - httpsCreateDepotOp.log = log.WithName(httpsCreateDepotOp.name) + httpsCreateDepotOp.logger = logger.WithName(httpsCreateDepotOp.name) httpsCreateDepotOp.hosts = hosts httpsCreateDepotOp.useHTTPPassword = useHTTPPassword diff --git a/vclusterops/https_create_node_op.go b/vclusterops/https_create_node_op.go index f7322a6..a900347 100644 --- a/vclusterops/https_create_node_op.go +++ b/vclusterops/https_create_node_op.go @@ -29,12 +29,12 @@ type HTTPSCreateNodeOp struct { RequestParams map[string]string } -func makeHTTPSCreateNodeOp(log vlog.Printer, newNodeHosts []string, bootstrapHost []string, +func makeHTTPSCreateNodeOp(logger vlog.Printer, newNodeHosts []string, bootstrapHost []string, useHTTPPassword bool, userName string, httpsPassword *string, vdb *VCoordinationDatabase, scName string) (HTTPSCreateNodeOp, error) { createNodeOp := HTTPSCreateNodeOp{} createNodeOp.name = "HTTPSCreateNodeOp" - createNodeOp.log = log.WithName(createNodeOp.name) + createNodeOp.logger = logger.WithName(createNodeOp.name) createNodeOp.hosts = bootstrapHost createNodeOp.RequestParams = make(map[string]string) // HTTPS create node endpoint requires passing everything before node name diff --git a/vclusterops/https_create_nodes_depot_op.go b/vclusterops/https_create_nodes_depot_op.go index 92d0a56..eb29648 100644 --- a/vclusterops/https_create_nodes_depot_op.go +++ b/vclusterops/https_create_nodes_depot_op.go @@ -31,12 +31,12 @@ type HTTPSCreateNodesDepotOp struct { } // makeHTTPSCreateNodesDepotOp will make an op that call vertica-http service to create depot for the new nodes -func makeHTTPSCreateNodesDepotOp(log vlog.Printer, vdb *VCoordinationDatabase, nodes []string, +func makeHTTPSCreateNodesDepotOp(logger vlog.Printer, vdb *VCoordinationDatabase, nodes []string, useHTTPPassword bool, userName string, httpsPassword *string, ) (HTTPSCreateNodesDepotOp, error) { httpsCreateNodesDepotOp := HTTPSCreateNodesDepotOp{} httpsCreateNodesDepotOp.name = "HTTPSCreateNodesDepotOp" - httpsCreateNodesDepotOp.log = log.WithName(httpsCreateNodesDepotOp.name) + httpsCreateNodesDepotOp.logger = logger.WithName(httpsCreateNodesDepotOp.name) httpsCreateNodesDepotOp.hosts = nodes httpsCreateNodesDepotOp.useHTTPPassword = useHTTPPassword httpsCreateNodesDepotOp.HostNodeMap = vdb.HostNodeMap diff --git a/vclusterops/https_drop_node_op.go b/vclusterops/https_drop_node_op.go index 6c45699..914ccc9 100644 --- a/vclusterops/https_drop_node_op.go +++ b/vclusterops/https_drop_node_op.go @@ -29,7 +29,7 @@ type HTTPSDropNodeOp struct { RequestParams map[string]string } -func makeHTTPSDropNodeOp(log vlog.Printer, vnode string, +func makeHTTPSDropNodeOp(logger vlog.Printer, vnode string, initiatorHost []string, useHTTPPassword bool, userName string, @@ -37,7 +37,7 @@ func makeHTTPSDropNodeOp(log vlog.Printer, vnode string, isEon bool) (HTTPSDropNodeOp, error) { dropNodeOp := HTTPSDropNodeOp{} dropNodeOp.name = "HTTPSDropNodeOp" - dropNodeOp.log = log.WithName(dropNodeOp.name) + dropNodeOp.logger = logger.WithName(dropNodeOp.name) dropNodeOp.hosts = initiatorHost dropNodeOp.targetHost = vnode dropNodeOp.useHTTPPassword = useHTTPPassword diff --git a/vclusterops/https_find_subcluster_op.go b/vclusterops/https_find_subcluster_op.go index d4fffdb..626d79e 100644 --- a/vclusterops/https_find_subcluster_op.go +++ b/vclusterops/https_find_subcluster_op.go @@ -33,13 +33,13 @@ type HTTPSFindSubclusterOp struct { // a subcluster by name and find the default subcluster. // When ignoreNotFound is true, the op will not error out if // the given cluster name is not found. -func makeHTTPSFindSubclusterOp(log vlog.Printer, hosts []string, useHTTPPassword bool, +func makeHTTPSFindSubclusterOp(logger vlog.Printer, hosts []string, useHTTPPassword bool, userName string, httpsPassword *string, scName string, ignoreNotFound bool, ) (HTTPSFindSubclusterOp, error) { op := HTTPSFindSubclusterOp{} op.name = "HTTPSFindSubclusterOp" - op.log = log.WithName(op.name) + op.logger = logger.WithName(op.name) op.hosts = hosts op.scName = scName op.ignoreNotFound = ignoreNotFound @@ -143,13 +143,13 @@ func (op *HTTPSFindSubclusterOp) processResult(execContext *OpEngineExecContext) for _, scInfo := range scResp.SCInfoList { if scInfo.SCName == op.scName { foundNamedSc = true - op.log.Info(`subcluster exists in the database`, "subcluster", scInfo.SCName, "dbName", op.name) + op.logger.Info(`subcluster exists in the database`, "subcluster", scInfo.SCName, "dbName", op.name) } if scInfo.IsDefault { // store the default sc name into execContext foundDefaultSc = true execContext.defaultSCName = scInfo.SCName - op.log.Info(`found default subcluster in the database`, "subcluster", scInfo.SCName, "dbName", op.name) + op.logger.Info(`found default subcluster in the database`, "subcluster", scInfo.SCName, "dbName", op.name) } if foundNamedSc && foundDefaultSc { break diff --git a/vclusterops/https_get_cluster_info_op.go b/vclusterops/https_get_cluster_info_op.go index 51c9589..fff28ac 100644 --- a/vclusterops/https_get_cluster_info_op.go +++ b/vclusterops/https_get_cluster_info_op.go @@ -30,12 +30,12 @@ type httpsGetClusterInfoOp struct { vdb *VCoordinationDatabase } -func makeHTTPSGetClusterInfoOp(log vlog.Printer, dbName string, hosts []string, +func makeHTTPSGetClusterInfoOp(logger vlog.Printer, dbName string, hosts []string, useHTTPPassword bool, userName string, httpsPassword *string, vdb *VCoordinationDatabase, ) (httpsGetClusterInfoOp, error) { op := httpsGetClusterInfoOp{} op.name = "HTTPSGetClusterInfoOp" - op.log = log.WithName(op.name) + op.logger = logger.WithName(op.name) op.dbName = dbName op.hosts = hosts op.vdb = vdb diff --git a/vclusterops/https_get_nodes_info_op.go b/vclusterops/https_get_nodes_info_op.go index acd5954..7c888d1 100644 --- a/vclusterops/https_get_nodes_info_op.go +++ b/vclusterops/https_get_nodes_info_op.go @@ -31,12 +31,12 @@ type httpsGetNodesInfoOp struct { vdb *VCoordinationDatabase } -func makeHTTPSGetNodesInfoOp(log vlog.Printer, dbName string, hosts []string, +func makeHTTPSGetNodesInfoOp(logger vlog.Printer, dbName string, hosts []string, useHTTPPassword bool, userName string, httpsPassword *string, vdb *VCoordinationDatabase, ) (httpsGetNodesInfoOp, error) { op := httpsGetNodesInfoOp{} op.name = "HTTPSGetNodeInfoOp" - op.log = log.WithName(op.name) + op.logger = logger.WithName(op.name) op.dbName = dbName op.hosts = hosts op.vdb = vdb @@ -124,7 +124,7 @@ func (op *httpsGetNodesInfoOp) processResult(_ *OpEngineExecContext) error { dbPath := "/" + node.Database index := strings.Index(node.CatalogPath, dbPath) if index == -1 { - op.log.PrintWarning("[%s] failed to get catalog prefix because catalog path %s does not contain database name %s", + op.logger.PrintWarning("[%s] failed to get catalog prefix because catalog path %s does not contain database name %s", op.name, node.CatalogPath, node.Database) } op.vdb.CatalogPrefix = node.CatalogPath[:index] diff --git a/vclusterops/https_get_up_nodes_op.go b/vclusterops/https_get_up_nodes_op.go index 7217488..bddd999 100644 --- a/vclusterops/https_get_up_nodes_op.go +++ b/vclusterops/https_get_up_nodes_op.go @@ -27,15 +27,16 @@ import ( type HTTPSGetUpNodesOp struct { OpBase OpHTTPSBase - DBName string + DBName string + noUpHostsOk bool } -func makeHTTPSGetUpNodesOp(log vlog.Printer, dbName string, hosts []string, +func makeHTTPSGetUpNodesOp(logger vlog.Printer, dbName string, hosts []string, useHTTPPassword bool, userName string, httpsPassword *string, ) (HTTPSGetUpNodesOp, error) { httpsGetUpNodesOp := HTTPSGetUpNodesOp{} httpsGetUpNodesOp.name = "HTTPSGetUpNodesOp" - httpsGetUpNodesOp.log = log.WithName(httpsGetUpNodesOp.name) + httpsGetUpNodesOp.logger = logger.WithName(httpsGetUpNodesOp.name) httpsGetUpNodesOp.hosts = hosts httpsGetUpNodesOp.useHTTPPassword = useHTTPPassword httpsGetUpNodesOp.DBName = dbName @@ -51,6 +52,10 @@ func makeHTTPSGetUpNodesOp(log vlog.Printer, dbName string, hosts []string, return httpsGetUpNodesOp, nil } +func (op *HTTPSGetUpNodesOp) allowNoUpHosts() { + op.noUpHostsOk = true +} + func (op *HTTPSGetUpNodesOp) setupClusterHTTPRequest(hosts []string) error { for _, host := range hosts { httpRequest := HostHTTPRequest{} @@ -157,26 +162,39 @@ func (op *HTTPSGetUpNodesOp) processResult(execContext *OpEngineExecContext) err } } + ignoreErrors := op.processHostLists(upHosts, exceptionHosts, downHosts, execContext) + if ignoreErrors { + return nil + } + + return errors.Join(allErrs, fmt.Errorf("no up nodes detected")) +} + +func (op *HTTPSGetUpNodesOp) finalize(_ *OpEngineExecContext) error { + return nil +} + +// processHostLists stashes the up hosts, and if there are no up hosts, prints and logs +// down or erratic hosts. Additionally, it determines if the op should fail or not. +func (op *HTTPSGetUpNodesOp) processHostLists(upHosts map[string]struct{}, + exceptionHosts, downHosts []string, + execContext *OpEngineExecContext) (ignoreErrors bool) { if len(upHosts) > 0 { for host := range upHosts { execContext.upHosts = append(execContext.upHosts, host) } // sorting the up hosts will be helpful for picking up the initiator in later instructions sort.Strings(execContext.upHosts) - return nil + return true } if len(exceptionHosts) > 0 { - op.log.PrintError(`[%s] fail to call https endpoint of database %s on hosts %s`, op.name, op.DBName, exceptionHosts) + op.logger.PrintError(`[%s] fail to call https endpoint of database %s on hosts %s`, op.name, op.DBName, exceptionHosts) } if len(downHosts) > 0 { - op.log.PrintError(`[%s] did not detect database %s running on hosts %v`, op.name, op.DBName, downHosts) + op.logger.PrintError(`[%s] did not detect database %s running on hosts %v`, op.name, op.DBName, downHosts) } - return errors.Join(allErrs, fmt.Errorf("no up nodes detected")) -} - -func (op *HTTPSGetUpNodesOp) finalize(_ *OpEngineExecContext) error { - return nil + return op.noUpHostsOk } diff --git a/vclusterops/https_install_packages_op.go b/vclusterops/https_install_packages_op.go index c263a39..216cfca 100644 --- a/vclusterops/https_install_packages_op.go +++ b/vclusterops/https_install_packages_op.go @@ -28,12 +28,12 @@ type HTTPSInstallPackagesOp struct { OpHTTPSBase } -func makeHTTPSInstallPackagesOp(log vlog.Printer, hosts []string, useHTTPPassword bool, +func makeHTTPSInstallPackagesOp(logger vlog.Printer, hosts []string, useHTTPPassword bool, userName string, httpsPassword *string, ) (HTTPSInstallPackagesOp, error) { installPackagesOp := HTTPSInstallPackagesOp{} installPackagesOp.name = "HTTPSInstallPackagesOp" - installPackagesOp.log = log.WithName(installPackagesOp.name) + installPackagesOp.logger = logger.WithName(installPackagesOp.name) installPackagesOp.hosts = hosts err := util.ValidateUsernameAndPassword(installPackagesOp.name, useHTTPPassword, userName) @@ -123,7 +123,7 @@ func (op *HTTPSInstallPackagesOp) processResult(_ *OpEngineExecContext) error { allErrs = errors.Join(allErrs, err) } - op.log.PrintInfo("[%s] installed packages: %v", op.name, installedPackages) + op.logger.PrintInfo("[%s] installed packages: %v", op.name, installedPackages) } return allErrs } diff --git a/vclusterops/https_mark_design_ksafe_op.go b/vclusterops/https_mark_design_ksafe_op.go index 12ffd66..e429fb9 100644 --- a/vclusterops/https_mark_design_ksafe_op.go +++ b/vclusterops/https_mark_design_ksafe_op.go @@ -35,7 +35,7 @@ type HTTPSMarkDesignKSafeOp struct { } func makeHTTPSMarkDesignKSafeOp( - log vlog.Printer, + logger vlog.Printer, hosts []string, useHTTPPassword bool, userName string, @@ -44,7 +44,7 @@ func makeHTTPSMarkDesignKSafeOp( ) (HTTPSMarkDesignKSafeOp, error) { httpsMarkDesignKSafeOp := HTTPSMarkDesignKSafeOp{} httpsMarkDesignKSafeOp.name = "HTTPSMarkDesignKsafeOp" - httpsMarkDesignKSafeOp.log = log.WithName(httpsMarkDesignKSafeOp.name) + httpsMarkDesignKSafeOp.logger = logger.WithName(httpsMarkDesignKSafeOp.name) httpsMarkDesignKSafeOp.hosts = hosts httpsMarkDesignKSafeOp.useHTTPPassword = useHTTPPassword @@ -145,7 +145,7 @@ func (op *HTTPSMarkDesignKSafeOp) processResult(_ *OpEngineExecContext) error { continue } - op.log.PrintInfo(`[%s] The K-safety value of the database is set as %d`, op.name, ksafeValue) + op.logger.PrintInfo(`[%s] The K-safety value of the database is set as %d`, op.name, ksafeValue) } return allErrs diff --git a/vclusterops/https_mark_nodes_ephemeral_op.go b/vclusterops/https_mark_nodes_ephemeral_op.go index 56f56ae..d56019d 100644 --- a/vclusterops/https_mark_nodes_ephemeral_op.go +++ b/vclusterops/https_mark_nodes_ephemeral_op.go @@ -28,14 +28,14 @@ type HTTPSMarkEphemeralNodeOp struct { targetNodeName string } -func makeHTTPSMarkEphemeralNodeOp(log vlog.Printer, nodeName string, +func makeHTTPSMarkEphemeralNodeOp(logger vlog.Printer, nodeName string, initiatorHost []string, useHTTPPassword bool, userName string, httpsPassword *string) (HTTPSMarkEphemeralNodeOp, error) { op := HTTPSMarkEphemeralNodeOp{} op.name = "HTTPSMarkEphemeralNodeOp" - op.log = log.WithName(op.name) + op.logger = logger.WithName(op.name) op.hosts = initiatorHost op.targetNodeName = nodeName op.useHTTPPassword = useHTTPPassword diff --git a/vclusterops/https_poll_node_state_op.go b/vclusterops/https_poll_node_state_op.go index 93c1b0e..e6a3a5b 100644 --- a/vclusterops/https_poll_node_state_op.go +++ b/vclusterops/https_poll_node_state_op.go @@ -53,11 +53,11 @@ type HTTPSPollNodeStateOp struct { cmdType CmdType } -func makeHTTPSPollNodeStateOpHelper(log vlog.Printer, hosts []string, +func makeHTTPSPollNodeStateOpHelper(logger vlog.Printer, hosts []string, useHTTPPassword bool, userName string, httpsPassword *string) (HTTPSPollNodeStateOp, error) { httpsPollNodeStateOp := HTTPSPollNodeStateOp{} httpsPollNodeStateOp.name = "HTTPSPollNodeStateOp" - httpsPollNodeStateOp.log = log.WithName(httpsPollNodeStateOp.name) + httpsPollNodeStateOp.logger = logger.WithName(httpsPollNodeStateOp.name) httpsPollNodeStateOp.hosts = hosts httpsPollNodeStateOp.useHTTPPassword = useHTTPPassword @@ -71,10 +71,10 @@ func makeHTTPSPollNodeStateOpHelper(log vlog.Printer, hosts []string, return httpsPollNodeStateOp, nil } -func makeHTTPSPollNodeStateOpWithTimeoutAndCommand(log vlog.Printer, hosts []string, +func makeHTTPSPollNodeStateOpWithTimeoutAndCommand(logger vlog.Printer, hosts []string, useHTTPPassword bool, userName string, httpsPassword *string, timeout int, cmdType CmdType) (HTTPSPollNodeStateOp, error) { - op, err := makeHTTPSPollNodeStateOpHelper(log, hosts, useHTTPPassword, userName, httpsPassword) + op, err := makeHTTPSPollNodeStateOpHelper(logger, hosts, useHTTPPassword, userName, httpsPassword) if err != nil { return op, err } @@ -83,10 +83,10 @@ func makeHTTPSPollNodeStateOpWithTimeoutAndCommand(log vlog.Printer, hosts []str return op, nil } -func makeHTTPSPollNodeStateOp(log vlog.Printer, hosts []string, +func makeHTTPSPollNodeStateOp(logger vlog.Printer, hosts []string, useHTTPPassword bool, userName string, httpsPassword *string) (HTTPSPollNodeStateOp, error) { - httpsPollNodeStateOp, err := makeHTTPSPollNodeStateOpHelper(log, hosts, useHTTPPassword, userName, httpsPassword) + httpsPollNodeStateOp, err := makeHTTPSPollNodeStateOpHelper(logger, hosts, useHTTPPassword, userName, httpsPassword) if err != nil { return httpsPollNodeStateOp, err } @@ -139,12 +139,14 @@ func (op *HTTPSPollNodeStateOp) finalize(_ *OpEngineExecContext) error { } func (op *HTTPSPollNodeStateOp) processResult(execContext *OpEngineExecContext) error { + vlog.PrintWithIndent("[%s] expecting %d up host(s)", op.name, len(op.hosts)) + err := pollState(op, execContext) if err != nil { // show the host that is not UP msg := fmt.Sprintf("Cannot get the correct response from the host %s after %d seconds, details: %s", op.currentHost, op.timeout, err) - op.log.PrintError(msg) + op.logger.PrintError(msg) return errors.New(msg) } return nil @@ -164,8 +166,9 @@ type NodesInfo struct { } func (op *HTTPSPollNodeStateOp) shouldStopPolling() (bool, error) { + upNodeCount := 0 + for host, result := range op.clusterHTTPRequest.ResultCollection { - op.logResponse(host, result) op.currentHost = host // when we get timeout error, we know that the host is unreachable/dead @@ -177,10 +180,10 @@ 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(op.log) { + if result.isPasswordAndCertificateError(op.logger) { switch op.cmdType { case StartDBCmd, RestartNodeCmd: - op.log.PrintError("[%s] The credentials are incorrect. 'Catalog Sync' will not be executed.", + op.logger.PrintError("[%s] The credentials are incorrect. 'Catalog Sync' will not be executed.", op.name) return true, fmt.Errorf("[%s] wrong password/certificate for https service on host %s, but the nodes' startup have been in progress."+ "Please use vsql to check the nodes' status and manually run sync_catalog vsql command 'select sync_catalog()'", op.name, host) @@ -193,7 +196,7 @@ func (op *HTTPSPollNodeStateOp) shouldStopPolling() (bool, error) { nodesInfo := NodesInfo{} err := op.parseAndCheckResponse(host, result.content, &nodesInfo) if err != nil { - op.log.PrintError("[%s] fail to parse result on host %s, details: %s", + op.logger.PrintError("[%s] fail to parse result on host %s, details: %s", op.name, host, err) return true, err } @@ -203,7 +206,7 @@ func (op *HTTPSPollNodeStateOp) shouldStopPolling() (bool, error) { if len(nodesInfo.NodeList) == 1 { nodeInfo := nodesInfo.NodeList[0] if nodeInfo.State == util.NodeUpState { - continue + upNodeCount++ } } else { // if NMA endpoint cannot function well on any of the hosts, we do not want to retry polling @@ -212,12 +215,14 @@ func (op *HTTPSPollNodeStateOp) shouldStopPolling() (bool, error) { op.name, len(nodesInfo.NodeList), host) } } + } - // if we cannot get correct response in current node, we assume the node is not up and wait for the next poll. - // if the node is busy and cannot return correct response in this poll, the following polls should get correct response from it. + if upNodeCount < len(op.hosts) { + vlog.PrintWithIndent("[%s] %d host(s) up", op.name, upNodeCount) return false, nil } - op.log.PrintInfo("All nodes are up") + vlog.PrintWithIndent("[%s] All nodes are up", op.name) + return true, nil } diff --git a/vclusterops/https_poll_subscription_state_op.go b/vclusterops/https_poll_subscription_state_op.go index ef92926..91e83f0 100644 --- a/vclusterops/https_poll_subscription_state_op.go +++ b/vclusterops/https_poll_subscription_state_op.go @@ -28,11 +28,11 @@ type httpsPollSubscriptionStateOp struct { timeout int } -func makeHTTPSPollSubscriptionStateOp(log vlog.Printer, hosts []string, +func makeHTTPSPollSubscriptionStateOp(logger vlog.Printer, hosts []string, useHTTPPassword bool, userName string, httpsPassword *string) (httpsPollSubscriptionStateOp, error) { op := httpsPollSubscriptionStateOp{} op.name = "HTTPSPollSubscriptionStateOp" - op.log = log.WithName(op.name) + op.logger = logger.WithName(op.name) op.hosts = hosts op.useHTTPPassword = useHTTPPassword op.timeout = StartupPollingTimeout @@ -129,7 +129,7 @@ func (op *httpsPollSubscriptionStateOp) shouldStopPolling() (bool, error) { for host, result := range op.clusterHTTPRequest.ResultCollection { op.logResponse(host, result) - if result.isPasswordAndCertificateError(op.log) { + if result.isPasswordAndCertificateError(op.logger) { return true, fmt.Errorf("[%s] wrong password/certificate for https service on host %s", op.name, host) } @@ -137,7 +137,7 @@ func (op *httpsPollSubscriptionStateOp) shouldStopPolling() (bool, error) { if result.isPassing() { err := op.parseAndCheckResponse(host, result.content, &subscriptionList) if err != nil { - op.log.PrintError("[%s] fail to parse result on host %s, details: %s", + op.logger.PrintError("[%s] fail to parse result on host %s, details: %s", op.name, host, err) return true, err } @@ -149,12 +149,12 @@ func (op *httpsPollSubscriptionStateOp) shouldStopPolling() (bool, error) { } } - op.log.PrintInfo("All subscriptions are ACTIVE") + op.logger.PrintInfo("All subscriptions are ACTIVE") return true, nil } } // this could happen if ResultCollection is empty - op.log.PrintError("[%s] empty result received from the provided hosts %v", op.name, op.hosts) + op.logger.PrintError("[%s] empty result received from the provided hosts %v", op.name, op.hosts) return false, nil } diff --git a/vclusterops/https_rebalance_cluster_op.go b/vclusterops/https_rebalance_cluster_op.go index fe558e4..89673e4 100644 --- a/vclusterops/https_rebalance_cluster_op.go +++ b/vclusterops/https_rebalance_cluster_op.go @@ -32,11 +32,11 @@ type HTTPSRebalanceClusterOp struct { } // makeHTTPSRebalanceClusterOp will make an op that call vertica-http service to rebalance the cluster -func makeHTTPSRebalanceClusterOp(log vlog.Printer, initiatorHost []string, useHTTPPassword bool, userName string, +func makeHTTPSRebalanceClusterOp(logger vlog.Printer, initiatorHost []string, useHTTPPassword bool, userName string, httpsPassword *string) (HTTPSRebalanceClusterOp, error) { httpsRBCOp := HTTPSRebalanceClusterOp{} httpsRBCOp.name = "HTTPSRebalanceClusterOp" - httpsRBCOp.log = log.WithName(httpsRBCOp.name) + httpsRBCOp.logger = logger.WithName(httpsRBCOp.name) httpsRBCOp.hosts = initiatorHost httpsRBCOp.useHTTPPassword = useHTTPPassword diff --git a/vclusterops/https_rebalance_subcluster_shards_op.go b/vclusterops/https_rebalance_subcluster_shards_op.go index 2e07fb5..1de7a83 100644 --- a/vclusterops/https_rebalance_subcluster_shards_op.go +++ b/vclusterops/https_rebalance_subcluster_shards_op.go @@ -32,11 +32,11 @@ type HTTPSRebalanceSubclusterShardsOp struct { } // makeHTTPSRebalanceSubclusterShardsOp creates an op that calls vertica-http service to rebalance shards of a subcluster -func makeHTTPSRebalanceSubclusterShardsOp(log vlog.Printer, bootstrapHost []string, useHTTPPassword bool, userName string, +func makeHTTPSRebalanceSubclusterShardsOp(logger vlog.Printer, bootstrapHost []string, useHTTPPassword bool, userName string, httpsPassword *string, scName string) (HTTPSRebalanceSubclusterShardsOp, error) { httpsRBSCShardsOp := HTTPSRebalanceSubclusterShardsOp{} httpsRBSCShardsOp.name = "HTTPSRebalanceSubclusterShardsOp" - httpsRBSCShardsOp.log = log.WithName(httpsRBSCShardsOp.name) + httpsRBSCShardsOp.logger = logger.WithName(httpsRBSCShardsOp.name) httpsRBSCShardsOp.hosts = bootstrapHost httpsRBSCShardsOp.scName = scName diff --git a/vclusterops/https_reload_spread_op.go b/vclusterops/https_reload_spread_op.go index 5524cd0..609ec50 100644 --- a/vclusterops/https_reload_spread_op.go +++ b/vclusterops/https_reload_spread_op.go @@ -28,12 +28,12 @@ type HTTPSReloadSpreadOp struct { OpHTTPSBase } -func makeHTTPSReloadSpreadOpWithInitiator(log vlog.Printer, initHosts []string, +func makeHTTPSReloadSpreadOpWithInitiator(logger vlog.Printer, initHosts []string, useHTTPPassword bool, userName string, httpsPassword *string) (HTTPSReloadSpreadOp, error) { httpsReloadSpreadOp := HTTPSReloadSpreadOp{} httpsReloadSpreadOp.name = "HTTPSReloadSpreadOp" - httpsReloadSpreadOp.log = log.WithName(httpsReloadSpreadOp.name) + httpsReloadSpreadOp.logger = logger.WithName(httpsReloadSpreadOp.name) httpsReloadSpreadOp.hosts = initHosts httpsReloadSpreadOp.useHTTPPassword = useHTTPPassword @@ -46,9 +46,9 @@ func makeHTTPSReloadSpreadOpWithInitiator(log vlog.Printer, initHosts []string, return httpsReloadSpreadOp, nil } -func makeHTTPSReloadSpreadOp(log vlog.Printer, useHTTPPassword bool, +func makeHTTPSReloadSpreadOp(logger vlog.Printer, useHTTPPassword bool, userName string, httpsPassword *string) (HTTPSReloadSpreadOp, error) { - return makeHTTPSReloadSpreadOpWithInitiator(log, nil, useHTTPPassword, userName, httpsPassword) + return makeHTTPSReloadSpreadOpWithInitiator(logger, nil, useHTTPPassword, userName, httpsPassword) } func (op *HTTPSReloadSpreadOp) setupClusterHTTPRequest(hosts []string) error { diff --git a/vclusterops/https_spread_remove_node_op.go b/vclusterops/https_spread_remove_node_op.go index b3ad346..4026aae 100644 --- a/vclusterops/https_spread_remove_node_op.go +++ b/vclusterops/https_spread_remove_node_op.go @@ -29,11 +29,11 @@ type HTTPSSpreadRemoveNodeOp struct { RequestParams map[string]string } -func makeHTTPSSpreadRemoveNodeOp(log vlog.Printer, hostsToRemove []string, initiatorHost []string, useHTTPPassword bool, +func makeHTTPSSpreadRemoveNodeOp(logger vlog.Printer, hostsToRemove []string, initiatorHost []string, useHTTPPassword bool, userName string, httpsPassword *string, hostNodeMap vHostNodeMap) (HTTPSSpreadRemoveNodeOp, error) { op := HTTPSSpreadRemoveNodeOp{} op.name = "HTTPSSpreadRemoveNodeOp" - op.log = log.WithName(op.name) + op.logger = logger.WithName(op.name) op.hosts = initiatorHost op.useHTTPPassword = useHTTPPassword diff --git a/vclusterops/https_startup_command_op.go b/vclusterops/https_startup_command_op.go index a965276..e042eae 100644 --- a/vclusterops/https_startup_command_op.go +++ b/vclusterops/https_startup_command_op.go @@ -29,11 +29,11 @@ type httpsStartUpCommandOp struct { vdb *VCoordinationDatabase } -func makeHTTPSStartUpCommandOp(log vlog.Printer, useHTTPPassword bool, userName string, httpsPassword *string, +func makeHTTPSStartUpCommandOp(logger vlog.Printer, useHTTPPassword bool, userName string, httpsPassword *string, vdb *VCoordinationDatabase) (httpsStartUpCommandOp, error) { op := httpsStartUpCommandOp{} op.name = "HTTPSStartUpCommandOp" - op.log = log.WithName(op.name) + op.logger = logger.WithName(op.name) op.useHTTPPassword = useHTTPPassword op.vdb = vdb diff --git a/vclusterops/https_stop_db_op.go b/vclusterops/https_stop_db_op.go index a28dd1c..c78e3be 100644 --- a/vclusterops/https_stop_db_op.go +++ b/vclusterops/https_stop_db_op.go @@ -31,11 +31,11 @@ type HTTPSStopDBOp struct { RequestParams map[string]string } -func makeHTTPSStopDBOp(log vlog.Printer, useHTTPPassword bool, userName string, +func makeHTTPSStopDBOp(logger vlog.Printer, useHTTPPassword bool, userName string, httpsPassword *string, timeout *int) (HTTPSStopDBOp, error) { httpsStopDBOp := HTTPSStopDBOp{} httpsStopDBOp.name = "HTTPSStopDBOp" - httpsStopDBOp.log = log.WithName(httpsStopDBOp.name) + httpsStopDBOp.logger = logger.WithName(httpsStopDBOp.name) httpsStopDBOp.useHTTPPassword = useHTTPPassword // set the query params, "timeout" is optional diff --git a/vclusterops/https_sync_catalog_op.go b/vclusterops/https_sync_catalog_op.go index 2321e62..1040f4a 100644 --- a/vclusterops/https_sync_catalog_op.go +++ b/vclusterops/https_sync_catalog_op.go @@ -29,11 +29,11 @@ type HTTPSSyncCatalogOp struct { OpHTTPSBase } -func makeHTTPSSyncCatalogOp(log vlog.Printer, hosts []string, useHTTPPassword bool, +func makeHTTPSSyncCatalogOp(logger vlog.Printer, hosts []string, useHTTPPassword bool, userName string, httpsPassword *string) (HTTPSSyncCatalogOp, error) { op := HTTPSSyncCatalogOp{} op.name = "HTTPSSyncCatalogOp" - op.log = log.WithName(op.name) + op.logger = logger.WithName(op.name) op.hosts = hosts op.useHTTPPassword = useHTTPPassword @@ -47,9 +47,9 @@ func makeHTTPSSyncCatalogOp(log vlog.Printer, hosts []string, useHTTPPassword bo return op, nil } -func makeHTTPSSyncCatalogOpWithoutHosts(log vlog.Printer, useHTTPPassword bool, +func makeHTTPSSyncCatalogOpWithoutHosts(logger vlog.Printer, useHTTPPassword bool, userName string, httpsPassword *string) (HTTPSSyncCatalogOp, error) { - return makeHTTPSSyncCatalogOp(log, nil, useHTTPPassword, userName, httpsPassword) + return makeHTTPSSyncCatalogOp(logger, nil, useHTTPPassword, userName, httpsPassword) } func (op *HTTPSSyncCatalogOp) setupClusterHTTPRequest(hosts []string) error { @@ -111,7 +111,7 @@ func (op *HTTPSSyncCatalogOp) processResult(_ *OpEngineExecContext) error { err = fmt.Errorf(`[%s] response does not contain field "new_truncation_version"`, op.name) allErrs = errors.Join(allErrs, err) } - op.log.PrintInfo(`[%s] the_latest_truncation_catalog_version: %s"`, op.name, version) + op.logger.PrintInfo(`[%s] the_latest_truncation_catalog_version: %s"`, op.name, version) } else { allErrs = errors.Join(allErrs, result.err) } diff --git a/vclusterops/nma_bootstrap_catalog_op.go b/vclusterops/nma_bootstrap_catalog_op.go index 04a98a2..2475b40 100644 --- a/vclusterops/nma_bootstrap_catalog_op.go +++ b/vclusterops/nma_bootstrap_catalog_op.go @@ -53,13 +53,13 @@ type bootstrapCatalogRequestData struct { } func makeNMABootstrapCatalogOp( - log vlog.Printer, + logger vlog.Printer, vdb *VCoordinationDatabase, options *VCreateDatabaseOptions, bootstrapHosts []string) (NMABootstrapCatalogOp, error) { nmaBootstrapCatalogOp := NMABootstrapCatalogOp{} nmaBootstrapCatalogOp.name = "NMABootstrapCatalogOp" - nmaBootstrapCatalogOp.log = log.WithName(nmaBootstrapCatalogOp.name) + nmaBootstrapCatalogOp.logger = logger.WithName(nmaBootstrapCatalogOp.name) // usually, only one node need bootstrap catalog nmaBootstrapCatalogOp.hosts = bootstrapHosts @@ -136,7 +136,7 @@ func (op *NMABootstrapCatalogOp) updateRequestBody(execContext *OpEngineExecCont dataBytes, err := json.Marshal(op.hostRequestBodyMap[host]) if err != nil { - op.log.Error(err, `[%s] fail to marshal request data to JSON string`, op.name) + op.logger.Error(err, `[%s] fail to marshal request data to JSON string`, op.name) return err } op.marshaledRequestBodyMap[host] = string(dataBytes) @@ -146,7 +146,7 @@ func (op *NMABootstrapCatalogOp) updateRequestBody(execContext *OpEngineExecCont maskedData.maskSensitiveInfo() maskedRequestBodyMap[host] = maskedData } - op.log.Info("request data", "op name", op.name, "bodyMap", maskedRequestBodyMap) + op.logger.Info("request data", "op name", op.name, "bodyMap", maskedRequestBodyMap) return nil } diff --git a/vclusterops/nma_delete_dir_op.go b/vclusterops/nma_delete_dir_op.go index 160b781..b36b2a7 100644 --- a/vclusterops/nma_delete_dir_op.go +++ b/vclusterops/nma_delete_dir_op.go @@ -21,13 +21,13 @@ type deleteDirParams struct { } func makeNMADeleteDirectoriesOp( - log vlog.Printer, + logger vlog.Printer, vdb *VCoordinationDatabase, forceDelete bool, ) (NMADeleteDirectoriesOp, error) { nmaDeleteDirectoriesOp := NMADeleteDirectoriesOp{} nmaDeleteDirectoriesOp.name = "NMADeleteDirectoriesOp" - nmaDeleteDirectoriesOp.log = log.WithName(nmaDeleteDirectoriesOp.name) + nmaDeleteDirectoriesOp.logger = logger.WithName(nmaDeleteDirectoriesOp.name) nmaDeleteDirectoriesOp.hosts = vdb.HostList err := nmaDeleteDirectoriesOp.buildRequestBody(vdb, forceDelete) @@ -71,7 +71,7 @@ func (op *NMADeleteDirectoriesOp) buildRequestBody( } op.hostRequestBodyMap[h] = string(dataBytes) - op.log.Info("delete directory params", "host", h, "params", p) + op.logger.Info("delete directory params", "host", h, "params", p) } return nil diff --git a/vclusterops/nma_download_config.go b/vclusterops/nma_download_config.go index eda5ce7..ce8e96d 100644 --- a/vclusterops/nma_download_config.go +++ b/vclusterops/nma_download_config.go @@ -32,7 +32,7 @@ type NMADownloadConfigOp struct { } func makeNMADownloadConfigOp( - log vlog.Printer, + logger vlog.Printer, opName string, sourceConfigHost []string, endpoint string, @@ -41,7 +41,7 @@ func makeNMADownloadConfigOp( ) NMADownloadConfigOp { nmaDownloadConfigOp := NMADownloadConfigOp{} nmaDownloadConfigOp.name = opName - nmaDownloadConfigOp.log = log.WithName(nmaDownloadConfigOp.name) + nmaDownloadConfigOp.logger = logger.WithName(nmaDownloadConfigOp.name) nmaDownloadConfigOp.hosts = sourceConfigHost nmaDownloadConfigOp.endpoint = endpoint nmaDownloadConfigOp.fileContent = fileContent @@ -133,8 +133,8 @@ func (op *NMADownloadConfigOp) processResult(_ *OpEngineExecContext) error { var allErrs error for host, result := range op.clusterHTTPRequest.ResultCollection { // VER-88362 will re-enable the result details and hide sensitive info in it - op.log.PrintInfo("[%s] result from host %s summary %s", - op.name, host, result.status.getStatusString()) + op.logger.Info("Download config file result", + "op name", op.name, "host", host, "status", result.status.getStatusString()) if result.isPassing() { // The content of config file will be stored as content of the response *op.fileContent = result.content diff --git a/vclusterops/nma_download_file_op.go b/vclusterops/nma_download_file_op.go index 298a5cd..824f0d8 100644 --- a/vclusterops/nma_download_file_op.go +++ b/vclusterops/nma_download_file_op.go @@ -83,11 +83,11 @@ func (e *ReviveDBNodeCountMismatchError) Error() string { e.ReviveDBStep, e.FailureHost, e.NumOfNewNodes, e.NumOfOldNodes) } -func makeNMADownloadFileOp(log vlog.Printer, newNodes []string, sourceFilePath, destinationFilePath, catalogPath string, +func makeNMADownloadFileOp(logger vlog.Printer, newNodes []string, sourceFilePath, destinationFilePath, catalogPath string, configurationParameters map[string]string, vdb *VCoordinationDatabase) (NMADownloadFileOp, error) { op := NMADownloadFileOp{} op.name = "NMADownloadFileOp" - op.log = log.WithName(op.name) + op.logger = logger.WithName(op.name) initiator := getInitiator(newNodes) op.hosts = []string{initiator} op.vdb = vdb @@ -113,9 +113,9 @@ func makeNMADownloadFileOp(log vlog.Printer, newNodes []string, sourceFilePath, return op, nil } -func makeNMADownloadFileOpForRevive(log vlog.Printer, newNodes []string, sourceFilePath, destinationFilePath, catalogPath string, +func makeNMADownloadFileOpForRevive(logger vlog.Printer, newNodes []string, sourceFilePath, destinationFilePath, catalogPath string, configurationParameters map[string]string, vdb *VCoordinationDatabase, displayOnly, ignoreClusterLease bool) (NMADownloadFileOp, error) { - op, err := makeNMADownloadFileOp(log, newNodes, sourceFilePath, destinationFilePath, + op, err := makeNMADownloadFileOp(logger, newNodes, sourceFilePath, destinationFilePath, catalogPath, configurationParameters, vdb) if err != nil { return op, err @@ -194,7 +194,7 @@ func (op *NMADownloadFileOp) processResult(execContext *OpEngineExecContext) err result := strings.TrimSpace(response.Result) if result != respSuccResult { err = fmt.Errorf(`[%s] fail to download file on host %s, error result in the response is %s`, op.name, host, result) - op.log.Error(err, "fail to download file, detail") + op.logger.Error(err, "fail to download file, detail") allErrs = errors.Join(allErrs, err) break } @@ -289,7 +289,7 @@ func (op *NMADownloadFileOp) buildVDBFromClusterConfig(descFileContent fileConte func (op *NMADownloadFileOp) clusterLeaseCheck(clusterLeaseExpiration string) error { if op.ignoreClusterLease { - op.log.PrintWarning("Skipping cluster lease check\n") + op.logger.PrintWarning("Skipping cluster lease check\n") return nil } @@ -305,6 +305,6 @@ func (op *NMADownloadFileOp) clusterLeaseCheck(clusterLeaseExpiration string) er return &ClusterLeaseNotExpiredError{Expiration: clusterLeaseExpiration} } - op.log.PrintInfo("Cluster lease check has passed. We proceed to revive the database") + op.logger.PrintInfo("Cluster lease check has passed. We proceed to revive the database") return nil } diff --git a/vclusterops/nma_get_healthy_nodes_op.go b/vclusterops/nma_get_healthy_nodes_op.go index 196b842..f3f2336 100644 --- a/vclusterops/nma_get_healthy_nodes_op.go +++ b/vclusterops/nma_get_healthy_nodes_op.go @@ -30,11 +30,11 @@ type NMAGetHealthyNodesOp struct { vdb *VCoordinationDatabase } -func makeNMAGetHealthyNodesOp(log vlog.Printer, hosts []string, +func makeNMAGetHealthyNodesOp(logger vlog.Printer, hosts []string, vdb *VCoordinationDatabase) NMAGetHealthyNodesOp { nmaGetHealthyNodesOp := NMAGetHealthyNodesOp{} nmaGetHealthyNodesOp.name = "NMAGetHealthyNodesOp" - nmaGetHealthyNodesOp.log = log.WithName(nmaGetHealthyNodesOp.name) + nmaGetHealthyNodesOp.logger = logger.WithName(nmaGetHealthyNodesOp.name) nmaGetHealthyNodesOp.hosts = hosts nmaGetHealthyNodesOp.vdb = vdb return nmaGetHealthyNodesOp @@ -80,12 +80,12 @@ func (op *NMAGetHealthyNodesOp) processResult(_ *OpEngineExecContext) error { if err == nil { op.vdb.HostList = append(op.vdb.HostList, host) } else { - op.log.Error(err, "NMA health check response malformed from host", "Host", host) - op.log.PrintWarning("Skipping unhealthy host %s", host) + op.logger.Error(err, "NMA health check response malformed from host", "Host", host) + op.logger.PrintWarning("Skipping unhealthy host %s", host) } } else { - op.log.Error(result.err, "Host is not reachable", "Host", host) - op.log.PrintWarning("Skipping unreachable host %s", host) + op.logger.Error(result.err, "Host is not reachable", "Host", host) + op.logger.PrintWarning("Skipping unreachable host %s", host) } } if len(op.vdb.HostList) == 0 { diff --git a/vclusterops/nma_get_nodes_info_op.go b/vclusterops/nma_get_nodes_info_op.go index 10a71fe..d71556e 100644 --- a/vclusterops/nma_get_nodes_info_op.go +++ b/vclusterops/nma_get_nodes_info_op.go @@ -31,13 +31,13 @@ type nmaGetNodesInfoOp struct { vdb *VCoordinationDatabase } -func makeNMAGetNodesInfoOp(log vlog.Printer, hosts []string, +func makeNMAGetNodesInfoOp(logger vlog.Printer, hosts []string, dbName, catalogPrefix string, ignoreInternalErrors bool, vdb *VCoordinationDatabase) nmaGetNodesInfoOp { op := nmaGetNodesInfoOp{} op.name = "NMAGetNodesInfoOp" - op.log = log.WithName(op.name) + op.logger = logger.WithName(op.name) op.hosts = hosts op.dbName = dbName op.catalogPrefix = catalogPrefix @@ -87,8 +87,8 @@ func (op *nmaGetNodesInfoOp) processResult(_ *OpEngineExecContext) error { err := op.parseAndCheckResponse(host, result.content, &vnode) if err != nil { if op.ignoreInternalErrors { - op.log.Error(err, "NMA node info response malformed from host", "Host", host) - op.log.PrintWarning("Host %s returned unparsable node info. Skipping host.", host) + op.logger.Error(err, "NMA node info response malformed from host", "Host", host) + op.logger.PrintWarning("Host %s returned unparsable node info. Skipping host.", host) } else { return errors.Join(allErrs, err) } @@ -97,11 +97,11 @@ func (op *nmaGetNodesInfoOp) processResult(_ *OpEngineExecContext) error { op.vdb.HostNodeMap[host] = &vnode } } else if result.isInternalError() && op.ignoreInternalErrors { - op.log.Error(result.err, "NMA node info reported internal error", "Host", host) - op.log.PrintWarning("Host %s reported internal error to node info query. Skipping host.", host) + op.logger.Error(result.err, "NMA node info reported internal error", "Host", host) + op.logger.PrintWarning("Host %s reported internal error to node info query. Skipping host.", host) } else if result.isTimeout() && op.ignoreInternalErrors { // it's unlikely for a node to pass health check but time out here, so leave default timeout limit - op.log.PrintWarning("Host %s timed out on node info query. Skipping host.", host) + op.logger.PrintWarning("Host %s timed out on node info query. Skipping host.", host) } else { allErrs = errors.Join(allErrs, result.err) } diff --git a/vclusterops/nma_get_scrutinize_tar_op.go b/vclusterops/nma_get_scrutinize_tar_op.go index 4517c0a..0044bc4 100644 --- a/vclusterops/nma_get_scrutinize_tar_op.go +++ b/vclusterops/nma_get_scrutinize_tar_op.go @@ -25,29 +25,31 @@ import ( ) type NMAGetScrutinizeTarOp struct { - OpBase - id string - hostNodeNameMap map[string]string // must correspond to host list exactly! - batch string + ScrutinizeOpBase } -func makeNMAGetScrutinizeTarOp(log vlog.Printer, +func makeNMAGetScrutinizeTarOp(logger vlog.Printer, id, batch string, hosts []string, hostNodeNameMap map[string]string) (NMAGetScrutinizeTarOp, error) { + // base members op := NMAGetScrutinizeTarOp{} op.name = "NMAGetScrutinizeTarOp" - op.log = log.WithName(op.name) + op.logger = logger.WithName(op.name) + op.hosts = hosts + + // scrutinize members op.id = id op.batch = batch op.hostNodeNameMap = hostNodeNameMap - op.hosts = hosts + op.httpMethod = GetMethod // the caller is responsible for making sure hosts and maps match up exactly err := validateHostMaps(hosts, hostNodeNameMap) if err != nil { return op, err } + err = op.createOutputDir() return op, err } @@ -72,23 +74,6 @@ func (op *NMAGetScrutinizeTarOp) createOutputDir() error { return nil } -func (op *NMAGetScrutinizeTarOp) setupClusterHTTPRequest(hosts []string) error { - op.clusterHTTPRequest = ClusterHTTPRequest{} - op.clusterHTTPRequest.RequestCollection = make(map[string]HostHTTPRequest) - op.setVersionToSemVar() - - for _, host := range hosts { - nodeName := op.hostNodeNameMap[host] - - httpRequest := HostHTTPRequest{} - httpRequest.Method = GetMethod - httpRequest.buildNMAEndpoint(scrutinizeURLPrefix + op.id + "/" + nodeName + "/" + op.batch) - op.clusterHTTPRequest.RequestCollection[host] = httpRequest - } - - return nil -} - func (op *NMAGetScrutinizeTarOp) prepare(execContext *OpEngineExecContext) error { hostToFilePathsMap := map[string]string{} for _, host := range op.hosts { @@ -122,17 +107,17 @@ func (op *NMAGetScrutinizeTarOp) processResult(_ *OpEngineExecContext) error { op.logResponse(host, result) if result.isPassing() { - op.log.Info("Retrieved tarball", + op.logger.Info("Retrieved tarball", "Host", host, "Node", op.hostNodeNameMap[host], "Batch", op.batch) } else { - op.log.Error(result.err, "Failed to retrieve tarball", + op.logger.Error(result.err, "Failed to retrieve tarball", "Host", host, "Node", op.hostNodeNameMap[host], "Batch", op.batch) if result.isInternalError() { - op.log.PrintWarning("Failed to tar batch %s on host %s. Skipping.", op.batch, host) + op.logger.PrintWarning("Failed to tar batch %s on host %s. Skipping.", op.batch, host) } else { err := fmt.Errorf("failed to retrieve tarball batch %s on host %s, details %w", op.batch, host, result.err) diff --git a/vclusterops/nma_health_op.go b/vclusterops/nma_health_op.go index f3568b4..eb0f441 100644 --- a/vclusterops/nma_health_op.go +++ b/vclusterops/nma_health_op.go @@ -25,10 +25,10 @@ type NMAHealthOp struct { OpBase } -func makeNMAHealthOp(log vlog.Printer, hosts []string) NMAHealthOp { +func makeNMAHealthOp(logger vlog.Printer, hosts []string) NMAHealthOp { nmaHealthOp := NMAHealthOp{} nmaHealthOp.name = "NMAHealthOp" - nmaHealthOp.log = log.WithName(nmaHealthOp.name) + nmaHealthOp.logger = logger.WithName(nmaHealthOp.name) nmaHealthOp.hosts = hosts return nmaHealthOp } diff --git a/vclusterops/nma_load_remote_catalog_op.go b/vclusterops/nma_load_remote_catalog_op.go index a3cfeb7..5f2f50d 100644 --- a/vclusterops/nma_load_remote_catalog_op.go +++ b/vclusterops/nma_load_remote_catalog_op.go @@ -46,11 +46,11 @@ type loadRemoteCatalogRequestData struct { Parameters map[string]string `json:"parameters,omitempty"` } -func makeNMALoadRemoteCatalogOp(log vlog.Printer, oldHosts []string, configurationParameters map[string]string, +func makeNMALoadRemoteCatalogOp(logger vlog.Printer, oldHosts []string, configurationParameters map[string]string, vdb *VCoordinationDatabase, timeout uint) nmaLoadRemoteCatalogOp { op := nmaLoadRemoteCatalogOp{} op.name = "NMALoadRemoteCatalogOp" - op.log = log.WithName(op.name) + op.logger = logger.WithName(op.name) op.hosts = vdb.HostList op.oldHosts = oldHosts op.configurationParameters = configurationParameters @@ -180,7 +180,7 @@ func (op *nmaLoadRemoteCatalogOp) processResult(_ *OpEngineExecContext) error { // quorum check if !op.hasQuorum(successPrimaryNodeCount, op.primaryNodeCount) { err := fmt.Errorf("[%s] fail to load catalog on enough primary nodes. Success count: %d", op.name, successPrimaryNodeCount) - op.log.Error(err, "fail to load catalog, detail") + op.logger.Error(err, "fail to load catalog, detail") allErrs = errors.Join(allErrs, err) return allErrs } diff --git a/vclusterops/nma_network_profile_op.go b/vclusterops/nma_network_profile_op.go index f7863c8..8f665b9 100644 --- a/vclusterops/nma_network_profile_op.go +++ b/vclusterops/nma_network_profile_op.go @@ -27,10 +27,10 @@ type NMANetworkProfileOp struct { OpBase } -func makeNMANetworkProfileOp(log vlog.Printer, hosts []string) NMANetworkProfileOp { +func makeNMANetworkProfileOp(logger vlog.Printer, hosts []string) NMANetworkProfileOp { nmaNetworkProfileOp := NMANetworkProfileOp{} nmaNetworkProfileOp.name = "NMANetworkProfileOp" - nmaNetworkProfileOp.log = log.WithName(nmaNetworkProfileOp.name) + nmaNetworkProfileOp.logger = logger.WithName(nmaNetworkProfileOp.name) nmaNetworkProfileOp.hosts = hosts return nmaNetworkProfileOp } diff --git a/vclusterops/nma_prepare_directories_op.go b/vclusterops/nma_prepare_directories_op.go index 87879eb..4a732b6 100644 --- a/vclusterops/nma_prepare_directories_op.go +++ b/vclusterops/nma_prepare_directories_op.go @@ -41,11 +41,11 @@ type prepareDirectoriesRequestData struct { IgnoreParent bool `json:"ignore_parent"` } -func makeNMAPrepareDirectoriesOp(log vlog.Printer, hostNodeMap vHostNodeMap, +func makeNMAPrepareDirectoriesOp(logger vlog.Printer, hostNodeMap vHostNodeMap, forceCleanup, forRevive bool) (NMAPrepareDirectoriesOp, error) { nmaPrepareDirectoriesOp := NMAPrepareDirectoriesOp{} nmaPrepareDirectoriesOp.name = "NMAPrepareDirectoriesOp" - nmaPrepareDirectoriesOp.log = log.WithName(nmaPrepareDirectoriesOp.name) + nmaPrepareDirectoriesOp.logger = logger.WithName(nmaPrepareDirectoriesOp.name) nmaPrepareDirectoriesOp.forceCleanup = forceCleanup nmaPrepareDirectoriesOp.forRevive = forRevive @@ -79,7 +79,7 @@ func (op *NMAPrepareDirectoriesOp) setupRequestBody(hostNodeMap vHostNodeMap) er op.hostRequestBodyMap[host] = string(dataBytes) } - op.log.Info("request data", "op name", op.name, "hostRequestBodyMap", op.hostRequestBodyMap) + op.logger.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 aaf6195..2b166ba 100644 --- a/vclusterops/nma_re_ip_op.go +++ b/vclusterops/nma_re_ip_op.go @@ -34,13 +34,13 @@ type NMAReIPOp struct { trimReIPData bool } -func makeNMAReIPOp(log vlog.Printer, +func makeNMAReIPOp(logger vlog.Printer, reIPList []ReIPInfo, vdb *VCoordinationDatabase, trimReIPData bool) NMAReIPOp { op := NMAReIPOp{} op.name = "NMAReIPOp" - op.log = log.WithName(op.name) + op.logger = logger.WithName(op.name) op.reIPList = reIPList op.vdb = vdb op.trimReIPData = trimReIPData @@ -69,13 +69,13 @@ func (op *NMAReIPOp) updateRequestBody(_ *OpEngineExecContext) error { p.ReIPInfoList = op.reIPList dataBytes, err := json.Marshal(p) if err != nil { - op.log.Error(err, `[%s] fail to marshal request data to JSON string, detail %s`, op.name) + op.logger.Error(err, `[%s] fail to marshal request data to JSON string, detail %s`, op.name) return err } op.hostRequestBodyMap[host] = string(dataBytes) } - op.log.Info("request data", "op name", op.name, "hostRequestBodyMap", op.hostRequestBodyMap) + op.logger.Info("request data", "op name", op.name, "hostRequestBodyMap", op.hostRequestBodyMap) return nil } @@ -153,7 +153,7 @@ func (op *NMAReIPOp) trimReIPList(execContext *OpEngineExecContext) error { } // otherwise, trim the re-ip list - op.log.Info("re-ip list is trimmed", "trimmed re-ip list", trimmedReIPList) + op.logger.Info("re-ip list is trimmed", "trimmed re-ip list", trimmedReIPList) } op.reIPList = trimmedReIPList @@ -181,7 +181,7 @@ func (op *NMAReIPOp) whetherSkipReIP(execContext *OpEngineExecContext) bool { } } - op.log.PrintInfo("[%s] all target addresses already exist in the catalog, no need to re-ip.", + op.logger.PrintInfo("[%s] all target addresses already exist in the catalog, no need to re-ip.", op.name) return true } diff --git a/vclusterops/nma_read_catalog_editor_op.go b/vclusterops/nma_read_catalog_editor_op.go index 8d0133f..7755794 100644 --- a/vclusterops/nma_read_catalog_editor_op.go +++ b/vclusterops/nma_read_catalog_editor_op.go @@ -34,21 +34,21 @@ type NMAReadCatalogEditorOp struct { // makeNMAReadCatalogEditorOpWithInitiator creates an op to read catalog editor info. // Initiator is needed when creating new nodes func makeNMAReadCatalogEditorOpWithInitiator( - log vlog.Printer, + logger vlog.Printer, initiator []string, vdb *VCoordinationDatabase, ) (NMAReadCatalogEditorOp, error) { op := NMAReadCatalogEditorOp{} op.name = "NMAReadCatalogEditorOp" - op.log = log.WithName(op.name) + op.logger = logger.WithName(op.name) op.initiator = initiator op.vdb = vdb return op, nil } // makeNMAReadCatalogEditorOp creates an op to read catalog editor info. -func makeNMAReadCatalogEditorOp(log vlog.Printer, vdb *VCoordinationDatabase) (NMAReadCatalogEditorOp, error) { - return makeNMAReadCatalogEditorOpWithInitiator(log, []string{}, vdb) +func makeNMAReadCatalogEditorOp(logger vlog.Printer, vdb *VCoordinationDatabase) (NMAReadCatalogEditorOp, error) { + return makeNMAReadCatalogEditorOpWithInitiator(logger, []string{}, vdb) } func (op *NMAReadCatalogEditorOp) setupClusterHTTPRequest(hosts []string) error { @@ -60,7 +60,7 @@ func (op *NMAReadCatalogEditorOp) setupClusterHTTPRequest(hosts []string) error catalogPath, ok := op.catalogPathMap[host] if !ok { err := fmt.Errorf("[%s] cannot find catalog path of host %s", op.name, host) - op.log.Error(err, "fail to find catalog path, detail") + op.logger.Error(err, "fail to find catalog path, detail") return err } httpRequest.QueryParams = map[string]string{"catalog_path": catalogPath} diff --git a/vclusterops/nma_spread_security_op.go b/vclusterops/nma_spread_security_op.go index 8a4fb19..ae3f534 100644 --- a/vclusterops/nma_spread_security_op.go +++ b/vclusterops/nma_spread_security_op.go @@ -41,14 +41,14 @@ const spreadKeyTypeVertica = "vertica" // makeNMASpreadSecurityOp will create the op to set or rotate the key for // spread encryption. func makeNMASpreadSecurityOp( - log vlog.Printer, + logger vlog.Printer, keyType string, ) nmaSpreadSecurityOp { return nmaSpreadSecurityOp{ OpBase: OpBase{ - log: log, - name: "NMASpreadSecurityOp", - hosts: nil, // We always set this at runtime from read catalog editor + logger: logger.WithName("NMASpreadSecurityOp"), + name: "NMASpreadSecurityOp", + hosts: nil, // We always set this at runtime from read catalog editor }, catalogPathMap: nil, // Set at runtime after reading the catalog editor keyType: keyType, @@ -176,7 +176,7 @@ func (op *nmaSpreadSecurityOp) generateSecurityDetails() (string, error) { } // Note, we log the key ID for info purposes and is safe because it isn't // sensitive. NEVER log the spreadKey. - op.log.Info("generating spread key", "keyID", keyID) + op.logger.Info("generating spread key", "keyID", keyID) return fmt.Sprintf(`{\"%s\":\"%s\"}`, keyID, spreadKey), nil } diff --git a/vclusterops/nma_stage_error_report_op.go b/vclusterops/nma_stage_error_report_op.go new file mode 100644 index 0000000..a54dcec --- /dev/null +++ b/vclusterops/nma_stage_error_report_op.go @@ -0,0 +1,105 @@ +/* + (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 vclusterops + +import ( + "encoding/json" + "fmt" + + "github.com/vertica/vcluster/vclusterops/vlog" +) + +type NMAStageErrorReportOp struct { + ScrutinizeOpBase +} + +type stageErrorReportRequestData struct { + CatalogPath string `json:"catalog_path"` +} + +type stageErrorReportResponseData struct { + Name string `json:"name"` + SizeBytes int64 `json:"size_bytes"` + ModTime string `json:"mod_time"` +} + +func makeNMAStageErrorReportOp(logger vlog.Printer, + id string, + hosts []string, + hostNodeNameMap map[string]string, + hostCatPathMap map[string]string) (NMAStageErrorReportOp, error) { + // base members + op := NMAStageErrorReportOp{} + op.name = "NMAStageErrorReportOp" + op.logger = logger.WithName(op.name) + op.hosts = hosts + + // scrutinize members + op.id = id + op.batch = scrutinizeBatchContext + op.hostNodeNameMap = hostNodeNameMap + op.hostCatPathMap = hostCatPathMap + op.httpMethod = PostMethod + op.urlSuffix = "/ErrorReport.txt" + + // the caller is responsible for making sure hosts and maps match up exactly + err := validateHostMaps(hosts, hostNodeNameMap, hostCatPathMap) + return op, err +} + +func (op *NMAStageErrorReportOp) setupRequestBody(hosts []string) error { + op.hostRequestBodyMap = make(map[string]string, len(hosts)) + for _, host := range hosts { + stageErrorReportData := stageErrorReportRequestData{} + stageErrorReportData.CatalogPath = op.hostCatPathMap[host] + + dataBytes, err := json.Marshal(stageErrorReportData) + if err != nil { + return fmt.Errorf("[%s] fail to marshal request data to JSON string, detail %w", op.name, err) + } + + op.hostRequestBodyMap[host] = string(dataBytes) + } + + return nil +} + +func (op *NMAStageErrorReportOp) prepare(execContext *OpEngineExecContext) error { + err := op.setupRequestBody(op.hosts) + if err != nil { + return err + } + execContext.dispatcher.setup(op.hosts) + + return op.setupClusterHTTPRequest(op.hosts) +} + +func (op *NMAStageErrorReportOp) execute(execContext *OpEngineExecContext) error { + if err := op.runExecute(execContext); err != nil { + return err + } + + return op.processResult(execContext) +} + +func (op *NMAStageErrorReportOp) finalize(_ *OpEngineExecContext) error { + return nil +} + +func (op *NMAStageErrorReportOp) processResult(_ *OpEngineExecContext) error { + fileList := make([]stageErrorReportResponseData, 0) + return processStagedFilesResult(&op.ScrutinizeOpBase, fileList) +} diff --git a/vclusterops/nma_stage_vertica_logs_op.go b/vclusterops/nma_stage_vertica_logs_op.go index b1e58bc..e7014da 100644 --- a/vclusterops/nma_stage_vertica_logs_op.go +++ b/vclusterops/nma_stage_vertica_logs_op.go @@ -17,21 +17,15 @@ package vclusterops import ( "encoding/json" - "errors" "fmt" "github.com/vertica/vcluster/vclusterops/vlog" ) type NMAStageVerticaLogsOp struct { - OpBase - hostRequestBodyMap map[string]string - id string - hostNodeNameMap map[string]string // must correspond to host list exactly! - hostCatPathMap map[string]string // must correspond to host list exactly! - batch string - logSizeLimitBytes int64 - logAgeHours int // The maximum age of archieved logs in hours to retrieve + ScrutinizeOpBase + logSizeLimitBytes int64 + logAgeHours int // The maximum age of archieved logs in hours to retrieve } type stageVerticaLogsRequestData struct { @@ -46,32 +40,38 @@ type stageVerticaLogsResponseData struct { ModTime string `json:"mod_time"` } -func makeNMAStageVerticaLogsOp(log vlog.Printer, +func makeNMAStageVerticaLogsOp(logger vlog.Printer, id string, hosts []string, hostNodeNameMap map[string]string, hostCatPathMap map[string]string, logSizeLimitBytes int64, logAgeHours int) (NMAStageVerticaLogsOp, error) { - nmaStageVerticaLogsOp := NMAStageVerticaLogsOp{} - nmaStageVerticaLogsOp.name = "NMAStageVerticaLogsOp" - nmaStageVerticaLogsOp.log = log.WithName(nmaStageVerticaLogsOp.name) - nmaStageVerticaLogsOp.id = id - nmaStageVerticaLogsOp.hostNodeNameMap = hostNodeNameMap - nmaStageVerticaLogsOp.hostCatPathMap = hostCatPathMap - nmaStageVerticaLogsOp.batch = scrutinizeBatchNormal - nmaStageVerticaLogsOp.hosts = hosts - nmaStageVerticaLogsOp.logSizeLimitBytes = logSizeLimitBytes - nmaStageVerticaLogsOp.logAgeHours = logAgeHours + // base members + op := NMAStageVerticaLogsOp{} + op.name = "NMAStageVerticaLogsOp" + op.logger = logger.WithName(op.name) + op.hosts = hosts + + // scrutinize members + op.id = id + op.batch = scrutinizeBatchNormal + op.hostNodeNameMap = hostNodeNameMap + op.hostCatPathMap = hostCatPathMap + op.httpMethod = PostMethod + op.urlSuffix = "/vertica.log" + + // custom members + op.logSizeLimitBytes = logSizeLimitBytes + op.logAgeHours = logAgeHours // the caller is responsible for making sure hosts and maps match up exactly err := validateHostMaps(hosts, hostNodeNameMap, hostCatPathMap) - return nmaStageVerticaLogsOp, err + return op, err } func (op *NMAStageVerticaLogsOp) setupRequestBody(hosts []string) error { - op.hostRequestBodyMap = make(map[string]string) - + op.hostRequestBodyMap = make(map[string]string, len(hosts)) for _, host := range hosts { stageVerticaLogsData := stageVerticaLogsRequestData{} stageVerticaLogsData.CatalogPath = op.hostCatPathMap[host] @@ -89,24 +89,6 @@ func (op *NMAStageVerticaLogsOp) setupRequestBody(hosts []string) error { return nil } -func (op *NMAStageVerticaLogsOp) setupClusterHTTPRequest(hosts []string) error { - op.clusterHTTPRequest = ClusterHTTPRequest{} - op.clusterHTTPRequest.RequestCollection = make(map[string]HostHTTPRequest) - op.setVersionToSemVar() - - for _, host := range hosts { - nodeName := op.hostNodeNameMap[host] - - httpRequest := HostHTTPRequest{} - httpRequest.Method = PostMethod - httpRequest.buildNMAEndpoint(scrutinizeURLPrefix + op.id + "/" + nodeName + "/" + op.batch + "/vertica.log") - httpRequest.RequestData = op.hostRequestBodyMap[host] - op.clusterHTTPRequest.RequestCollection[host] = httpRequest - } - - return nil -} - func (op *NMAStageVerticaLogsOp) prepare(execContext *OpEngineExecContext) error { err := op.setupRequestBody(op.hosts) if err != nil { @@ -130,31 +112,6 @@ func (op *NMAStageVerticaLogsOp) finalize(_ *OpEngineExecContext) error { } func (op *NMAStageVerticaLogsOp) processResult(_ *OpEngineExecContext) error { - var allErrs error - - for host, result := range op.clusterHTTPRequest.ResultCollection { - op.logResponse(host, result) - if result.isPassing() { - if result.content == "" { - // note that an empty response (nothing staged) is not an error - op.log.Info("no logs staged on host", "Host", host) - continue - } - // the response is an array of file info structs - fileList := make([]stageVerticaLogsResponseData, 0) - err := op.parseAndCheckResponse(host, result.content, fileList) - if err != nil { - err = fmt.Errorf("[%s] fail to parse result on host %s, details: %w", op.name, host, err) - allErrs = errors.Join(allErrs, err) - continue - } - for _, fileEntry := range fileList { - op.log.Info("file staged on host", "Host", host, "FileInfo", fileEntry) - } - } else { - allErrs = errors.Join(allErrs, result.err) - } - } - - return allErrs + fileList := make([]stageVerticaLogsResponseData, 0) + return processStagedFilesResult(&op.ScrutinizeOpBase, fileList) } diff --git a/vclusterops/nma_start_node_op.go b/vclusterops/nma_start_node_op.go index 8221f55..1755e33 100644 --- a/vclusterops/nma_start_node_op.go +++ b/vclusterops/nma_start_node_op.go @@ -29,17 +29,17 @@ type nmaStartNodeOp struct { vdb *VCoordinationDatabase } -func makeNMAStartNodeOp(log vlog.Printer, +func makeNMAStartNodeOp(logger vlog.Printer, hosts []string) nmaStartNodeOp { startNodeOp := nmaStartNodeOp{} startNodeOp.name = "NMAStartNodeOp" - startNodeOp.log = log.WithName(startNodeOp.name) + startNodeOp.logger = logger.WithName(startNodeOp.name) startNodeOp.hosts = hosts return startNodeOp } -func makeNMAStartNodeOpWithVDB(log vlog.Printer, hosts []string, vdb *VCoordinationDatabase) nmaStartNodeOp { - startNodeOp := makeNMAStartNodeOp(log, hosts) +func makeNMAStartNodeOpWithVDB(logger vlog.Printer, hosts []string, vdb *VCoordinationDatabase) nmaStartNodeOp { + startNodeOp := makeNMAStartNodeOp(logger, hosts) startNodeOp.vdb = vdb return startNodeOp } diff --git a/vclusterops/nma_upload_config.go b/vclusterops/nma_upload_config.go index 971fe80..663a284 100644 --- a/vclusterops/nma_upload_config.go +++ b/vclusterops/nma_upload_config.go @@ -48,7 +48,7 @@ type uploadConfigRequestData struct { // To add nodes to the DB, use the bootstrapHost value for sourceConfigHost, a list of newly added nodes // for newNodeHosts and provide a nil value for hosts. func makeNMAUploadConfigOp( - log vlog.Printer, + logger vlog.Printer, opName string, sourceConfigHost []string, // source host for transferring configuration files, specifically, it is // 1. the bootstrap host when creating the database @@ -60,7 +60,7 @@ func makeNMAUploadConfigOp( ) NMAUploadConfigOp { nmaUploadConfigOp := NMAUploadConfigOp{} nmaUploadConfigOp.name = opName - nmaUploadConfigOp.log = log.WithName(nmaUploadConfigOp.name) + nmaUploadConfigOp.logger = logger.WithName(nmaUploadConfigOp.name) nmaUploadConfigOp.endpoint = endpoint nmaUploadConfigOp.fileContent = fileContent nmaUploadConfigOp.catalogPathMap = make(map[string]string) @@ -121,7 +121,7 @@ func (op *NMAUploadConfigOp) prepare(execContext *OpEngineExecContext) error { // If no hosts to upload, skip this operation. This can happen if all // hosts have the latest catalog. if len(op.hosts) == 0 { - op.log.Info("no hosts require an upload, skipping the operation") + op.logger.Info("no hosts require an upload, skipping the operation") op.skipExecute = true return nil } diff --git a/vclusterops/nma_vertica_version_op.go b/vclusterops/nma_vertica_version_op.go index 4531779..f1a1cc8 100644 --- a/vclusterops/nma_vertica_version_op.go +++ b/vclusterops/nma_vertica_version_op.go @@ -48,10 +48,10 @@ func makeSCToHostVersionMap() map[string]HostVersionMap { } // makeNMAVerticaVersionOp is used when db has not been created -func makeNMAVerticaVersionOp(log vlog.Printer, hosts []string, sameVersion, isEon bool) NMAVerticaVersionOp { +func makeNMAVerticaVersionOp(logger vlog.Printer, hosts []string, sameVersion, isEon bool) NMAVerticaVersionOp { nmaVerticaVersionOp := NMAVerticaVersionOp{} nmaVerticaVersionOp.name = "NMAVerticaVersionOp" - nmaVerticaVersionOp.log = log.WithName(nmaVerticaVersionOp.name) + nmaVerticaVersionOp.logger = logger.WithName(nmaVerticaVersionOp.name) nmaVerticaVersionOp.hosts = hosts nmaVerticaVersionOp.RequireSameVersion = sameVersion nmaVerticaVersionOp.IsEon = isEon @@ -60,15 +60,15 @@ func makeNMAVerticaVersionOp(log vlog.Printer, hosts []string, sameVersion, isEo } // makeNMAVerticaVersionOpWithoutHosts is used when db is down -func makeNMAVerticaVersionOpWithoutHosts(log vlog.Printer, sameVersion bool) NMAVerticaVersionOp { +func makeNMAVerticaVersionOpWithoutHosts(logger vlog.Printer, sameVersion bool) NMAVerticaVersionOp { // We set hosts to nil and isEon to false temporarily, and they will get the correct value from execute context in prepare() - return makeNMAVerticaVersionOp(log, nil /*hosts*/, sameVersion, false /*isEon*/) + return makeNMAVerticaVersionOp(logger, nil /*hosts*/, sameVersion, false /*isEon*/) } // makeNMAVerticaVersionOpWithVDB is used when db is up -func makeNMAVerticaVersionOpWithVDB(log vlog.Printer, sameVersion bool, vdb *VCoordinationDatabase) NMAVerticaVersionOp { +func makeNMAVerticaVersionOpWithVDB(logger vlog.Printer, sameVersion bool, vdb *VCoordinationDatabase) NMAVerticaVersionOp { // We set hosts to nil temporarily, and it will get the correct value from vdb in prepare() - op := makeNMAVerticaVersionOp(log, nil /*hosts*/, sameVersion, vdb.IsEon) + op := makeNMAVerticaVersionOp(logger, nil /*hosts*/, sameVersion, vdb.IsEon) op.vdb = vdb return op } @@ -169,7 +169,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, op.log) + err := util.GetJSONLogErrors(resultContent, &responseObj, op.name, op.logger) if err != nil { return err } @@ -180,7 +180,7 @@ func (op *NMAVerticaVersionOp) parseAndCheckResponse(host, resultContent string) return errors.New("Unable to get vertica version from host " + host) } - op.log.Info("JSON response", "host", host, "responseObj", responseObj) + op.logger.Info("JSON response", "host", host, "responseObj", responseObj) // update version for the host in SCToHostVersionMap for sc, hostVersionMap := range op.SCToHostVersionMap { if _, exists := hostVersionMap[host]; exists { @@ -192,6 +192,8 @@ func (op *NMAVerticaVersionOp) parseAndCheckResponse(host, resultContent string) func (op *NMAVerticaVersionOp) logResponseCollectVersions() error { for host, result := range op.clusterHTTPRequest.ResultCollection { + op.logResponse(host, result) + if !result.isPassing() { errStr := fmt.Sprintf("[%s] result from host %s summary %s, details: %+v\n", op.name, host, FailureResult, result) @@ -200,12 +202,8 @@ func (op *NMAVerticaVersionOp) logResponseCollectVersions() error { err := op.parseAndCheckResponse(host, result.content) if err != nil { - op.log.Info("failure response", "host", host, "result", result, "err", err) - return err + return fmt.Errorf("[%s] fail to parse result on host %s, details: %w", op.name, host, err) } - - op.log.PrintInfo("[%s] result from host %s summary %s, details: %+v", - op.name, host, SuccessResult, result) } return nil } @@ -222,7 +220,7 @@ func (op *NMAVerticaVersionOp) logCheckVersionMatch() error { for sc, hostVersionMap := range op.SCToHostVersionMap { versionStr = NoVersion for host, version := range hostVersionMap { - op.log.Info("version check", "host", host, "version", version) + op.logger.Info("version check", "host", host, "version", version) if version == "" { if op.IsEon && op.HasIncomingSCNames { return fmt.Errorf("[%s] No version collected for host [%s] in subcluster [%s]", op.name, host, sc) diff --git a/vclusterops/re_ip.go b/vclusterops/re_ip.go index cfafc4f..79aade6 100644 --- a/vclusterops/re_ip.go +++ b/vclusterops/re_ip.go @@ -43,7 +43,7 @@ func VReIPFactory() VReIPOptions { return opt } -func (opt *VReIPOptions) validateParseOptions(log vlog.Printer) error { +func (opt *VReIPOptions) validateParseOptions(logger vlog.Printer) error { err := util.ValidateRequiredAbsPath(opt.CatalogPrefix, "catalog path") if err != nil { return err @@ -53,7 +53,7 @@ func (opt *VReIPOptions) validateParseOptions(log vlog.Printer) error { return util.ValidateCommunalStorageLocation(*opt.CommunalStorageLocation) } - return opt.validateBaseOptions("re_ip", log) + return opt.validateBaseOptions("re_ip", logger) } func (opt *VReIPOptions) analyzeOptions() error { @@ -66,8 +66,8 @@ func (opt *VReIPOptions) analyzeOptions() error { return nil } -func (opt *VReIPOptions) validateAnalyzeOptions(log vlog.Printer) error { - if err := opt.validateParseOptions(log); err != nil { +func (opt *VReIPOptions) validateAnalyzeOptions(logger vlog.Printer) error { + if err := opt.validateParseOptions(logger); err != nil { return err } if err := opt.analyzeOptions(); err != nil { diff --git a/vclusterops/remove_subcluster.go b/vclusterops/remove_subcluster.go index 32677ce..42f4276 100644 --- a/vclusterops/remove_subcluster.go +++ b/vclusterops/remove_subcluster.go @@ -46,8 +46,8 @@ func (o *VRemoveScOptions) setDefaultValues() { o.ForceDelete = new(bool) } -func (o *VRemoveScOptions) validateRequiredOptions(log vlog.Printer) error { - err := o.validateBaseOptions("db_remove_subcluster", log) +func (o *VRemoveScOptions) validateRequiredOptions(logger vlog.Printer) error { + err := o.validateBaseOptions("db_remove_subcluster", logger) if err != nil { return err } @@ -71,8 +71,8 @@ func (o *VRemoveScOptions) validatePathOptions() error { return util.ValidateRequiredAbsPath(o.DepotPrefix, "depot path") } -func (o *VRemoveScOptions) validateParseOptions(log vlog.Printer) error { - err := o.validateRequiredOptions(log) +func (o *VRemoveScOptions) validateParseOptions(logger vlog.Printer) error { + err := o.validateRequiredOptions(logger) if err != nil { return err } @@ -93,15 +93,15 @@ func (o *VRemoveScOptions) analyzeOptions() (err error) { return nil } -func (o *VRemoveScOptions) validateAnalyzeOptions(log vlog.Printer) error { - if err := o.validateParseOptions(log); err != nil { +func (o *VRemoveScOptions) validateAnalyzeOptions(logger vlog.Printer) error { + if err := o.validateParseOptions(logger); err != nil { return err } err := o.analyzeOptions() if err != nil { return err } - return o.setUsePassword(log) + return o.setUsePassword(logger) } /* diff --git a/vclusterops/restart_node.go b/vclusterops/restart_node.go index d39ad9b..282b467 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(log vlog.Printer) error { - err := options.validateBaseOptions("restart_node", log) +func (options *VRestartNodesOptions) validateRequiredOptions(logger vlog.Printer) error { + err := options.validateBaseOptions("restart_node", logger) if err != nil { return err } @@ -67,8 +67,8 @@ func (options *VRestartNodesOptions) validateRequiredOptions(log vlog.Printer) e return nil } -func (options *VRestartNodesOptions) validateParseOptions(log vlog.Printer) error { - return options.validateRequiredOptions(log) +func (options *VRestartNodesOptions) validateParseOptions(logger vlog.Printer) error { + return options.validateRequiredOptions(logger) } // 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(log vlog.Printer) error { - if err := options.validateParseOptions(log); err != nil { +func (options *VRestartNodesOptions) validateAnalyzeOptions(logger vlog.Printer) error { + if err := options.validateParseOptions(logger); err != nil { return err } return options.analyzeOptions() diff --git a/vclusterops/scrutinize.go b/vclusterops/scrutinize.go index d0ea29d..7ff5374 100644 --- a/vclusterops/scrutinize.go +++ b/vclusterops/scrutinize.go @@ -18,6 +18,8 @@ package vclusterops import ( "errors" "fmt" + "os" + "os/exec" "time" "github.com/vertica/vcluster/vclusterops/util" @@ -27,9 +29,6 @@ import ( // const to sync cmd, options parsing, and this const VScrutinizeTypeName = "scrutinize" -// top level handler for scrutinize operations -const scrutinizeURLPrefix = "scrutinize/" - // folders used by scrutinize const scrutinizeOutputBasePath = "/tmp/scrutinize" const scrutinizeRemoteOutputPath = scrutinizeOutputBasePath + "/remote" @@ -40,6 +39,7 @@ const scrutinizeLogLimitBytes = 10737418240 // 10GB in bytes // batches are fixed, top level folders for each node's data const scrutinizeBatchNormal = "normal" +const scrutinizeBatchContext = "context" type VScrutinizeOptions struct { DatabaseOptions @@ -65,23 +65,20 @@ func generateScrutinizeID() string { return idPrefix + idSuffix } -func (options *VScrutinizeOptions) validateRequiredOptions(log vlog.Printer) error { +func (options *VScrutinizeOptions) validateRequiredOptions(logger vlog.Printer) error { // checks for correctness, but not for presence of all flags - err := options.validateBaseOptions(VScrutinizeTypeName, log) + err := options.validateBaseOptions(VScrutinizeTypeName, logger) if err != nil { return err } // will auto-generate username if not provided - // should be replaced by a later call to SetUsePassword() when we use - // an embedded server endpoint for system table retrieval - err = options.validateUserName(log) + // can be removed after adding embedded server endpoint for system table retrieval + err = options.validateUserName(logger) if err != nil { return err } - // Password is not required - if *options.HonorUserInput { // RawHosts is already required by the cmd parser, so no need to check here err = options.validateCatalogPath() @@ -94,12 +91,12 @@ func (options *VScrutinizeOptions) validateRequiredOptions(log vlog.Printer) err return nil } -func (options *VScrutinizeOptions) validateParseOptions(log vlog.Printer) error { - return options.validateRequiredOptions(log) +func (options *VScrutinizeOptions) validateParseOptions(logger vlog.Printer) error { + return options.validateRequiredOptions(logger) } // analyzeOptions will modify some options based on what is chosen -func (options *VScrutinizeOptions) analyzeOptions(log vlog.Printer) (err error) { +func (options *VScrutinizeOptions) analyzeOptions(logger vlog.Printer) (err error) { // we analyze host names when HonorUserInput is set, otherwise we use hosts in yaml config if *options.HonorUserInput { // resolve RawHosts to be IP addresses @@ -107,17 +104,18 @@ func (options *VScrutinizeOptions) analyzeOptions(log vlog.Printer) (err error) if err != nil { return err } - log.V(1).Info("Resolved host list to IPs", "Hosts", options.Hosts) + logger.V(1).Info("Resolved host list to IPs", "Hosts", options.Hosts) } - return nil + err = options.setUsePassword(logger) + return err } -func (options *VScrutinizeOptions) ValidateAnalyzeOptions(log vlog.Printer) error { - if err := options.validateParseOptions(log); err != nil { +func (options *VScrutinizeOptions) ValidateAnalyzeOptions(logger vlog.Printer) error { + if err := options.validateParseOptions(logger); err != nil { return err } - return options.analyzeOptions(log) + return options.analyzeOptions(logger) } func (vcc *VClusterCommands) VScrutinize(options *VScrutinizeOptions) error { @@ -169,26 +167,47 @@ func (vcc *VClusterCommands) VScrutinize(options *VScrutinizeOptions) error { return err } - // TODO tar all results (VER-89741) + // tar all results + if err = tarAndRemoveDirectory(options.ID, vcc.Log); err != nil { + vcc.Log.Error(err, "failed to create final scrutinize output tarball") + return err + } + + return nil +} + +func tarAndRemoveDirectory(id string, log vlog.Printer) (err error) { + tarballPath := "/tmp/scrutinize/" + id + ".tgz" + cmd := exec.Command("tar", "czf", tarballPath, "-C", "/tmp/scrutinize/remote", id) + log.Info("running command %s with args %v", cmd.Path, cmd.Args) + if err = cmd.Run(); err != nil { + return + } + log.PrintInfo("Scrutinize final result at %s", tarballPath) + + intermediateDirectoryPath := "/tmp/scrutinize/remote/" + id + if err = os.RemoveAll(intermediateDirectoryPath); err != nil { + log.PrintError("Failed to remove intermediate output directory %s: %s", intermediateDirectoryPath, err.Error()) + } return nil } // getVDBForScrutinize populates an empty coordinator database with the minimum // required information for further scrutinize operations. -func (options *VScrutinizeOptions) getVDBForScrutinize(log vlog.Printer, +func (options *VScrutinizeOptions) getVDBForScrutinize(logger vlog.Printer, vdb *VCoordinationDatabase) error { // get nodes where NMA is running and only use those for NMA ops - nmaGetHealthyNodesOp := makeNMAGetHealthyNodesOp(log, options.Hosts, vdb) - err := options.runClusterOpEngine(log, []ClusterOp{&nmaGetHealthyNodesOp}) + nmaGetHealthyNodesOp := makeNMAGetHealthyNodesOp(logger, options.Hosts, vdb) + err := options.runClusterOpEngine(logger, []ClusterOp{&nmaGetHealthyNodesOp}) if err != nil { return err } // get map of host to node name and fully qualified catalog path - nmaGetNodesInfoOp := makeNMAGetNodesInfoOp(log, vdb.HostList, *options.DBName, + nmaGetNodesInfoOp := makeNMAGetNodesInfoOp(logger, vdb.HostList, *options.DBName, *options.CatalogPrefix, true /* ignore internal errors */, vdb) - err = options.runClusterOpEngine(log, []ClusterOp{&nmaGetNodesInfoOp}) + err = options.runClusterOpEngine(logger, []ClusterOp{&nmaGetNodesInfoOp}) if err != nil { return err } @@ -212,14 +231,13 @@ func (options *VScrutinizeOptions) getVDBForScrutinize(log vlog.Printer, // // The generated instructions will later perform the following operations necessary // for a successful scrutinize: -// - TODO Get up nodes through https call +// - Get up nodes through https call // - TODO Initiate system table staging on the first up node, if available // - Stage vertica logs on all nodes -// - TODO Stage ErrorReport.txt on all nodes +// - Stage ErrorReport.txt on all nodes // - TODO Stage DC tables on all nodes -// - Tar and retrieve vertica logs from all nodes (batch normal) -// - TODO Tar and retrieve error report from all nodes (batch context) -// - TODO Tar and retrieve DC tables from all nodes (batch context) +// - Tar and retrieve vertica logs (TODO + DC tables) from all nodes (batch normal) +// - Tar and retrieve error report from all nodes (batch context) // - TODO (If applicable) Poll for system table staging completion on task node // - TODO (If applicable) Tar and retrieve system tables from task node (batch system_tables) func (vcc *VClusterCommands) produceScrutinizeInstructions(options *VScrutinizeOptions, @@ -230,6 +248,15 @@ func (vcc *VClusterCommands) produceScrutinizeInstructions(options *VScrutinizeO return nil, fmt.Errorf("failed to process retrieved node info, details %w", err) } + // Get up database nodes for the system table task later + httpsGetUpNodesOp, err := makeHTTPSGetUpNodesOp(vcc.Log, *options.DBName, options.Hosts, + options.usePassword, *options.UserName, options.Password) + if err != nil { + return nil, err + } + httpsGetUpNodesOp.allowNoUpHosts() + instructions = append(instructions, &httpsGetUpNodesOp) + // stage Vertica logs nmaStageVerticaLogsOp, err := makeNMAStageVerticaLogsOp(vcc.Log, options.ID, options.Hosts, hostNodeNameMap, hostCatPathMap, scrutinizeLogLimitBytes, scrutinizeLogAgeHours) @@ -239,6 +266,14 @@ func (vcc *VClusterCommands) produceScrutinizeInstructions(options *VScrutinizeO } instructions = append(instructions, &nmaStageVerticaLogsOp) + // stage ErrorReport.txt + nmaStageVerticaErrorReportOp, err := makeNMAStageErrorReportOp(vcc.Log, options.ID, options.Hosts, + hostNodeNameMap, hostCatPathMap) + if err != nil { + return nil, err + } + instructions = append(instructions, &nmaStageVerticaErrorReportOp) + // get 'normal' batch tarball (inc. Vertica logs) getNormalTarballOp, err := makeNMAGetScrutinizeTarOp(vcc.Log, options.ID, scrutinizeBatchNormal, options.Hosts, hostNodeNameMap) @@ -247,6 +282,14 @@ func (vcc *VClusterCommands) produceScrutinizeInstructions(options *VScrutinizeO } instructions = append(instructions, &getNormalTarballOp) + // get 'context' batch tarball (inc. ErrorReport.txt) + getContextTarballOp, err := makeNMAGetScrutinizeTarOp(vcc.Log, options.ID, scrutinizeBatchContext, + options.Hosts, hostNodeNameMap) + if err != nil { + return nil, err + } + instructions = append(instructions, &getContextTarballOp) + return instructions, nil } diff --git a/vclusterops/scrutinize_op.go b/vclusterops/scrutinize_op.go new file mode 100644 index 0000000..5642f3b --- /dev/null +++ b/vclusterops/scrutinize_op.go @@ -0,0 +1,84 @@ +/* + (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 vclusterops + +import ( + "errors" + "fmt" +) + +// top level handler for scrutinize operations +const scrutinizeURLPrefix = "scrutinize/" + +// ScrutinizeOpBase, in addition to embedding the standard OpBase, wraps some +// common data and functionality for scrutinize-specific ops +type ScrutinizeOpBase struct { + OpBase + id string + batch string + urlSuffix string + httpMethod string + hostNodeNameMap map[string]string // must correspond to host list exactly! + hostCatPathMap map[string]string // must correspond to host list exactly, if non-nil + hostRequestBodyMap map[string]string // should be nil if not used +} + +func (op *ScrutinizeOpBase) setupClusterHTTPRequest(hosts []string) error { + for _, host := range hosts { + nodeName := op.hostNodeNameMap[host] + + httpRequest := HostHTTPRequest{} + httpRequest.Method = op.httpMethod + httpRequest.buildNMAEndpoint(scrutinizeURLPrefix + op.id + "/" + nodeName + "/" + op.batch + op.urlSuffix) + if op.hostRequestBodyMap != nil { + httpRequest.RequestData = op.hostRequestBodyMap[host] + } + op.clusterHTTPRequest.RequestCollection[host] = httpRequest + } + + return nil +} + +// processeStagedFilesResult is a parameterized function which contains common logic +// for processing the results of staging various types of files, e.g. vertica.log +func processStagedFilesResult[T any](op *ScrutinizeOpBase, fileList []T) error { + var allErrs error + + for host, result := range op.clusterHTTPRequest.ResultCollection { + op.logResponse(host, result) + if result.isPassing() { + if result.content == "" { + // note that an empty response (nothing staged) is not an error + op.logger.Info("nothing staged on host", "Host", host) + continue + } + // the response is an array of file info structs + err := op.parseAndCheckResponse(host, result.content, &fileList) + if err != nil { + err = fmt.Errorf("[%s] fail to parse result on host %s, details: %w", op.name, host, err) + allErrs = errors.Join(allErrs, err) + continue + } + for _, fileEntry := range fileList { + op.logger.Info("file staged on host", "Host", host, "FileInfo", fileEntry) + } + } else { + allErrs = errors.Join(allErrs, result.err) + } + } + + return allErrs +} diff --git a/vclusterops/start_db.go b/vclusterops/start_db.go index 1f71509..0feb174 100644 --- a/vclusterops/start_db.go +++ b/vclusterops/start_db.go @@ -39,6 +39,9 @@ func VStartDatabaseOptionsFactory() VStartDatabaseOptions { // set default values to the params opt.setDefaultValues() + opt.StatePollingTimeout = new(int) + opt.TrimHostList = new(bool) + return opt } @@ -46,8 +49,8 @@ func (options *VStartDatabaseOptions) setDefaultValues() { options.DatabaseOptions.setDefaultValues() } -func (options *VStartDatabaseOptions) validateRequiredOptions(log vlog.Printer) error { - err := options.validateBaseOptions("start_db", log) +func (options *VStartDatabaseOptions) validateRequiredOptions(logger vlog.Printer) error { + err := options.validateBaseOptions("start_db", logger) if err != nil { return err } @@ -70,9 +73,9 @@ func (options *VStartDatabaseOptions) validateEonOptions() error { return nil } -func (options *VStartDatabaseOptions) validateParseOptions(log vlog.Printer) error { +func (options *VStartDatabaseOptions) validateParseOptions(logger vlog.Printer) error { // batch 1: validate required parameters - err := options.validateRequiredOptions(log) + err := options.validateRequiredOptions(logger) if err != nil { return err } @@ -92,8 +95,8 @@ func (options *VStartDatabaseOptions) analyzeOptions() (err error) { return nil } -func (options *VStartDatabaseOptions) validateAnalyzeOptions(log vlog.Printer) error { - if err := options.validateParseOptions(log); err != nil { +func (options *VStartDatabaseOptions) validateAnalyzeOptions(logger vlog.Printer) error { + if err := options.validateParseOptions(logger); err != nil { return err } return options.analyzeOptions() diff --git a/vclusterops/vlog/printer.go b/vclusterops/vlog/printer.go index 6f8cd06..f69e16c 100644 --- a/vclusterops/vlog/printer.go +++ b/vclusterops/vlog/printer.go @@ -216,3 +216,10 @@ func (p *Printer) SetupOrDie(logFile string) { p.Log = zapr.NewLogger(zapLg) p.Log.Info("Successfully started logger", "logFile", logFile) } + +// PrintWithIndent prints message to console only with an indentation +func PrintWithIndent(msg string, v ...any) { + // the indent level may be adjusted + const indentLevel = 2 + fmt.Printf("%*s%s\n", indentLevel, "", fmt.Sprintf(msg, v...)) +}