Skip to content

Commit

Permalink
Sync from server repo (67a8bdb142d)
Browse files Browse the repository at this point in the history
  • Loading branch information
Matt Spilchen committed Sep 11, 2023
1 parent 97ca903 commit ee52961
Show file tree
Hide file tree
Showing 33 changed files with 109 additions and 80 deletions.
22 changes: 22 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "vcluster",
"type": "go",
"request": "launch",
"mode": "test",
"program": "${workspaceFolder}/vclusterops"
},
{
"name": "commands",
"type": "go",
"request": "launch",
"mode": "test",
"program": "${workspaceFolder}/commands"
}
]
}
2 changes: 1 addition & 1 deletion commands/cluster_command_launcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ type ClusterCommandLauncher struct {
*
* Examples:
* vcluster help
* vcluster create_db --name db1
* vcluster create_db --db-name db1
*
*
*/
Expand Down
4 changes: 2 additions & 2 deletions commands/cmd_add_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func makeCmdAddNode() *CmdAddNode {
addNodeOptions := vclusterops.VAddNodeOptionsFactory()

// required flags
addNodeOptions.Name = newCmd.parser.String("db-name", "", "The name of the database to be modified")
addNodeOptions.DBName = newCmd.parser.String("db-name", "", "The name of the database to be modified")
newCmd.newHostListStr = newCmd.parser.String("add", "", "Comma-separated list of hosts to add to the database")

// optional flags
Expand Down Expand Up @@ -139,6 +139,6 @@ func (c *CmdAddNode) Run(log vlog.Printer) error {
if err != nil {
vlog.LogPrintWarning("fail to write config file, details: %s", err)
}
vcc.Log.PrintInfo("Added nodes %s to database %s", *c.newHostListStr, *c.addNodeOptions.Name)
vcc.Log.PrintInfo("Added nodes %s to database %s", *c.newHostListStr, *c.addNodeOptions.DBName)
return nil
}
4 changes: 2 additions & 2 deletions commands/cmd_add_subcluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func makeCmdAddSubcluster() *CmdAddSubcluster {
addSubclusterOptions := vclusterops.VAddSubclusterOptionsFactory()

// required flags
addSubclusterOptions.Name = newCmd.parser.String("name", "", "The name of the database to be modified")
addSubclusterOptions.DBName = newCmd.parser.String("db-name", "", "The name of the database to be modified")
addSubclusterOptions.SCName = newCmd.parser.String("subcluster", "", "The name of the new subcluster")

// optional flags
Expand Down Expand Up @@ -134,6 +134,6 @@ func (c *CmdAddSubcluster) Run(log vlog.Printer) error {
vcc.Log.Error(err, "failed to add subcluster")
return err
}
vlog.LogPrintInfo("Added subcluster %s to database %s", *c.addSubclusterOptions.SCName, *c.addSubclusterOptions.Name)
vlog.LogPrintInfo("Added subcluster %s to database %s", *c.addSubclusterOptions.SCName, *c.addSubclusterOptions.DBName)
return nil
}
2 changes: 1 addition & 1 deletion commands/cmd_create_db.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func makeCmdCreateDB() *CmdCreateDB {
createDBOptions := vclusterops.VCreateDatabaseOptionsFactory()

// required flags
createDBOptions.Name = newCmd.parser.String("name", "", "The name of the database to be created")
createDBOptions.DBName = newCmd.parser.String("db-name", "", "The name of the database to be created")
newCmd.hostListStr = newCmd.parser.String("hosts", "", "Comma-separated list of hosts to participate in database")
createDBOptions.CatalogPrefix = newCmd.parser.String("catalog-path", "", "Path of catalog directory")
createDBOptions.DataPrefix = newCmd.parser.String("data-path", "", "Path of data directory")
Expand Down
4 changes: 2 additions & 2 deletions commands/cmd_drop_db.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func makeCmdDropDB() *CmdDropDB {
util.GetOptionalFlagMsg("Forcefully use the user's input instead of reading the options from "+vclusterops.ConfigFileName))

// TODO: the following options will be processed later
dropDBOptions.Name = newCmd.parser.String("name", "", "The name of the database to be dropped")
dropDBOptions.DBName = newCmd.parser.String("db-name", "", "The name of the database to be dropped")
dropDBOptions.CatalogPrefix = newCmd.parser.String("catalog-path", "", "The catalog path of the database")
dropDBOptions.DataPrefix = newCmd.parser.String("data-path", "", "The data path of the database")
dropDBOptions.DepotPrefix = newCmd.parser.String("depot-path", "", "The depot path of the database")
Expand Down Expand Up @@ -91,6 +91,6 @@ func (c *CmdDropDB) Run(log vlog.Printer) error {
return err
}

vlog.LogPrintInfo("Successfully dropped database %s\n", *c.dropDBOptions.Name)
vlog.LogPrintInfo("Successfully dropped database %s\n", *c.dropDBOptions.DBName)
return nil
}
2 changes: 1 addition & 1 deletion commands/cmd_re_ip.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func makeCmdReIP() *CmdReIP {
newCmd.reIPFilePath = newCmd.parser.String("re-ip-file", "", "Absolute path of the re-ip file")

reIPOpt := vclusterops.VReIPFactory()
reIPOpt.Name = newCmd.parser.String("name", "", "The name of the database")
reIPOpt.DBName = newCmd.parser.String("db-name", "", "The name of the database")
reIPOpt.CatalogPrefix = newCmd.parser.String("catalog-path", "", "The catalog path of the database")
newCmd.reIPOptions = &reIPOpt

Expand Down
4 changes: 2 additions & 2 deletions commands/cmd_remove_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func makeCmdRemoveNode() *CmdRemoveNode {
removeNodeOptions := vclusterops.VRemoveNodeOptionsFactory()

// required flags
removeNodeOptions.Name = newCmd.parser.String("db-name", "", "The name of the database to remove node(s) from")
removeNodeOptions.DBName = newCmd.parser.String("db-name", "", "The name of the database to remove node(s) from")
newCmd.hostToRemoveListStr = newCmd.parser.String("remove", "", "Comma-separated list of hosts to remove from the database")

// optional flags
Expand Down Expand Up @@ -116,7 +116,7 @@ func (c *CmdRemoveNode) Run(log vlog.Printer) error {
if err != nil {
return err
}
vlog.LogPrintInfo("Successfully removed nodes %s from database %s", *c.hostToRemoveListStr, *c.removeNodeOptions.Name)
vlog.LogPrintInfo("Successfully removed nodes %s from database %s", *c.hostToRemoveListStr, *c.removeNodeOptions.DBName)

// write cluster information to the YAML config file.
err = vclusterops.WriteClusterConfig(&vdb, c.removeNodeOptions.ConfigDirectory)
Expand Down
4 changes: 2 additions & 2 deletions commands/cmd_remove_subcluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func makeCmdRemoveSubcluster() *CmdRemoveSubcluster {
removeScOptions := vclusterops.VRemoveScOptionsFactory()

// required flags
removeScOptions.Name = newCmd.parser.String("db-name", "", "Name of the database to remove subcluster")
removeScOptions.DBName = newCmd.parser.String("db-name", "", "Name of the database to remove subcluster")
removeScOptions.SubclusterToRemove = newCmd.parser.String("remove", "", "Name of subcluster to be removed")
// VER-88096: get all nodes information from the database and remove this option
removeScOptions.DepotPrefix = newCmd.parser.String("depot-path", "", util.GetEonFlagMsg("Path to depot directory"))
Expand Down Expand Up @@ -108,7 +108,7 @@ func (c *CmdRemoveSubcluster) Run(log vlog.Printer) error {
return err
}
vcc.Log.PrintInfo("Successfully removed subcluster %s from database %s",
*c.removeScOptions.SubclusterToRemove, *c.removeScOptions.Name)
*c.removeScOptions.SubclusterToRemove, *c.removeScOptions.DBName)

// write cluster information to the YAML config file.
err = vclusterops.WriteClusterConfig(&vdb, c.removeScOptions.ConfigDirectory)
Expand Down
4 changes: 2 additions & 2 deletions commands/cmd_restart_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func makeCmdRestartNodes() *CmdRestartNodes {
restartNodesOptions := vclusterops.VRestartNodesOptionsFactory()

// require flags
restartNodesOptions.Name = newCmd.parser.String("db-name", "", "The name of the database to restart nodes")
restartNodesOptions.DBName = newCmd.parser.String("db-name", "", "The name of the database to restart nodes")
newCmd.vnodeListStr = newCmd.parser.String("restart", "",
"Comma-separated list of NODENAME=REIPHOST pairs part of the database nodes that need to be restarted")

Expand Down Expand Up @@ -115,7 +115,7 @@ func (c *CmdRestartNodes) Run(log vlog.Printer) error {
for _, ip := range c.restartNodesOptions.Nodes {
hostToRestart = append(hostToRestart, ip)
}
vlog.LogPrintInfo("Successfully restart hosts %s of the database %s", hostToRestart, *c.restartNodesOptions.Name)
vlog.LogPrintInfo("Successfully restart hosts %s of the database %s", hostToRestart, *c.restartNodesOptions.DBName)

return nil
}
6 changes: 3 additions & 3 deletions commands/cmd_revive_db.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func makeCmdReviveDB() *CmdReviveDB {
reviveDBOptions := vclusterops.VReviveDBOptionsFactory()

// require flags
reviveDBOptions.Name = newCmd.parser.String("db-name", "", "The name of the database to revive")
reviveDBOptions.DBName = newCmd.parser.String("db-name", "", "The name of the database to revive")
newCmd.hostListStr = newCmd.parser.String("hosts", "", "Comma-separated hosts that participate in the database")
reviveDBOptions.CommunalStorageLocation = newCmd.parser.String("communal-storage-location", "",
util.GetEonFlagMsg("Location of communal storage"))
Expand Down Expand Up @@ -112,7 +112,7 @@ func (c *CmdReviveDB) Run(log vlog.Printer) error {
vcc.Log.V(1).Info("Called method Run()")
dbInfo, err := vcc.VReviveDatabase(c.reviveDBOptions)
if err != nil {
vcc.Log.Error(err, "fail to revive database %s", *c.reviveDBOptions.Name)
vcc.Log.Error(err, "fail to revive database %s", *c.reviveDBOptions.DBName)
return err
}

Expand All @@ -121,7 +121,7 @@ func (c *CmdReviveDB) Run(log vlog.Printer) error {
return nil
}

vcc.Log.PrintInfo("Successfully revived database %s", *c.reviveDBOptions.Name)
vcc.Log.PrintInfo("Successfully revived database %s", *c.reviveDBOptions.DBName)

return nil
}
4 changes: 2 additions & 2 deletions commands/cmd_start_db.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func makeCmdStartDB() *CmdStartDB {
startDBOptions := vclusterops.VStartDatabaseOptionsFactory()

// require flags
startDBOptions.Name = newCmd.parser.String("name", "", util.GetOptionalFlagMsg("The name of the database to be started."+
startDBOptions.DBName = newCmd.parser.String("db-name", "", util.GetOptionalFlagMsg("The name of the database to be started."+
" Use it when you do not trust "+vclusterops.ConfigFileName))

// optional flags
Expand Down Expand Up @@ -125,6 +125,6 @@ func (c *CmdStartDB) Run(log vlog.Printer) error {
return err
}

vlog.LogPrintInfo("Successfully start the database %s\n", *c.startDBOptions.Name)
vlog.LogPrintInfo("Successfully start the database %s\n", *c.startDBOptions.DBName)
return nil
}
4 changes: 2 additions & 2 deletions commands/cmd_stop_db.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func makeCmdStopDB() *CmdStopDB {
stopDBOptions := vclusterops.VStopDatabaseOptionsFactory()

// required flags
stopDBOptions.Name = newCmd.parser.String("name", "", "The name of the database to be stopped")
stopDBOptions.DBName = newCmd.parser.String("db-name", "", "The name of the database to be stopped")

// optional flags
stopDBOptions.Password = newCmd.parser.String("password", "", util.GetOptionalFlagMsg("Database password in single quotes"))
Expand Down Expand Up @@ -135,6 +135,6 @@ func (c *CmdStopDB) Run(log vlog.Printer) error {
vcc.Log.Error(err, "failed to stop the database")
return err
}
vlog.LogPrintInfo("Stopped a database with name %s", *c.stopDBOptions.Name)
vlog.LogPrintInfo("Stopped a database with name %s", *c.stopDBOptions.DBName)
return nil
}
2 changes: 1 addition & 1 deletion vclusterops/add_subcluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ func (options *VAddSubclusterOptions) validateExtraOptions() error {
}
}
if len(dupHosts) > 0 {
return fmt.Errorf("new subcluster has hosts %v which already exist in database %s", dupHosts, *options.Name)
return fmt.Errorf("new subcluster has hosts %v which already exist in database %s", dupHosts, *options.DBName)
}

// TODO remove this log after we supported adding subcluster with nodes
Expand Down
4 changes: 2 additions & 2 deletions vclusterops/coordinator_database.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func (vdb *VCoordinationDatabase) SetFromCreateDBOptions(options *VCreateDatabas

// build coordinate db object from the create db options
// section 1: set db info
vdb.Name = *options.Name
vdb.Name = *options.DBName
vdb.CatalogPrefix = *options.CatalogPrefix
vdb.DataPrefix = *options.DataPrefix
vdb.HostList = make([]string, len(options.Hosts))
Expand Down Expand Up @@ -343,7 +343,7 @@ func (vnode *VCoordinationNode) SetFromCreateDBOptions(
options *VCreateDatabaseOptions,
host string,
) error {
dbName := *options.Name
dbName := *options.DBName
dbNameInNode := strings.ToLower(dbName)
// compute node name and complete paths for each node
for i, h := range options.Hosts {
Expand Down
4 changes: 2 additions & 2 deletions vclusterops/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,14 +153,14 @@ func getVDBFromRunningDB(vdb *VCoordinationDatabase, options *DatabaseOptions) e
return err
}

httpsGetNodesInfoOp, err := makeHTTPSGetNodesInfoOp(*options.Name, options.Hosts,
httpsGetNodesInfoOp, err := makeHTTPSGetNodesInfoOp(*options.DBName, options.Hosts,
options.usePassword, *options.UserName, options.Password, vdb)
if err != nil {
vlog.LogPrintError("fail to produce httpsGetNodesInfo instructions while retrieving database configurations, %v", err)
return err
}

httpsGetClusterInfoOp, err := makeHTTPSGetClusterInfoOp(*options.Name, options.Hosts,
httpsGetClusterInfoOp, err := makeHTTPSGetClusterInfoOp(*options.DBName, options.Hosts,
options.usePassword, *options.UserName, options.Password, vdb)
if err != nil {
vlog.LogPrintError("fail to produce httpsGetClusterInfo instructions while retrieving database configurations, %v", err)
Expand Down
37 changes: 23 additions & 14 deletions vclusterops/https_poll_node_state_op.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,8 @@ type HTTPSPollNodeStateOp struct {
timeout int
}

func makeHTTPSPollNodeStateOpWithTimeout(hosts []string,
useHTTPPassword bool, userName string, httpsPassword *string,
timeout int) (HTTPSPollNodeStateOp, error) {
op, err := makeHTTPSPollNodeStateOp(hosts, useHTTPPassword, userName, httpsPassword)
if err != nil {
return op, err
}
op.timeout = timeout
return op, nil
}

func makeHTTPSPollNodeStateOp(hosts []string,
useHTTPPassword bool, userName string,
httpsPassword *string) (HTTPSPollNodeStateOp, error) {
func makeHTTPSPollNodeStateOpHelper(hosts []string,
useHTTPPassword bool, userName string, httpsPassword *string) (HTTPSPollNodeStateOp, error) {
httpsPollNodeStateOp := HTTPSPollNodeStateOp{}
httpsPollNodeStateOp.name = "HTTPSPollNodeStateOp"
httpsPollNodeStateOp.hosts = hosts
Expand All @@ -66,6 +54,27 @@ func makeHTTPSPollNodeStateOp(hosts []string,
for _, h := range hosts {
httpsPollNodeStateOp.allHosts[h] = struct{}{}
}
return httpsPollNodeStateOp, nil
}

func makeHTTPSPollNodeStateOpWithTimeout(hosts []string,
useHTTPPassword bool, userName string, httpsPassword *string,
timeout int) (HTTPSPollNodeStateOp, error) {
op, err := makeHTTPSPollNodeStateOpHelper(hosts, useHTTPPassword, userName, httpsPassword)
if err != nil {
return op, err
}
op.timeout = timeout
return op, nil
}

func makeHTTPSPollNodeStateOp(hosts []string,
useHTTPPassword bool, userName string,
httpsPassword *string) (HTTPSPollNodeStateOp, error) {
httpsPollNodeStateOp, err := makeHTTPSPollNodeStateOpHelper(hosts, useHTTPPassword, userName, httpsPassword)
if err != nil {
return httpsPollNodeStateOp, err
}
timeoutSecondStr := util.GetEnv("NODE_STATE_POLLING_TIMEOUT", strconv.Itoa(StartupPollingTimeout))
timeoutSecond, err := strconv.Atoi(timeoutSecondStr)
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions vclusterops/https_startup_command_op.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ func (op *httpsStartUpCommandOp) setupClusterHTTPRequest(hosts []string) error {
func (op *httpsStartUpCommandOp) prepare(execContext *OpEngineExecContext) error {
// Use the /v1/startup/command endpoint for a primary Up host to view every start command of existing nodes
var primaryUpHosts []string
for host := range op.vdb.HostNodeMap {
if op.vdb.HostNodeMap[host].IsPrimary && op.vdb.HostNodeMap[host].State == util.NodeUpState {
for host, vnode := range op.vdb.HostNodeMap {
if vnode.IsPrimary && vnode.State == util.NodeUpState {
primaryUpHosts = append(primaryUpHosts, host)
break
}
Expand Down
6 changes: 3 additions & 3 deletions vclusterops/nma_download_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,10 @@ func (op *NMADownloadConfigOp) prepare(execContext *OpEngineExecContext) error {
// For restartNodes, If the sourceConfigHost input is a nil value, we find any UP primary nodes as source host to update the host input.
// we update the catalogPathMap for next download operation's steps from node information by using HTTPS /v1/nodes
var primaryUpHosts []string
for host := range op.vdb.HostNodeMap {
if op.vdb.HostNodeMap[host].IsPrimary && op.vdb.HostNodeMap[host].State == util.NodeUpState {
for host, vnode := range op.vdb.HostNodeMap {
if vnode.IsPrimary && vnode.State == util.NodeUpState {
primaryUpHosts = append(primaryUpHosts, host)
op.catalogPathMap[host] = getCatalogPath(op.vdb.HostNodeMap[host].CatalogPath)
op.catalogPathMap[host] = getCatalogPath(vnode.CatalogPath)
break
}
}
Expand Down
7 changes: 3 additions & 4 deletions vclusterops/nma_re_ip_op.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,10 +128,9 @@ func (op *NMAReIPOp) prepare(execContext *OpEngineExecContext) error {
// build mapHostToNodeName and catalogPathMap from vdb
op.mapHostToNodeName = make(map[string]string)
op.mapHostToCatalogPath = make(map[string]string)
// VER-88453 will put vnode back in the loop
for h := range op.vdb.HostNodeMap {
op.mapHostToNodeName[h] = op.vdb.HostNodeMap[h].Name
op.mapHostToCatalogPath[h] = op.vdb.HostNodeMap[h].CatalogPath
for host, vnode := range op.vdb.HostNodeMap {
op.mapHostToNodeName[host] = vnode.Name
op.mapHostToCatalogPath[host] = vnode.CatalogPath
}

// get the primary node names
Expand Down
5 changes: 2 additions & 3 deletions vclusterops/nma_read_catalog_editor_op.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,8 @@ func (op *NMAReadCatalogEditorOp) prepare(execContext *OpEngineExecContext) erro
op.catalogPathMap = make(map[string]string)
if len(op.initiator) == 0 {
op.hosts = maps.Keys(op.vdb.HostNodeMap)
// VER-88453 will put vnode back in the loop
for host := range op.vdb.HostNodeMap {
op.catalogPathMap[host] = op.vdb.HostNodeMap[host].CatalogPath
for host, vnode := range op.vdb.HostNodeMap {
op.catalogPathMap[host] = vnode.CatalogPath
}
} else {
for _, host := range op.initiator {
Expand Down
4 changes: 2 additions & 2 deletions vclusterops/nma_start_node_op.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ func (op *nmaStartNodeOp) updateRequestBody(execContext *OpEngineExecContext) er
// {ip1:[/opt/vertica/bin/vertica -D /data/practice_db/v_practice_db_node0001_catalog -C
// practice_db -n v_practice_db_node0001 -h 192.168.1.101 -p 5433 -P 4803 -Y ipv4]}
hostStartCommandMap := make(map[string][]string)
for host := range op.vdb.HostNodeMap {
hoststartCommand, ok := execContext.startupCommandMap[op.vdb.HostNodeMap[host].Name]
for host, vnode := range op.vdb.HostNodeMap {
hoststartCommand, ok := execContext.startupCommandMap[vnode.Name]
if ok {
hostStartCommandMap[host] = hoststartCommand
}
Expand Down
4 changes: 2 additions & 2 deletions vclusterops/nma_upload_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,8 @@ func (op *NMAUploadConfigOp) prepare(execContext *OpEngineExecContext) error {
// use started nodes input provided by the user
op.hosts = op.destHosts
// Update the catalogPathMap for next upload operation's steps from node List information
for host := range op.vdb.HostNodeMap {
op.catalogPathMap[host] = getCatalogPath(op.vdb.HostNodeMap[host].CatalogPath)
for host, vnode := range op.vdb.HostNodeMap {
op.catalogPathMap[host] = getCatalogPath(vnode.CatalogPath)
}
}

Expand Down
2 changes: 1 addition & 1 deletion vclusterops/re_ip.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ func (vcc *VClusterCommands) produceReIPInstructions(options *VReIPOptions) ([]C

// build a VCoordinationDatabase (vdb) object by reading the nodes' information from /v1/nodes
vdb := VCoordinationDatabase{}
nmaGetNodesInfoOp := makeNMAGetNodesInfoOp(hosts, *options.Name, *options.CatalogPrefix, &vdb)
nmaGetNodesInfoOp := makeNMAGetNodesInfoOp(hosts, *options.DBName, *options.CatalogPrefix, &vdb)

// read catalog editor to get hosts with latest catalog
nmaReadCatEdOp, err := makeNMAReadCatalogEditorOp([]string{}, &vdb)
Expand Down
Loading

0 comments on commit ee52961

Please sign in to comment.