Skip to content

Commit

Permalink
Sync from server repo (8a1dc990293)
Browse files Browse the repository at this point in the history
  • Loading branch information
Matt Spilchen committed Apr 19, 2024
1 parent 1f4bc64 commit 865a2aa
Show file tree
Hide file tree
Showing 7 changed files with 370 additions and 40 deletions.
95 changes: 86 additions & 9 deletions commands/cluster_command_launcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ const (
subclusterFlag = "subcluster"
addNodeFlag = "new-hosts"
sandboxFlag = "sandbox"
connFlag = "conn"
connKey = "conn"
)

// Flag and key for database replication
Expand Down Expand Up @@ -126,11 +128,20 @@ var flagKeyMap = map[string]string{
sourceTLSConfigFlag: sourceTLSConfigKey,
}

// target database flags to viper key map
var targetFlagKeyMap = map[string]string{
targetDBNameFlag: targetDBNameKey,
targetHostsFlag: targetHostsKey,
targetUserNameFlag: targetUserNameKey,
targetPasswordFileFlag: targetPasswordFileKey,
}

const (
createDBSubCmd = "create_db"
stopDBSubCmd = "stop_db"
reviveDBSubCmd = "revive_db"
manageConfigSubCmd = "manage_config"
createConnectionSubCmd = "create_connection"
configRecoverSubCmd = "recover"
configShowSubCmd = "show"
replicationSubCmd = "replication"
Expand Down Expand Up @@ -159,6 +170,13 @@ type cmdGlobals struct {
file *os.File
keyFile string
certFile string

// Global variables for targetDB are used for the replication subcommand
targetHosts []string
targetPasswordFile string
targetDB string
targetUserName string
connFile string
}

var (
Expand Down Expand Up @@ -262,19 +280,44 @@ func setDBOptionsUsingViper(flag string) error {
return nil
}

// setTargetDBOptionsUsingViper can set the value of flag using the relevant key
// in viper
func setTargetDBOptionsUsingViper(flag string) error {
switch flag {
case targetDBNameFlag:
globals.targetDB = viper.GetString(targetDBNameKey)
case targetHostsFlag:
globals.targetHosts = viper.GetStringSlice(targetHostsKey)
case targetUserNameFlag:
globals.targetUserName = viper.GetString(targetUserNameKey)
case targetPasswordFileFlag:
globals.targetPasswordFile = viper.GetString(targetPasswordFileKey)
default:
return fmt.Errorf("cannot find the relevant target database option for flag %q", flag)
}
return nil
}

// configViper configures viper to load database options using this order:
// user input -> environment variables -> vcluster config file
func configViper(cmd *cobra.Command, flagsInConfig []string) error {
// initialize config file
initConfig()

// target-flags are only available for replication start command
if cmd.CalledAs() == startReplicationSubCmd {
for targetFlag := range targetFlagKeyMap {
flagsInConfig = append(flagsInConfig, targetFlag)
}
}
// log-path is a flag that all the subcommands need
flagsInConfig = append(flagsInConfig, logPathFlag)
// cert-file and key-file are not available for
// - manage_config
// - manage_config show
// - create_connection
if cmd.CalledAs() != manageConfigSubCmd &&
cmd.CalledAs() != configShowSubCmd {
cmd.CalledAs() != configShowSubCmd && cmd.CalledAs() != createConnectionSubCmd {
flagsInConfig = append(flagsInConfig, certFileFlag, keyFileFlag)
}

Expand All @@ -289,7 +332,21 @@ func configViper(cmd *cobra.Command, flagsInConfig []string) error {
}
}

// bind viper keys to env vars
// Bind viper keys to environment variables
if err := bindKeysToEnv(); err != nil {
return err
}

// Load config options from file to viper
if err := loadConfig(cmd); err != nil {
return err
}

return handleViperUserInput(flagsInConfig)
}

// bind viper keys to env vars
func bindKeysToEnv() error {
err := viper.BindEnv(logPathKey, vclusterLogPathEnv)
if err != nil {
return fmt.Errorf("fail to bind viper key %q to environment variable %q: %w", logPathKey, vclusterLogPathEnv, err)
Expand All @@ -302,39 +359,58 @@ func configViper(cmd *cobra.Command, flagsInConfig []string) error {
if err != nil {
return fmt.Errorf("fail to bind viper key %q to environment variable %q: %w", certFileKey, vclusterCertFileEnv, err)
}
return nil
}

// load db options from file to viper
func loadConfig(cmd *cobra.Command) (err error) {
// load db options from config file to viper
// note: config file is not available for create_db and revive_db
// manage_config does not need viper to load config file info
if cmd.CalledAs() != createDBSubCmd &&
cmd.CalledAs() != reviveDBSubCmd &&
cmd.CalledAs() != configRecoverSubCmd &&
cmd.CalledAs() != configShowSubCmd {
err = loadConfigToViper()
err := loadConfigToViper()
if err != nil {
return err
}
}

return handleViperUserInput(flagsInConfig)
// load target db options from connection file to viper
// conn file is only available for replication subcommand
if cmd.CalledAs() == startReplicationSubCmd {
err := loadConnToViper()
if err != nil {
return err
}
}
return nil
}

func handleViperUserInput(flagsInConfig []string) error {
// if a flag is set in viper through user input, env var or config file, we assign its viper value
// if a flag is set in viper through user input, env var or config/connection file, we assign its viper value
// to database options. viper can automatically retrieve the correct value following below order:
// 1. user input
// 2. environment variable
// 3. config file
// 3. config/connection file
// if the flag is not set in viper, the default value of it will be used
for _, flag := range flagsInConfig {
if _, ok := flagKeyMap[flag]; !ok {
fmt.Printf("Warning: cannot find a relevant viper key for flag %q\n", flag)
continue
}
if viper.IsSet(flagKeyMap[flag]) {
err := setDBOptionsUsingViper(flag)
if err != nil {
return fmt.Errorf("fail to set flag %q using viper: %w", flag, err)
if _, ok := targetFlagKeyMap[flag]; !ok {
err := setDBOptionsUsingViper(flag)
if err != nil {
return fmt.Errorf("fail to set flag %q using viper: %w", flag, err)
}
} else {
err := setTargetDBOptionsUsingViper(flag)
if err != nil {
return fmt.Errorf("fail to set target flag %q using viper: %w", flag, err)
}
}
}
}
Expand Down Expand Up @@ -442,6 +518,7 @@ func constructCmds() []*cobra.Command {
makeCmdScrutinize(),
makeCmdManageConfig(),
makeCmdReplication(),
makeCmdCreateConnection(),
}
}

Expand Down
24 changes: 13 additions & 11 deletions commands/cmd_base.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ func (c *CmdBase) setCommonFlags(cmd *cobra.Command, flags []string) {
"Show the details of VCluster run in the console",
)
// keyFile and certFile are flags that all subcommands require,
// except for manage_config and `manage_config show`
if cmd.Name() != configShowSubCmd {
// except for create_connection and manage_config show
if cmd.Name() != configShowSubCmd && cmd.Name() != createConnectionSubCmd {
cmd.Flags().StringVar(
&globals.keyFile,
keyFileFlag,
Expand Down Expand Up @@ -272,6 +272,17 @@ func (c *CmdBase) setDBPassword(opt *vclusterops.DatabaseOptions) error {
return nil
}

// hyphen(`-`) is used to indicate that input should come
// from stdin rather than from a file
if c.passwordFile == "-" {
password, err := readFromStdin()
if err != nil {
return err
}
*opt.Password = strings.TrimSuffix(password, "\n")
return nil
}

password, err := c.passwordFileHelper(c.passwordFile)
if err != nil {
return err
Expand All @@ -284,15 +295,6 @@ func (c *CmdBase) passwordFileHelper(passwordFile string) (string, error) {
if passwordFile == "" {
return "", fmt.Errorf("password file path is empty")
}
// hyphen(`-`) is used to indicate that input should come
// from stdin rather than from a file
if passwordFile == "-" {
password, err := readFromStdin()
if err != nil {
return "", err
}
return strings.TrimSuffix(password, "\n"), nil
}

// Read password from file
passwordBytes, err := os.ReadFile(passwordFile)
Expand Down
122 changes: 122 additions & 0 deletions commands/cmd_create_connection.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
(c) Copyright [2023-2024] 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 commands

import (
"fmt"

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

/* CmdCreateConnection
*
* Implements ClusterCommand interface
*/
type CmdCreateConnection struct {
connectionOptions *vclusterops.VReplicationDatabaseOptions
CmdBase
}

func makeCmdCreateConnection() *cobra.Command {
newCmd := &CmdCreateConnection{}
opt := vclusterops.VReplicationDatabaseFactory()
newCmd.connectionOptions = &opt
opt.TargetPassword = new(string)

cmd := makeBasicCobraCmd(
newCmd,
createConnectionSubCmd,
"create the content of the connection file",
`This subcommand is used to create the content of the connection file.
You must specify the database name and host list. If the database has a
password, you need to provide password. If the database uses
trust authentication, the password can be ignored.
Examples:
# create the connection file to /tmp/vertica_connection.yaml
vcluster create_connection --db-name platform_test_db --hosts 10.20.30.43 --db-user \
dkr_dbadmin --password-file /tmp/password.txt --conn /tmp/vertica_connection.yaml
`,
[]string{connFlag},
)

// local flags
newCmd.setLocalFlags(cmd)

markFlagsRequired(cmd, []string{dbNameFlag, hostsFlag, connFlag})
return cmd
}

// setLocalFlags will set the local flags the command has
func (c *CmdCreateConnection) setLocalFlags(cmd *cobra.Command) {
cmd.Flags().StringVar(
&c.connectionOptions.TargetDB,
dbNameFlag,
"",
"The name of the database",
)
cmd.Flags().StringSliceVar(
&c.connectionOptions.TargetHosts,
hostsFlag,
[]string{},
"Comma-separated list of hosts in database")
cmd.Flags().StringVar(
&c.connectionOptions.TargetUserName,
dbUserFlag,
"",
"The username for connecting to the database",
)
// password flags
cmd.Flags().StringVar(
c.connectionOptions.TargetPassword,
passwordFileFlag,
"",
"Path to the file to read the password from. ",
)
cmd.Flags().StringVar(
&globals.connFile,
connFlag,
"",
"Path to the connection file")
markFlagsFileName(cmd, map[string][]string{connFlag: {"yaml"}})
}

func (c *CmdCreateConnection) Parse(inputArgv []string, logger vlog.Printer) error {
c.argv = inputArgv
logger.LogMaskedArgParse(c.argv)

return nil
}

func (c *CmdCreateConnection) Run(vcc vclusterops.ClusterCommands) error {
vcc.LogInfo("Called method Run()")

// write target db info to vcluster connection file
err := writeConn(c.connectionOptions)
if err != nil {
return fmt.Errorf("fail to write connection file, details: %s", err)
}
fmt.Printf("Successfully write connection file in %s", globals.connFile)
return nil
}

// SetDatabaseOptions will assign a vclusterops.DatabaseOptions instance
func (c *CmdCreateConnection) SetDatabaseOptions(opt *vclusterops.DatabaseOptions) {
c.connectionOptions.DatabaseOptions = *opt
}
Loading

0 comments on commit 865a2aa

Please sign in to comment.