diff --git a/commands/cluster_command_launcher.go b/commands/cluster_command_launcher.go index a124fef..4bc8573 100644 --- a/commands/cluster_command_launcher.go +++ b/commands/cluster_command_launcher.go @@ -85,6 +85,7 @@ const ( saveRpFlag = "save-restore-point" isolateMetadataFlag = "isolate-metadata" createStorageLocationsFlag = "create-storage-locations" + forUpgradeFlag = "for-upgrade" sandboxKey = "sandbox" connFlag = "conn" connKey = "conn" diff --git a/commands/cmd_sandbox.go b/commands/cmd_sandbox.go index 3465819..1ab5cf0 100644 --- a/commands/cmd_sandbox.go +++ b/commands/cmd_sandbox.go @@ -52,18 +52,18 @@ func makeCmdSandboxSubcluster() *cobra.Command { `Sandboxes a secondary subcluster in an Eon Mode database. All hosts in the subcluster must be up. - + When you sandbox a subcluster, its hosts immediately shut down and restart; the subcluster becomes sandboxed after the hosts start back up. -A sandbox can contain multiple subclusters, and subclusters in the sandbox can -interact with each other. If you want to isolate subclusters, they must +A sandbox can contain multiple subclusters, and subclusters in the sandbox can +interact with each other. If you want to isolate subclusters, they must be in separate sandboxes. -Subcluster sandboxing should be used for testing changes or upgrades in safe, isolated -environment and should not be used for production subclusters. For example, you can +Subcluster sandboxing should be used for testing changes or upgrades in safe, isolated +environment and should not be used for production subclusters. For example, you can create sandboxes and then upgrade Vertica in those sandboxes. - + Examples: # Sandbox a subcluster with config file vcluster sandbox_subcluster --subcluster sc1 --sandbox sand \ @@ -117,6 +117,12 @@ func (c *CmdSandboxSubcluster) setLocalFlags(cmd *cobra.Command) { false, "The sandbox can create its own storage locations.", ) + cmd.Flags().BoolVar( + &c.sbOptions.ForUpgrade, + forUpgradeFlag, + false, + "The sandbox is to be used for online upgrade", + ) } func (c *CmdSandboxSubcluster) Parse(inputArgv []string, logger vlog.Printer) error { diff --git a/rfc7807/errors.go b/rfc7807/errors.go index 2a0a765..f5af687 100644 --- a/rfc7807/errors.go +++ b/rfc7807/errors.go @@ -227,4 +227,9 @@ var ( "Failed to write file", http.StatusInternalServerError, ) + MessageQueueFull = newProblemID( + path.Join(errorEndpointsPrefix, "message-queue-full"), + "Message queue is full", + http.StatusInternalServerError, + ) ) diff --git a/vclusterops/add_node.go b/vclusterops/add_node.go index 1a4c08d..ab5b754 100644 --- a/vclusterops/add_node.go +++ b/vclusterops/add_node.go @@ -200,8 +200,7 @@ func (vcc VClusterCommands) VAddNode(options *VAddNodeOptions) (VCoordinationDat return vdb, fmt.Errorf("fail to produce add node instructions, %w", err) } - certs := httpsCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} - clusterOpEngine := makeClusterOpEngine(instructions, &certs) + clusterOpEngine := makeClusterOpEngine(instructions, options) if runError := clusterOpEngine.run(vcc.Log); runError != nil { return vdb, fmt.Errorf("fail to complete add node operation, %w", runError) } @@ -316,8 +315,7 @@ func (vcc VClusterCommands) trimNodesInCatalog(vdb *VCoordinationDatabase, instructions = append(instructions, &httpsDropNodeOp) } - certs := httpsCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} - clusterOpEngine := makeClusterOpEngine(instructions, &certs) + clusterOpEngine := makeClusterOpEngine(instructions, options) err := clusterOpEngine.run(vcc.Log) if err != nil { vcc.Log.Error(err, "fail to trim nodes from catalog, %v") diff --git a/vclusterops/add_subcluster.go b/vclusterops/add_subcluster.go index f6be071..05ab9f7 100644 --- a/vclusterops/add_subcluster.go +++ b/vclusterops/add_subcluster.go @@ -153,8 +153,6 @@ func (options *VAddSubclusterOptions) validateAnalyzeOptions(logger vlog.Printer // VAddSubcluster adds to a running database a new subcluster with provided options. // It returns any error encountered. -// -//nolint:dupl func (vcc VClusterCommands) VAddSubcluster(options *VAddSubclusterOptions) error { /* * - Validate Options @@ -175,8 +173,7 @@ func (vcc VClusterCommands) VAddSubcluster(options *VAddSubclusterOptions) error } // Create a VClusterOpEngine, and add certs to the engine - certs := httpsCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} - clusterOpEngine := makeClusterOpEngine(instructions, &certs) + clusterOpEngine := makeClusterOpEngine(instructions, options) // Give the instructions to the VClusterOpEngine to run runError := clusterOpEngine.run(vcc.Log) diff --git a/vclusterops/alter_subcluster_type.go b/vclusterops/alter_subcluster_type.go index fa80de6..713fd3e 100644 --- a/vclusterops/alter_subcluster_type.go +++ b/vclusterops/alter_subcluster_type.go @@ -139,8 +139,7 @@ func (vcc VClusterCommands) VAlterSubclusterType(options *VAlterSubclusterTypeOp } // create a VClusterOpEngine, and add certs to the engine - certs := httpsCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} - clusterOpEngine := makeClusterOpEngine(instructions, &certs) + clusterOpEngine := makeClusterOpEngine(instructions, options) // give the instructions to the VClusterOpEngine to run runError := clusterOpEngine.run(vcc.Log) diff --git a/vclusterops/cluster_op.go b/vclusterops/cluster_op.go index 220f232..1f2c854 100644 --- a/vclusterops/cluster_op.go +++ b/vclusterops/cluster_op.go @@ -197,7 +197,7 @@ type clusterOp interface { logExecute() logFinalize() setupBasicInfo() - loadCertsIfNeeded(certs *httpsCerts, findCertsInOptions bool) error + loadCertsIfNeeded(tlsOptions opTLSOptions) error isSkipExecute() bool filterUnreachableHosts(execContext *opEngineExecContext) } @@ -382,9 +382,14 @@ func (op *opBase) runExecute(execContext *opEngineExecContext) error { return nil } +type opTLSOptions interface { + hasCerts() bool + getCerts() *httpsCerts +} + // if found certs in the options, we add the certs to http requests of each instruction -func (op *opBase) loadCertsIfNeeded(certs *httpsCerts, findCertsInOptions bool) error { - if !findCertsInOptions { +func (op *opBase) loadCertsIfNeeded(tlsOptions opTLSOptions) error { + if tlsOptions == nil || !tlsOptions.hasCerts() { return nil } @@ -393,16 +398,15 @@ func (op *opBase) loadCertsIfNeeded(certs *httpsCerts, findCertsInOptions bool) return fmt.Errorf("[%s] clusterHTTPRequest.RequestCollection is empty", op.name) } + // retrieve certs once to avoid extra copies + certs := tlsOptions.getCerts() if certs == nil { return fmt.Errorf("[%s] is trying to use certificates, but none are set", op.name) } for host := range op.clusterHTTPRequest.RequestCollection { request := op.clusterHTTPRequest.RequestCollection[host] - request.UseCertsInOptions = true - request.Certs.key = certs.key - request.Certs.cert = certs.cert - request.Certs.caCert = certs.caCert + request.setCerts(certs) op.clusterHTTPRequest.RequestCollection[host] = request } return nil diff --git a/vclusterops/cluster_op_engine.go b/vclusterops/cluster_op_engine.go index 1c37283..3966e38 100644 --- a/vclusterops/cluster_op_engine.go +++ b/vclusterops/cluster_op_engine.go @@ -23,21 +23,17 @@ import ( type VClusterOpEngine struct { instructions []clusterOp - certs *httpsCerts + tlsOptions opTLSOptions execContext *opEngineExecContext } -func makeClusterOpEngine(instructions []clusterOp, certs *httpsCerts) VClusterOpEngine { +func makeClusterOpEngine(instructions []clusterOp, tlsOptions opTLSOptions) VClusterOpEngine { newClusterOpEngine := VClusterOpEngine{} newClusterOpEngine.instructions = instructions - newClusterOpEngine.certs = certs + newClusterOpEngine.tlsOptions = tlsOptions return newClusterOpEngine } -func (opEngine *VClusterOpEngine) shouldGetCertsFromOptions() bool { - return (opEngine.certs.key != "" && opEngine.certs.cert != "") -} - func (opEngine *VClusterOpEngine) run(logger vlog.Printer) error { execContext := makeOpEngineExecContext(logger) opEngine.execContext = &execContext @@ -46,10 +42,8 @@ func (opEngine *VClusterOpEngine) run(logger vlog.Printer) error { } func (opEngine *VClusterOpEngine) runWithExecContext(logger vlog.Printer, execContext *opEngineExecContext) error { - findCertsInOptions := opEngine.shouldGetCertsFromOptions() - for _, op := range opEngine.instructions { - err := opEngine.runInstruction(logger, execContext, op, findCertsInOptions) + err := opEngine.runInstruction(logger, execContext, op) if err != nil { return err } @@ -66,7 +60,7 @@ func (opEngine *VClusterOpEngine) runWithExecContext(logger vlog.Printer, execCo func (opEngine *VClusterOpEngine) runInstruction( logger vlog.Printer, execContext *opEngineExecContext, - op clusterOp, findCertsInOptions bool) error { + op clusterOp) error { op.setLogger(logger) op.setupBasicInfo() op.setupSpinner() @@ -84,7 +78,7 @@ func (opEngine *VClusterOpEngine) runInstruction( // start the progress spinner op.startSpinner() - err = op.loadCertsIfNeeded(opEngine.certs, findCertsInOptions) + err = op.loadCertsIfNeeded(opEngine.tlsOptions) if err != nil { // here we do not return an error as the spinner error does not // affect the functionality diff --git a/vclusterops/cluster_op_engine_test.go b/vclusterops/cluster_op_engine_test.go index 67b23ae..8c7a240 100644 --- a/vclusterops/cluster_op_engine_test.go +++ b/vclusterops/cluster_op_engine_test.go @@ -73,8 +73,7 @@ func TestSkipExecuteOp(t *testing.T) { opWithSkipEnabled := makeMockOp(true) opWithSkipDisabled := makeMockOp(false) instructions := []clusterOp{&opWithSkipDisabled, &opWithSkipEnabled} - certs := httpsCerts{key: "key", cert: "cert", caCert: "ca-cert"} - opEngn := makeClusterOpEngine(instructions, &certs) + opEngn := makeClusterOpEngine(instructions, nil) err := opEngn.run(vlog.Printer{}) assert.Equal(t, nil, err) assert.True(t, opWithSkipDisabled.calledPrepare) diff --git a/vclusterops/create_db.go b/vclusterops/create_db.go index e79f2fa..03d4992 100644 --- a/vclusterops/create_db.go +++ b/vclusterops/create_db.go @@ -305,8 +305,7 @@ func (vcc VClusterCommands) VCreateDatabase(options *VCreateDatabaseOptions) (VC } // create a VClusterOpEngine, and add certs to the engine - certs := httpsCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} - clusterOpEngine := makeClusterOpEngine(instructions, &certs) + clusterOpEngine := makeClusterOpEngine(instructions, options) // Give the instructions to the VClusterOpEngine to run err = clusterOpEngine.run(vcc.Log) diff --git a/vclusterops/drop_db.go b/vclusterops/drop_db.go index b1ff04d..dda610e 100644 --- a/vclusterops/drop_db.go +++ b/vclusterops/drop_db.go @@ -98,8 +98,7 @@ func (vcc VClusterCommands) VDropDatabase(options *VDropDatabaseOptions) error { } // create a VClusterOpEngine, and add certs to the engine - certs := httpsCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} - clusterOpEngine := makeClusterOpEngine(instructions, &certs) + clusterOpEngine := makeClusterOpEngine(instructions, options) // give the instructions to the VClusterOpEngine to run runError := clusterOpEngine.run(vcc.Log) diff --git a/vclusterops/fetch_database.go b/vclusterops/fetch_database.go index c2a544a..3a26523 100644 --- a/vclusterops/fetch_database.go +++ b/vclusterops/fetch_database.go @@ -101,8 +101,7 @@ func (vcc VClusterCommands) VFetchCoordinationDatabase(options *VFetchCoordinati } // create a VClusterOpEngine, and add certs to the engine - certs := httpsCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} - clusterOpEngine := makeClusterOpEngine(instructions, &certs) + clusterOpEngine := makeClusterOpEngine(instructions, options) // Give the instructions to the VClusterOpEngine to run runError := clusterOpEngine.run(vcc.Log) diff --git a/vclusterops/fetch_node_state.go b/vclusterops/fetch_node_state.go index d7617a7..41704a0 100644 --- a/vclusterops/fetch_node_state.go +++ b/vclusterops/fetch_node_state.go @@ -92,8 +92,7 @@ func (vcc VClusterCommands) VFetchNodeState(options *VFetchNodeStateOptions) ([] } // create a VClusterOpEngine, and add certs to the engine - certs := httpsCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} - clusterOpEngine := makeClusterOpEngine(instructions, &certs) + clusterOpEngine := makeClusterOpEngine(instructions, options) // give the instructions to the VClusterOpEngine to run runError := clusterOpEngine.run(vcc.Log) diff --git a/vclusterops/fetch_nodes_details.go b/vclusterops/fetch_nodes_details.go index a055c46..abb53dd 100644 --- a/vclusterops/fetch_nodes_details.go +++ b/vclusterops/fetch_nodes_details.go @@ -133,8 +133,7 @@ func (vcc VClusterCommands) VFetchNodesDetails(options *VFetchNodesDetailsOption return nodesDetails, fmt.Errorf("fail to produce instructions: %w", err) } - certs := httpsCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} - clusterOpEngine := makeClusterOpEngine(instructions, &certs) + clusterOpEngine := makeClusterOpEngine(instructions, options) err = clusterOpEngine.run(vcc.Log) if err != nil { diff --git a/vclusterops/get_config_parameter.go b/vclusterops/get_config_parameter.go index 8620282..de9d086 100644 --- a/vclusterops/get_config_parameter.go +++ b/vclusterops/get_config_parameter.go @@ -110,8 +110,7 @@ func (vcc VClusterCommands) VGetConfigurationParameters(options *VGetConfigurati } // Create a VClusterOpEngine, and add certs to the engine - certs := httpsCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} - clusterOpEngine := makeClusterOpEngine(instructions, &certs) + clusterOpEngine := makeClusterOpEngine(instructions, options) // Give the instructions to the VClusterOpEngine to run runError := clusterOpEngine.run(vcc.Log) diff --git a/vclusterops/helpers.go b/vclusterops/helpers.go index 0c5306f..33f3736 100644 --- a/vclusterops/helpers.go +++ b/vclusterops/helpers.go @@ -265,8 +265,7 @@ func (vcc VClusterCommands) getVDBFromRunningDBImpl(vdb *VCoordinationDatabase, instructions = append(instructions, &httpsUpdateNodeState) } - certs := httpsCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} - clusterOpEngine := makeClusterOpEngine(instructions, &certs) + clusterOpEngine := makeClusterOpEngine(instructions, options) err = clusterOpEngine.run(vcc.Log) if err != nil { return fmt.Errorf("fail to retrieve database configurations, %w", err) @@ -291,8 +290,7 @@ func (vcc VClusterCommands) getClusterInfoFromRunningDB(vdb *VCoordinationDataba var instructions []clusterOp instructions = append(instructions, &httpsGetClusterInfoOp) - certs := httpsCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} - clusterOpEngine := makeClusterOpEngine(instructions, &certs) + clusterOpEngine := makeClusterOpEngine(instructions, options) err = clusterOpEngine.run(vcc.Log) if err != nil { return fmt.Errorf("fail to retrieve cluster configurations, %w", err) @@ -460,8 +458,7 @@ func (vcc *VClusterCommands) doReIP(options *DatabaseOptions, scName string, } instructions = append(instructions, &httpsReloadSpreadOp) } - certs := httpsCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} - clusterOpEngine := makeClusterOpEngine(instructions, &certs) + clusterOpEngine := makeClusterOpEngine(instructions, options) err = clusterOpEngine.run(vcc.Log) if err != nil { return fmt.Errorf("failed to re-ip nodes of subcluster %q: %w", scName, err) @@ -474,8 +471,7 @@ func (vcc *VClusterCommands) getUnreachableHosts(options *DatabaseOptions, hosts var nmaHealthInstructions []clusterOp nmaHealthOp := makeNMAHealthOpSkipUnreachable(hosts) nmaHealthInstructions = []clusterOp{&nmaHealthOp} - certs := httpsCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} - opEng := makeClusterOpEngine(nmaHealthInstructions, &certs) + opEng := makeClusterOpEngine(nmaHealthInstructions, options) err := opEng.run(vcc.Log) if err != nil { return nil, err diff --git a/vclusterops/http_request.go b/vclusterops/http_request.go index e6c2947..391e88c 100644 --- a/vclusterops/http_request.go +++ b/vclusterops/http_request.go @@ -37,6 +37,13 @@ type httpsCerts struct { caCert string } +func (req *hostHTTPRequest) setCerts(certs *httpsCerts) { + req.UseCertsInOptions = true + req.Certs.key = certs.key + req.Certs.cert = certs.cert + req.Certs.caCert = certs.caCert +} + func (req *hostHTTPRequest) buildNMAEndpoint(url string) { req.IsNMACommand = true req.Endpoint = NMACurVersion + url diff --git a/vclusterops/https_add_subcluster_op.go b/vclusterops/https_add_subcluster_op.go index c04f3b0..3734df1 100644 --- a/vclusterops/https_add_subcluster_op.go +++ b/vclusterops/https_add_subcluster_op.go @@ -95,7 +95,7 @@ func (op *httpsAddSubclusterOp) setupClusterHTTPRequest(hosts []string) error { func (op *httpsAddSubclusterOp) prepare(execContext *opEngineExecContext) error { if len(execContext.upHosts) == 0 { - return fmt.Errorf(`[%s] Cannot find any up hosts in OpEngineExecContext`, op.name) + return fmt.Errorf(`[%s] Cannot find any main cluster up hosts in OpEngineExecContext`, op.name) } // use first up host to execute https post request, this host will be the initiator hosts := []string{execContext.upHosts[0]} diff --git a/vclusterops/https_get_up_nodes_op.go b/vclusterops/https_get_up_nodes_op.go index 90180b1..905ed26 100644 --- a/vclusterops/https_get_up_nodes_op.go +++ b/vclusterops/https_get_up_nodes_op.go @@ -316,6 +316,15 @@ func (op *httpsGetUpNodesOp) isSubclusterCritical(nodesStates nodesStateInfo, up return nil } +// Check if host is eligible to add to the UP hostlist +func (op *httpsGetUpNodesOp) checkUpHostEligible(node *nodeStateInfo) bool { + // Add subcluster needs to get an UP node from main cluster as initiator + if op.cmdType == AddSubclusterCmd && node.Sandbox != util.MainClusterSandbox { + return false + } + return true +} + func (op *httpsGetUpNodesOp) collectUpHosts(nodesStates nodesStateInfo, host string, upHosts mapset.Set[string], upScInfo, sandboxInfo map[string]string, upScNodes, scNodes mapset.Set[NodeInfo]) (err error) { foundSC := false @@ -329,7 +338,10 @@ func (op *httpsGetUpNodesOp) collectUpHosts(nodesStates nodesStateInfo, host str } if node.State == util.NodeUpState { - upHosts.Add(node.Address) + // Add subcluster needs to get an UP node from main cluster as initiator + if op.checkUpHostEligible(node) { + upHosts.Add(node.Address) + } upScInfo[node.Address] = node.Subcluster if op.requiresSandboxInfo() { sandboxInfo[node.Address] = node.Sandbox @@ -341,24 +353,7 @@ func (op *httpsGetUpNodesOp) collectUpHosts(nodesStates nodesStateInfo, host str if node.IsPrimary { op.isScPrimary = true } - var n NodeInfo - // collect info for "UP" and "DOWN" nodes, ignore "UNKNOWN" nodes here - // because we want to avoid getting duplicate nodes' info. For a sandbox node, - // we will get two duplicate NodeInfo entries if we do not ignore "UNKNOWN" nodes: - // one with state "UNKNOWN" from main cluster, and the other with state "UP" - // from sandboxes. - if node.State == util.NodeUpState { - if n, err = node.asNodeInfo(); err != nil { - op.logger.PrintError("[%s] %s", op.name, err.Error()) - } else { - upScNodes.Add(n) - scNodes.Add(n) - } - } else if node.State == util.NodeDownState { - // for "DOWN" node, we cannot get its version from https response - n = node.asNodeInfoWithoutVer() - scNodes.Add(n) - } + op.populateScNodes(node, upScNodes, scNodes) } } @@ -374,6 +369,26 @@ func (op *httpsGetUpNodesOp) collectUpHosts(nodesStates nodesStateInfo, host str return nil } +func (op *httpsGetUpNodesOp) populateScNodes(node *nodeStateInfo, upScNodes, scNodes mapset.Set[NodeInfo]) { + // collect info for "UP" and "DOWN" nodes, ignore "UNKNOWN" nodes here + // because we want to avoid getting duplicate nodes' info. For a sandbox node, + // we will get two duplicate NodeInfo entries if we do not ignore "UNKNOWN" nodes: + // one with state "UNKNOWN" from main cluster, and the other with state "UP" + // from sandboxes. + if node.State == util.NodeUpState { + if n, err := node.asNodeInfo(); err != nil { + op.logger.PrintError("[%s] %s", op.name, err.Error()) + } else { + upScNodes.Add(n) + scNodes.Add(n) + } + } else if node.State == util.NodeDownState { + // for "DOWN" node, we cannot get its version from https response + n := node.asNodeInfoWithoutVer() + scNodes.Add(n) + } +} + func (op *httpsGetUpNodesOp) requiresSandboxInfo() bool { return op.cmdType == ManageConnectionDrainingCmd || op.cmdType == SetConfigurationParameterCmd || diff --git a/vclusterops/https_poll_node_state_op_test.go b/vclusterops/https_poll_node_state_op_test.go index 443f09a..fdb112a 100644 --- a/vclusterops/https_poll_node_state_op_test.go +++ b/vclusterops/https_poll_node_state_op_test.go @@ -37,8 +37,7 @@ func TestTimeoutErrorCase(t *testing.T) { instructions = append(instructions, &httpsPollNodeStateOp) // default timeout value for the op - certs := httpsCerts{} - clusterOpEngine := makeClusterOpEngine(instructions, &certs) + clusterOpEngine := makeClusterOpEngine(instructions, nil) err = clusterOpEngine.run(vlog.Printer{}) // expect timeout error in http response assert.ErrorContains(t, err, "[HTTPSPollNodeStateOp] cannot connect to host 192.0.2.1, please check if the host is still alive") @@ -50,7 +49,7 @@ func TestTimeoutErrorCase(t *testing.T) { assert.Nil(t, err) httpsPollNodeStateOp.httpRequestTimeout = httpRequestTimeoutForTest instructions = append(instructions, &httpsPollNodeStateOp) - clusterOpEngine = makeClusterOpEngine(instructions, &certs) + clusterOpEngine = makeClusterOpEngine(instructions, nil) err = clusterOpEngine.run(vlog.Printer{}) // negative value means no polling timeout assert.ErrorContains(t, err, "[HTTPSPollNodeStateOp] cannot connect to host") diff --git a/vclusterops/https_sandbox_subcluster_op.go b/vclusterops/https_sandbox_subcluster_op.go index 58045e7..04cae81 100644 --- a/vclusterops/https_sandbox_subcluster_op.go +++ b/vclusterops/https_sandbox_subcluster_op.go @@ -32,11 +32,12 @@ type httpsSandboxingOp struct { SaveRp bool Imeta bool Sls bool + ForUpgrade bool } // This op is used to sandbox the given subcluster `scName` as `sandboxName` -func makeHTTPSandboxingOp(logger vlog.Printer, scName, sandboxName string, - useHTTPPassword bool, userName string, httpsPassword *string, saveRp, imeta, sls bool) (httpsSandboxingOp, error) { +func makeHTTPSandboxingOp(logger vlog.Printer, scName, sandboxName string, useHTTPPassword bool, + userName string, httpsPassword *string, saveRp, imeta, sls, forUpgrade bool) (httpsSandboxingOp, error) { op := httpsSandboxingOp{} op.name = "HTTPSSansboxingOp" op.description = "Convert subcluster into sandbox in catalog system" @@ -47,6 +48,7 @@ func makeHTTPSandboxingOp(logger vlog.Printer, scName, sandboxName string, op.SaveRp = saveRp op.Imeta = imeta op.Sls = sls + op.ForUpgrade = forUpgrade if useHTTPPassword { err := util.ValidateUsernameAndPassword(op.name, useHTTPPassword, userName) @@ -83,6 +85,7 @@ func (op *httpsSandboxingOp) setupRequestBody() error { op.hostRequestBodyMap["save-restore-point"] = util.BoolToStr(op.SaveRp) op.hostRequestBodyMap["create-storage-locations"] = util.BoolToStr(op.Sls) op.hostRequestBodyMap["isolate-metadata"] = util.BoolToStr(op.Imeta) + op.hostRequestBodyMap["for-upgrade"] = util.BoolToStr(op.ForUpgrade) return nil } diff --git a/vclusterops/https_stage_system_tables_op.go b/vclusterops/https_stage_system_tables_op.go index a7ee8a9..cdb63c9 100644 --- a/vclusterops/https_stage_system_tables_op.go +++ b/vclusterops/https_stage_system_tables_op.go @@ -33,8 +33,8 @@ type httpsStageSystemTablesOp struct { hostNodeNameMap map[string]string stagingDir *string excludedTables []string - certs *httpsCerts // for resetting on each new request set - timeoutError error // for breaking out early if systable gathering times out + tlsOptions opTLSOptions // for resetting certs on each new request set + timeoutError error // for breaking out early if systable gathering times out } type prepareStagingSystemTableRequestData struct { @@ -208,7 +208,6 @@ func (op *httpsStageSystemTablesOp) prepare(execContext *opEngineExecContext) er } func (op *httpsStageSystemTablesOp) execute(execContext *opEngineExecContext) error { - findCertsInOptions := op.certs != nil for _, systemTableInfo := range execContext.systemTableList.SystemTableList { if slices.Contains(op.excludedTables, systemTableInfo.TableName) { continue @@ -216,7 +215,7 @@ func (op *httpsStageSystemTablesOp) execute(execContext *opEngineExecContext) er if err := op.setupClusterHTTPRequest(op.hosts, systemTableInfo.Schema, systemTableInfo.TableName); err != nil { return err } - if err := op.opBase.loadCertsIfNeeded(op.certs, findCertsInOptions); err != nil { + if err := op.opBase.loadCertsIfNeeded(op.tlsOptions); err != nil { return err } op.logger.Info("Staging System Table:", "Schema", systemTableInfo.Schema, "Table", systemTableInfo.TableName) @@ -266,9 +265,7 @@ func (op *httpsStageSystemTablesOp) finalize(_ *opEngineExecContext) error { // loadCertsIfNeeded shadows the op base function and stashes the certs instead of immediately setting them, // as httpsStageSystemTablesOp delays creation of request objects and resets them repeatedly -func (op *httpsStageSystemTablesOp) loadCertsIfNeeded(certs *httpsCerts, findCertsInOptions bool) error { - if findCertsInOptions { - op.certs = certs - } +func (op *httpsStageSystemTablesOp) loadCertsIfNeeded(tlsOptions opTLSOptions) error { + op.tlsOptions = tlsOptions return nil } diff --git a/vclusterops/install_packages.go b/vclusterops/install_packages.go index 6502a70..c35c5ad 100644 --- a/vclusterops/install_packages.go +++ b/vclusterops/install_packages.go @@ -86,9 +86,9 @@ func (vcc VClusterCommands) VInstallPackages(options *VInstallPackagesOptions) ( return nil, fmt.Errorf("fail to production instructions: %w", err) } - // Create a VClusterOpEngine. No need for certs since this operation doesn't - // talk to the NMA. - clusterOpEngine := makeClusterOpEngine(instructions, &httpsCerts{}) + // Create a VClusterOpEngine, and add certs in case the Vertica HTTPS service + // is configured to require certs. + clusterOpEngine := makeClusterOpEngine(instructions, options) // Give the instructions to the VClusterOpEngine to run runError := clusterOpEngine.run(vcc.Log) diff --git a/vclusterops/manage_connection_draining.go b/vclusterops/manage_connection_draining.go index 12de6d9..9657b74 100644 --- a/vclusterops/manage_connection_draining.go +++ b/vclusterops/manage_connection_draining.go @@ -131,8 +131,6 @@ func (opt *VManageConnectionDrainingOptions) validateAnalyzeOptions(log vlog.Pri // VManageConnectionDraining manages connection draining of nodes by pausing, redirecting, or // resuming connections. It returns any error encountered. -// -//nolint:dupl func (vcc VClusterCommands) VManageConnectionDraining(options *VManageConnectionDrainingOptions) error { // validate and analyze all options err := options.validateAnalyzeOptions(vcc.Log) @@ -147,8 +145,7 @@ func (vcc VClusterCommands) VManageConnectionDraining(options *VManageConnection } // Create a VClusterOpEngine, and add certs to the engine - certs := httpsCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} - clusterOpEngine := makeClusterOpEngine(instructions, &certs) + clusterOpEngine := makeClusterOpEngine(instructions, options) // Give the instructions to the VClusterOpEngine to run runError := clusterOpEngine.run(vcc.Log) diff --git a/vclusterops/nma_start_node_op_test.go b/vclusterops/nma_start_node_op_test.go index d36e68c..94efa25 100644 --- a/vclusterops/nma_start_node_op_test.go +++ b/vclusterops/nma_start_node_op_test.go @@ -19,8 +19,7 @@ func TestStartNodeOp(t *testing.T) { op := makeNMAStartNodeOp(hosts, startupConf) op.skipExecute = true instructions := []clusterOp{&op} - certs := httpsCerts{} - clusterOpEngine := makeClusterOpEngine(instructions, &certs) + clusterOpEngine := makeClusterOpEngine(instructions, nil) execContext := makeOpEngineExecContext(vl) clusterOpEngine.execContext = &execContext diff --git a/vclusterops/promote_sandbox_to_main.go b/vclusterops/promote_sandbox_to_main.go index 444c215..36a140d 100644 --- a/vclusterops/promote_sandbox_to_main.go +++ b/vclusterops/promote_sandbox_to_main.go @@ -118,8 +118,7 @@ func (vcc VClusterCommands) VPromoteSandboxToMain(options *VPromoteSandboxToMain } // create a VClusterOpEngine, and add certs to the engine - certs := httpsCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} - clusterOpEngine := makeClusterOpEngine(instructions, &certs) + clusterOpEngine := makeClusterOpEngine(instructions, options) // give the instructions to the VClusterOpEngine to run runError := clusterOpEngine.run(vcc.Log) diff --git a/vclusterops/re_ip.go b/vclusterops/re_ip.go index 64465fd..5bf194c 100644 --- a/vclusterops/re_ip.go +++ b/vclusterops/re_ip.go @@ -184,8 +184,7 @@ func (vcc VClusterCommands) VReIP(options *VReIPOptions) error { } // create a VClusterOpEngine, and add certs to the engine - certs := httpsCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} - clusterOpEngine := makeClusterOpEngine(instructions, &certs) + clusterOpEngine := makeClusterOpEngine(instructions, options) // give the instructions to the VClusterOpEngine to run runError := clusterOpEngine.run(vcc.Log) diff --git a/vclusterops/remove_node.go b/vclusterops/remove_node.go index 6dc130d..ef07c05 100644 --- a/vclusterops/remove_node.go +++ b/vclusterops/remove_node.go @@ -177,8 +177,7 @@ func (vcc VClusterCommands) removeNodesInCatalog(options *VRemoveNodeOptions, vd remainingHosts := util.SliceDiff(vdb.HostList, options.HostsToRemove) - certs := httpsCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} - clusterOpEngine := makeClusterOpEngine(instructions, &certs) + clusterOpEngine := makeClusterOpEngine(instructions, options) if runError := clusterOpEngine.run(vcc.Log); runError != nil { // If the machines of the to-be-removed nodes crashed or get killed, // the run error may be ignored. @@ -213,8 +212,7 @@ func (vcc VClusterCommands) handleRemoveNodeForHostsNotInCatalog(vdb *VCoordinat nmaGetNodesInfoOp := makeNMAGetNodesInfoOp(missingHosts, options.DBName, options.CatalogPrefix, false /* report all errors */, vdb) instructions := []clusterOp{&nmaGetNodesInfoOp} - certs := httpsCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} - opEng := makeClusterOpEngine(instructions, &certs) + opEng := makeClusterOpEngine(instructions, options) err := opEng.run(vcc.Log) if err != nil { return *vdb, fmt.Errorf("failed to get node info for missing hosts: %w", err) @@ -235,7 +233,7 @@ func (vcc VClusterCommands) handleRemoveNodeForHostsNotInCatalog(vdb *VCoordinat return *vdb, err } instructions = []clusterOp{&nmaDeleteDirectoriesOp} - opEng = makeClusterOpEngine(instructions, &certs) + opEng = makeClusterOpEngine(instructions, options) err = opEng.run(vcc.Log) if err != nil { return *vdb, fmt.Errorf("failed to delete directories for missing hosts: %w", err) diff --git a/vclusterops/remove_subcluster.go b/vclusterops/remove_subcluster.go index a0bc811..5573cde 100644 --- a/vclusterops/remove_subcluster.go +++ b/vclusterops/remove_subcluster.go @@ -271,8 +271,7 @@ func (vcc VClusterCommands) removeScPreCheck(vdb *VCoordinationDatabase, options &httpsFindSubclusterOp, ) - certs := httpsCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} - clusterOpEngine := makeClusterOpEngine(instructions, &certs) + clusterOpEngine := makeClusterOpEngine(instructions, options) err = clusterOpEngine.run(vcc.Log) if err != nil { // VER-88585 will improve this rfc error flow @@ -338,8 +337,7 @@ func (vcc VClusterCommands) dropSubcluster(vdb *VCoordinationDatabase, options * var instructions []clusterOp instructions = append(instructions, &httpsDropScOp) - certs := httpsCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} - clusterOpEngine := makeClusterOpEngine(instructions, &certs) + clusterOpEngine := makeClusterOpEngine(instructions, options) err = clusterOpEngine.run(vcc.Log) if err != nil { vcc.Log.Error(err, "fail to drop subcluster, details: %v", dropScErrMsg) diff --git a/vclusterops/rename_subcluster.go b/vclusterops/rename_subcluster.go index bbfca2b..a8abcec 100644 --- a/vclusterops/rename_subcluster.go +++ b/vclusterops/rename_subcluster.go @@ -128,8 +128,7 @@ func (vcc VClusterCommands) VRenameSubcluster(options *VRenameSubclusterOptions) } // create a VClusterOpEngine, and add certs to the engine - certs := httpsCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} - clusterOpEngine := makeClusterOpEngine(instructions, &certs) + clusterOpEngine := makeClusterOpEngine(instructions, options) // give the instructions to the VClusterOpEngine to run runError := clusterOpEngine.run(vcc.Log) diff --git a/vclusterops/replication.go b/vclusterops/replication.go index e7c21eb..03ac737 100644 --- a/vclusterops/replication.go +++ b/vclusterops/replication.go @@ -172,8 +172,7 @@ func (vcc VClusterCommands) VReplicateDatabase(options *VReplicationDatabaseOpti } // create a VClusterOpEngine, and add certs to the engine - certs := httpsCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} - clusterOpEngine := makeClusterOpEngine(instructions, &certs) + clusterOpEngine := makeClusterOpEngine(instructions, options) // give the instructions to the VClusterOpEngine to run runError := clusterOpEngine.run(vcc.Log) diff --git a/vclusterops/restore_points.go b/vclusterops/restore_points.go index a087d6d..6c5101b 100644 --- a/vclusterops/restore_points.go +++ b/vclusterops/restore_points.go @@ -180,8 +180,7 @@ func (vcc VClusterCommands) VShowRestorePoints(options *VShowRestorePointsOption } // create a VClusterOpEngine, and add certs to the engine - certs := httpsCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} - clusterOpEngine := makeClusterOpEngine(instructions, &certs) + clusterOpEngine := makeClusterOpEngine(instructions, options) // give the instructions to the VClusterOpEngine to run runError := clusterOpEngine.run(vcc.Log) diff --git a/vclusterops/revive_db.go b/vclusterops/revive_db.go index 54df725..e7ecd63 100644 --- a/vclusterops/revive_db.go +++ b/vclusterops/revive_db.go @@ -217,10 +217,8 @@ func (vcc VClusterCommands) VReviveDatabase(options *VReviveDatabaseOptions) (db return dbInfo, nil, fmt.Errorf("fail to produce pre-revive database instructions %w", err) } - // generate clusterOpEngine certs - certs := httpsCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} // feed the pre-revive db instructions to the VClusterOpEngine - clusterOpEngine := makeClusterOpEngine(preReviveDBInstructions, &certs) + clusterOpEngine := makeClusterOpEngine(preReviveDBInstructions, options) err = clusterOpEngine.run(vcc.GetLog()) if err != nil { return dbInfo, nil, fmt.Errorf("fail to collect the information of database in revive_db %w", err) @@ -238,7 +236,7 @@ func (vcc VClusterCommands) VReviveDatabase(options *VReviveDatabaseOptions) (db } // feed the restore db specific instructions to the VClusterOpEngine - clusterOpEngine = makeClusterOpEngine(restoreDBSpecificInstructions, &certs) + clusterOpEngine = makeClusterOpEngine(restoreDBSpecificInstructions, options) runErr := clusterOpEngine.run(vcc.GetLog()) if runErr != nil { return dbInfo, &vdb, fmt.Errorf("fail to collect the restore-specific information of database in revive_db %w", runErr) @@ -257,7 +255,7 @@ func (vcc VClusterCommands) VReviveDatabase(options *VReviveDatabaseOptions) (db } // feed revive db instructions to the VClusterOpEngine - clusterOpEngine = makeClusterOpEngine(reviveDBInstructions, &certs) + clusterOpEngine = makeClusterOpEngine(reviveDBInstructions, options) err = clusterOpEngine.run(vcc.GetLog()) if err != nil { return dbInfo, &vdb, fmt.Errorf("fail to revive database %w", err) diff --git a/vclusterops/sandbox.go b/vclusterops/sandbox.go index c42ea28..dc05288 100644 --- a/vclusterops/sandbox.go +++ b/vclusterops/sandbox.go @@ -34,6 +34,8 @@ type VSandboxOptions struct { Imeta bool // indicate whether the sandbox should create its own storage locations Sls bool + // indicate wether the sandbox is being created for online upgrade + ForUpgrade bool // The expected node names with their IPs in the subcluster, the user of vclusterOps needs // to make sure the provided values are correct. This option will be used to do re-ip in // the target sandbox. @@ -162,7 +164,7 @@ func (vcc *VClusterCommands) produceSandboxSubclusterInstructions(options *VSand // Run Sandboxing httpsSandboxSubclusterOp, err := makeHTTPSandboxingOp(vcc.Log, options.SCName, options.SandboxName, - usePassword, username, options.Password, options.SaveRp, options.Imeta, options.Sls) + usePassword, username, options.Password, options.SaveRp, options.Imeta, options.Sls, options.ForUpgrade) if err != nil { return instructions, err } @@ -219,8 +221,7 @@ func (options *VSandboxOptions) runCommand(vcc VClusterCommands) error { } // add certs and instructions to the engine - certs := httpsCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} - clusterOpEngine := makeClusterOpEngine(instructions, &certs) + clusterOpEngine := makeClusterOpEngine(instructions, options) // run the engine runError := clusterOpEngine.run(vcc.Log) diff --git a/vclusterops/set_config_parameter.go b/vclusterops/set_config_parameter.go index 89b32ce..fa32026 100644 --- a/vclusterops/set_config_parameter.go +++ b/vclusterops/set_config_parameter.go @@ -111,8 +111,7 @@ func (vcc VClusterCommands) VSetConfigurationParameters(options *VSetConfigurati } // Create a VClusterOpEngine, and add certs to the engine - certs := httpsCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} - clusterOpEngine := makeClusterOpEngine(instructions, &certs) + clusterOpEngine := makeClusterOpEngine(instructions, options) // Give the instructions to the VClusterOpEngine to run runError := clusterOpEngine.run(vcc.Log) diff --git a/vclusterops/start_db.go b/vclusterops/start_db.go index 7d5baa7..c8a2acd 100644 --- a/vclusterops/start_db.go +++ b/vclusterops/start_db.go @@ -170,8 +170,7 @@ func (vcc VClusterCommands) VStartDatabase(options *VStartDatabaseOptions) (vdbP } // create a VClusterOpEngine for start_db instructions, and add certs to the engine - certs := httpsCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} - clusterOpEngine := makeClusterOpEngine(instructions, &certs) + clusterOpEngine := makeClusterOpEngine(instructions, options) // Give the instructions to the VClusterOpEngine to run runError := clusterOpEngine.run(vcc.Log) @@ -209,8 +208,7 @@ func (vcc VClusterCommands) runStartDBPrecheck(options *VStartDatabaseOptions, v } // create a VClusterOpEngine for pre-check, and add certs to the engine - certs := httpsCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} - clusterOpEngine := makeClusterOpEngine(preInstructions, &certs) + clusterOpEngine := makeClusterOpEngine(preInstructions, options) runError := clusterOpEngine.run(vcc.Log) if runError != nil { return nil, fmt.Errorf("fail to start database pre-checks: %w", runError) diff --git a/vclusterops/start_node.go b/vclusterops/start_node.go index 7dd697e..819a19d 100644 --- a/vclusterops/start_node.go +++ b/vclusterops/start_node.go @@ -286,8 +286,7 @@ func (vcc VClusterCommands) VStartNodes(options *VStartNodesOptions) error { } // create a VClusterOpEngine, and add certs to the engine - certs := httpsCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} - clusterOpEngine := makeClusterOpEngine(instructions, &certs) + clusterOpEngine := makeClusterOpEngine(instructions, options) // Give the instructions to the VClusterOpEngine to run err = clusterOpEngine.run(vcc.Log) diff --git a/vclusterops/stop_db.go b/vclusterops/stop_db.go index e5509ea..80c83f4 100644 --- a/vclusterops/stop_db.go +++ b/vclusterops/stop_db.go @@ -157,8 +157,7 @@ func (vcc VClusterCommands) VStopDatabase(options *VStopDatabaseOptions) error { } // Create a VClusterOpEngine, and add certs to the engine - certs := httpsCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} - clusterOpEngine := makeClusterOpEngine(instructions, &certs) + clusterOpEngine := makeClusterOpEngine(instructions, options) // Give the instructions to the VClusterOpEngine to run runError := clusterOpEngine.run(vcc.Log) diff --git a/vclusterops/stop_node.go b/vclusterops/stop_node.go index 888339a..94751f8 100644 --- a/vclusterops/stop_node.go +++ b/vclusterops/stop_node.go @@ -121,8 +121,7 @@ func (vcc VClusterCommands) VStopNode(options *VStopNodeOptions) error { return fmt.Errorf("fail to produce stop node instructions, %w", err) } - certs := httpsCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} - clusterOpEngine := makeClusterOpEngine(instructions, &certs) + clusterOpEngine := makeClusterOpEngine(instructions, options) if runError := clusterOpEngine.run(vcc.Log); runError != nil { return fmt.Errorf("fail to complete stop node operation, %w", runError) } diff --git a/vclusterops/stop_subcluster.go b/vclusterops/stop_subcluster.go index afb6e1d..65b7ec8 100644 --- a/vclusterops/stop_subcluster.go +++ b/vclusterops/stop_subcluster.go @@ -117,7 +117,6 @@ func (options *VStopSubclusterOptions) validateAnalyzeOptions(log vlog.Printer) return options.analyzeOptions() } -//nolint:dupl func (vcc VClusterCommands) VStopSubcluster(options *VStopSubclusterOptions) error { /* * - Validate Options @@ -138,8 +137,7 @@ func (vcc VClusterCommands) VStopSubcluster(options *VStopSubclusterOptions) err } // Create a VClusterOpEngine, and add certs to the engine - certs := httpsCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} - clusterOpEngine := makeClusterOpEngine(instructions, &certs) + clusterOpEngine := makeClusterOpEngine(instructions, options) // Give the instructions to the VClusterOpEngine to run runError := clusterOpEngine.run(vcc.Log) diff --git a/vclusterops/unsandbox.go b/vclusterops/unsandbox.go index 0310a3d..fc0f098 100644 --- a/vclusterops/unsandbox.go +++ b/vclusterops/unsandbox.go @@ -324,8 +324,7 @@ func (options *VUnsandboxOptions) runCommand(vcc VClusterCommands) error { } // add certs and instructions to the engine - certs := httpsCerts{key: options.Key, cert: options.Cert, caCert: options.CaCert} - clusterOpEngine := makeClusterOpEngine(instructions, &certs) + clusterOpEngine := makeClusterOpEngine(instructions, options) // run the engine runError := clusterOpEngine.run(vcc.Log) diff --git a/vclusterops/vcluster_database_options.go b/vclusterops/vcluster_database_options.go index af49de5..8a11520 100644 --- a/vclusterops/vcluster_database_options.go +++ b/vclusterops/vcluster_database_options.go @@ -308,8 +308,7 @@ func (opt *DatabaseOptions) getVDBWhenDBIsDown(vcc VClusterCommands) (vdb VCoord &nmaGetNodesInfoOp, ) - certs := httpsCerts{key: opt.Key, cert: opt.Cert, caCert: opt.CaCert} - clusterOpEngine := makeClusterOpEngine(instructions1, &certs) + clusterOpEngine := makeClusterOpEngine(instructions1, opt) err = clusterOpEngine.run(vcc.Log) if err != nil { vcc.Log.PrintError("fail to retrieve node names from NMA /nodes: %v", err) @@ -327,7 +326,7 @@ func (opt *DatabaseOptions) getVDBWhenDBIsDown(vcc VClusterCommands) (vdb VCoord } instructions2 = append(instructions2, &nmaDownLoadFileOp) - clusterOpEngine = makeClusterOpEngine(instructions2, &certs) + clusterOpEngine = makeClusterOpEngine(instructions2, opt) err = clusterOpEngine.run(vcc.Log) if err != nil { vcc.Log.PrintError("fail to retrieve node details from %s: %v", descriptionFileName, err) @@ -403,9 +402,23 @@ func (opt *DatabaseOptions) isSpreadEncryptionEnabled() (enabled bool, encryptio func (opt *DatabaseOptions) runClusterOpEngine(log vlog.Printer, instructions []clusterOp) error { // Create a VClusterOpEngine, and add certs to the engine - certs := httpsCerts{key: opt.Key, cert: opt.Cert, caCert: opt.CaCert} - clusterOpEngine := makeClusterOpEngine(instructions, &certs) + clusterOpEngine := makeClusterOpEngine(instructions, opt) // Give the instructions to the VClusterOpEngine to run return clusterOpEngine.run(log) } + +/* Begin opTLSOptions interface */ + +// hasCerts indicates we want to use in memory certs. This doesn't check +// the presence of a CA cert, as we want to support providing a cert +// even when vclusterops isn't validating the peer cert. +func (opt *DatabaseOptions) hasCerts() bool { + return opt.Key != "" && opt.Cert != "" +} + +func (opt *DatabaseOptions) getCerts() *httpsCerts { + return &httpsCerts{key: opt.Key, cert: opt.Cert, caCert: opt.CaCert} +} + +/* End opTLSOptions interface */