Skip to content

Commit

Permalink
Sync from server repo (7e32e1f50cb)
Browse files Browse the repository at this point in the history
  • Loading branch information
Matt Spilchen committed Jan 18, 2024
1 parent 288b84d commit 7a8dd47
Show file tree
Hide file tree
Showing 8 changed files with 493 additions and 9 deletions.
1 change: 1 addition & 0 deletions commands/cluster_command_launcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ func constructCmds(_ vlog.Printer) []ClusterCommand {
makeListAllNodes(),
makeCmdReIP(),
makeCmdReviveDB(),
makeCmdShowRestorePoints(),
// sc-scope cmds
makeCmdAddSubcluster(),
makeCmdRemoveSubcluster(),
Expand Down
113 changes: 113 additions & 0 deletions commands/cmd_show_restore_points.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package commands

import (
"flag"

"github.com/vertica/vcluster/vclusterops"
"github.com/vertica/vcluster/vclusterops/util"
"github.com/vertica/vcluster/vclusterops/vlog"
)

/* CmdShowRestorePoints
*
* Implements ClusterCommand interface
*/
type CmdShowRestorePoints struct {
CmdBase
showRestorePointsOptions *vclusterops.VShowRestorePointsOptions
configurationParams *string // raw input from user, need further processing

}

func makeCmdShowRestorePoints() *CmdShowRestorePoints {
// CmdShowRestorePoints
newCmd := &CmdShowRestorePoints{}

// parser, used to parse command-line flags
newCmd.parser = flag.NewFlagSet("show_restore_points", flag.ExitOnError)
showRestorePointsOptions := vclusterops.VShowRestorePointsFactory()

// require flags
showRestorePointsOptions.DBName = newCmd.parser.String("db-name", "", "The name of the database to show restore points")
showRestorePointsOptions.CommunalStorageLocation = newCmd.parser.String("communal-storage-location", "",
util.GetEonFlagMsg("Location of communal storage"))

// optional flags
newCmd.configurationParams = newCmd.parser.String("config-param", "", util.GetOptionalFlagMsg(
"Comma-separated list of NAME=VALUE pairs for configuration parameters"))
newCmd.hostListStr = newCmd.parser.String("hosts", "", util.GetOptionalFlagMsg("Comma-separated list of hosts to participate in database."+
" Use it when you do not trust "+vclusterops.ConfigFileName))
newCmd.ipv6 = newCmd.parser.Bool("ipv6", false, "Whether the database hosts use IPv6 addresses")

showRestorePointsOptions.Password = newCmd.parser.String("password", "", util.GetOptionalFlagMsg("Database password in single quotes"))
showRestorePointsOptions.HonorUserInput = newCmd.parser.Bool("honor-user-input", false,
util.GetOptionalFlagMsg("Forcefully use the user's input instead of reading the options from "+vclusterops.ConfigFileName))
showRestorePointsOptions.ConfigDirectory = newCmd.parser.String("config-directory", "",
util.GetOptionalFlagMsg("Directory where "+vclusterops.ConfigFileName+" is located"))

newCmd.showRestorePointsOptions = &showRestorePointsOptions

return newCmd
}

func (c *CmdShowRestorePoints) CommandType() string {
return "show_restore_points"
}

func (c *CmdShowRestorePoints) Parse(inputArgv []string, logger vlog.Printer) error {
c.argv = inputArgv
err := c.ValidateParseMaskedArgv(c.CommandType(), logger)
if err != nil {
return err
}

if !util.IsOptionSet(c.parser, "ipv6") {
c.CmdBase.ipv6 = nil
}

if !util.IsOptionSet(c.parser, "config-directory") {
c.showRestorePointsOptions.ConfigDirectory = nil
}

return c.validateParse(logger)
}

func (c *CmdShowRestorePoints) 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)
if err != nil {
return err
}
if configurationParams != nil {
c.showRestorePointsOptions.ConfigurationParameters = configurationParams
}

return c.ValidateParseBaseOptions(&c.showRestorePointsOptions.DatabaseOptions)
}

func (c *CmdShowRestorePoints) Analyze(logger vlog.Printer) error {
logger.Info("Called method Analyze()")
return nil
}

func (c *CmdShowRestorePoints) Run(vcc vclusterops.VClusterCommands) error {
vcc.Log.V(1).Info("Called method Run()")

options := c.showRestorePointsOptions
config, err := options.GetDBConfig(vcc)
if err != nil {
return err
}
options.Config = config

restorePoints, err := vcc.VShowRestorePoints(options)
if err != nil {
vcc.Log.Error(err, "fail to show restore points", "DBName", *options.DBName)
return err
}

vcc.Log.PrintInfo("Successfully show restore points %v in database %s", restorePoints, *options.DBName)
return nil
}
9 changes: 9 additions & 0 deletions vclusterops/cluster_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,15 @@ func (c *ClusterConfig) getPathPrefix(dbName string) (catalogPrefix string,
dbConfig.Nodes[0].DepotPath, nil
}

func (c *ClusterConfig) getCommunalStorageLocation(dbName string) (communalStorageLocation string,
err error) {
dbConfig, ok := (*c)[dbName]
if !ok {
return "", cannotFindDBFromConfigErr(dbName)
}
return dbConfig.CommunalStorageLocation, nil
}

func (c *DatabaseConfig) getHosts() []string {
var hostList []string

Expand Down
3 changes: 2 additions & 1 deletion vclusterops/cluster_op_engine_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ type opEngineExecContext struct {
defaultSCName string // store the default subcluster name of the database
hostsWithLatestCatalog []string
primaryHostsWithLatestCatalog []string
startupCommandMap map[string][]string // store start up command map to restart nodes
startupCommandMap map[string][]string // store start up command map to start nodes
dbInfo string // store the db info that retrieved from communal storage
restorePoints []RestorePoint // store list existing restore points that queried from an archive
}

func makeOpEngineExecContext(logger vlog.Printer) opEngineExecContext {
Expand Down
156 changes: 156 additions & 0 deletions vclusterops/nma_show_restore_points_op.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/*
(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"
"errors"
"fmt"

"github.com/vertica/vcluster/vclusterops/vlog"
)

type nmaShowRestorePointsOp struct {
opBase
dbName string
communalLocation string
configurationParameters map[string]string
}

type showRestorePointsRequestData struct {
DBName string `json:"db_name"`
CommunalLocation string `json:"communal_location"`
Parameters map[string]string `json:"parameters,omitempty"`
}

// This op is used to show restore points in a database
func makeNMAShowRestorePointsOp(logger vlog.Printer,
hosts []string, dbName, communalLocation string, configurationParameters map[string]string) nmaShowRestorePointsOp {
return nmaShowRestorePointsOp{
opBase: opBase{
name: "NMAShowRestorePointsOp",
logger: logger.WithName("NMAShowRestorePointsOp"),
hosts: hosts,
},
dbName: dbName,
configurationParameters: configurationParameters,
communalLocation: communalLocation,
}
}

// make https json data
func (op *nmaShowRestorePointsOp) setupRequestBody() (map[string]string, error) {
hostRequestBodyMap := make(map[string]string, len(op.hosts))
for _, host := range op.hosts {
requestData := showRestorePointsRequestData{}
requestData.DBName = op.dbName
requestData.CommunalLocation = op.communalLocation
requestData.Parameters = op.configurationParameters

dataBytes, err := json.Marshal(requestData)
if err != nil {
return nil, fmt.Errorf("[%s] fail to marshal request data to JSON string, detail %w", op.name, err)
}
hostRequestBodyMap[host] = string(dataBytes)
}
return hostRequestBodyMap, nil
}

func (op *nmaShowRestorePointsOp) setupClusterHTTPRequest(hostRequestBodyMap map[string]string) error {
for host, requestBody := range hostRequestBodyMap {
httpRequest := hostHTTPRequest{}
httpRequest.Method = GetMethod
httpRequest.buildNMAEndpoint("restore-points")
httpRequest.RequestData = requestBody
op.clusterHTTPRequest.RequestCollection[host] = httpRequest
}

return nil
}

func (op *nmaShowRestorePointsOp) prepare(execContext *opEngineExecContext) error {
hostRequestBodyMap, err := op.setupRequestBody()
if err != nil {
return err
}

execContext.dispatcher.setup(op.hosts)
return op.setupClusterHTTPRequest(hostRequestBodyMap)
}

func (op *nmaShowRestorePointsOp) execute(execContext *opEngineExecContext) error {
if err := op.runExecute(execContext); err != nil {
return err
}

return op.processResult(execContext)
}

func (op *nmaShowRestorePointsOp) finalize(_ *opEngineExecContext) error {
return nil
}

// RestorePoint contains information about a single restore point.
type RestorePoint struct {
// Name of the archive that this restore point was created in.
Archive string
// The ID of the restore point. This is a form of a UID that is static for the restore point.
ID string
// The current index of this restore point. Lower value means it was taken more recently.
// This changes when new restore points are created.
Index int
// The timestamp when the restore point was created.
Timestamp string
}

func (op *nmaShowRestorePointsOp) processResult(execContext *opEngineExecContext) error {
var allErrs error

for host, result := range op.clusterHTTPRequest.ResultCollection {
op.logResponse(host, result)

if result.isPassing() {
/* [
{
"archive": "db",
"id": "4ee4119b-802c-4bb4-94b0-061c8748b602",
"index": 1,
"timestamp": "2023-05-02 14:10:31.038289"
},
{
"archive": "db",
"id": "bdaa4764-d8aa-4979-89e5-e642cc58d972",
"index": 2,
"timestamp": "2023-05-02 14:10:28.717667"
}
]
*/
var responseObj []RestorePoint
err := op.parseAndCheckResponse(host, result.content, &responseObj)
if err != nil {
allErrs = errors.Join(allErrs, err)
continue
}

op.logger.PrintInfo("[%s] response: %v", op.name, result.content)
execContext.restorePoints = responseObj
return nil
}

allErrs = errors.Join(allErrs, result.err)
}
return allErrs
}
38 changes: 38 additions & 0 deletions vclusterops/nma_show_restore_points_op_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
(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 (
"testing"

"github.com/stretchr/testify/assert"
"github.com/vertica/vcluster/vclusterops/vlog"
)

func TestShowRestorePointsRequestBody(t *testing.T) {
const hostName = "host1"
const dbName = "testDB"
const communalLocation = "/communal"
op := makeNMAShowRestorePointsOp(vlog.Printer{}, []string{hostName}, dbName, communalLocation, nil)

requestBody, err := op.setupRequestBody()
assert.NoError(t, err)
assert.Len(t, requestBody, 1)
assert.Contains(t, requestBody, hostName)
hostReq := requestBody[hostName]
assert.Contains(t, hostReq, `"communal_location":"`+communalLocation+`"`)
assert.Contains(t, hostReq, `"db_name":"`+dbName+`"`)
}
Loading

0 comments on commit 7a8dd47

Please sign in to comment.