c45bdca0b3
* raft: initial work on raft ha storage support * add note on join * add todo note * raft: add support for bootstrapping and joining existing nodes * raft: gate bootstrap join by reading leader api address from storage * raft: properly check for raft-only for certain conditionals * raft: add bootstrap to api and cli * raft: fix bootstrap cli command * raft: add test for setting up new cluster with raft HA * raft: extend TestRaft_HA_NewCluster to include inmem and consul backends * raft: add test for updating an existing cluster to use raft HA * raft: remove debug log lines, clean up verifyRaftPeers * raft: minor cleanup * raft: minor cleanup * Update physical/raft/raft.go Co-authored-by: Brian Kassouf <briankassouf@users.noreply.github.com> * Update vault/ha.go Co-authored-by: Brian Kassouf <briankassouf@users.noreply.github.com> * Update vault/ha.go Co-authored-by: Brian Kassouf <briankassouf@users.noreply.github.com> * Update vault/logical_system_raft.go Co-authored-by: Brian Kassouf <briankassouf@users.noreply.github.com> * Update vault/raft.go Co-authored-by: Brian Kassouf <briankassouf@users.noreply.github.com> * Update vault/raft.go Co-authored-by: Brian Kassouf <briankassouf@users.noreply.github.com> * address feedback comments * address feedback comments * raft: refactor tls keyring logic * address feedback comments * Update vault/raft.go Co-authored-by: Alexander Bezobchuk <alexanderbez@users.noreply.github.com> * Update vault/raft.go Co-authored-by: Alexander Bezobchuk <alexanderbez@users.noreply.github.com> * address feedback comments * testing: fix import ordering * raft: rename var, cleanup comment line * docs: remove ha_storage restriction note on raft * docs: more raft HA interaction updates with migration and recovery mode * docs: update the raft join command * raft: update comments * raft: add missing isRaftHAOnly check for clearing out state set earlier * raft: update a few ha_storage config checks * Update command/operator_raft_bootstrap.go Co-authored-by: Vishal Nayak <vishalnayak@users.noreply.github.com> * raft: address feedback comments * raft: fix panic when checking for config.HAStorage.Type * Update vault/raft.go Co-authored-by: Alexander Bezobchuk <alexanderbez@users.noreply.github.com> * Update website/pages/docs/commands/operator/raft.mdx Co-authored-by: Alexander Bezobchuk <alexanderbez@users.noreply.github.com> * raft: remove bootstrap cli command * Update vault/raft.go Co-authored-by: Brian Kassouf <briankassouf@users.noreply.github.com> * Update vault/raft.go Co-authored-by: Brian Kassouf <briankassouf@users.noreply.github.com> * raft: address review feedback * raft: revert vendored sdk * raft: don't send applied index and node ID info if we're HA-only Co-authored-by: Brian Kassouf <briankassouf@users.noreply.github.com> Co-authored-by: Alexander Bezobchuk <alexanderbez@users.noreply.github.com> Co-authored-by: Vishal Nayak <vishalnayak@users.noreply.github.com>
173 lines
4.3 KiB
Go
173 lines
4.3 KiB
Go
package command
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/hashicorp/vault/api"
|
|
"github.com/mitchellh/cli"
|
|
"github.com/posener/complete"
|
|
)
|
|
|
|
var _ cli.Command = (*OperatorRaftJoinCommand)(nil)
|
|
var _ cli.CommandAutocomplete = (*OperatorRaftJoinCommand)(nil)
|
|
|
|
type OperatorRaftJoinCommand struct {
|
|
flagRetry bool
|
|
flagLeaderCACert string
|
|
flagLeaderClientCert string
|
|
flagLeaderClientKey string
|
|
flagNonVoter bool
|
|
*BaseCommand
|
|
}
|
|
|
|
func (c *OperatorRaftJoinCommand) Synopsis() string {
|
|
return "Joins a node to the Raft cluster"
|
|
}
|
|
|
|
func (c *OperatorRaftJoinCommand) Help() string {
|
|
helpText := `
|
|
Usage: vault operator raft join [options] <leader-api-addr>
|
|
|
|
Join the current node as a peer to the Raft cluster by providing the address
|
|
of the Raft leader node.
|
|
|
|
$ vault operator raft join "http://127.0.0.2:8200"
|
|
|
|
TLS certificate data can also be consumed from a file on disk by prefixing with
|
|
the "@" symbol. For example:
|
|
|
|
$ vault operator raft join "http://127.0.0.2:8200" \
|
|
-leader-ca-cert=@leader_ca.crt \
|
|
-leader-client-cert=@leader_client.crt \
|
|
-leader-client-key=@leader.key
|
|
|
|
` + c.Flags().Help()
|
|
|
|
return strings.TrimSpace(helpText)
|
|
}
|
|
|
|
func (c *OperatorRaftJoinCommand) Flags() *FlagSets {
|
|
set := c.flagSet(FlagSetHTTP | FlagSetOutputFormat)
|
|
|
|
f := set.NewFlagSet("Command Options")
|
|
|
|
f.StringVar(&StringVar{
|
|
Name: "leader-ca-cert",
|
|
Target: &c.flagLeaderCACert,
|
|
Completion: complete.PredictNothing,
|
|
Usage: "CA cert to use when verifying the Raft leader certificate.",
|
|
})
|
|
|
|
f.StringVar(&StringVar{
|
|
Name: "leader-client-cert",
|
|
Target: &c.flagLeaderClientCert,
|
|
Completion: complete.PredictNothing,
|
|
Usage: "Client cert to use when authenticating with the Raft leader.",
|
|
})
|
|
|
|
f.StringVar(&StringVar{
|
|
Name: "leader-client-key",
|
|
Target: &c.flagLeaderClientKey,
|
|
Completion: complete.PredictNothing,
|
|
Usage: "Client key to use when authenticating with the Raft leader.",
|
|
})
|
|
|
|
f.BoolVar(&BoolVar{
|
|
Name: "retry",
|
|
Target: &c.flagRetry,
|
|
Default: false,
|
|
Usage: "Continuously retry joining the Raft cluster upon failures.",
|
|
})
|
|
|
|
f.BoolVar(&BoolVar{
|
|
Name: "non-voter",
|
|
Target: &c.flagNonVoter,
|
|
Default: false,
|
|
Usage: "(Enterprise-only) This flag is used to make the server not participate in the Raft quorum, and have it only receive the data replication stream. This can be used to add read scalability to a cluster in cases where a high volume of reads to servers are needed.",
|
|
})
|
|
|
|
return set
|
|
}
|
|
|
|
func (c *OperatorRaftJoinCommand) AutocompleteArgs() complete.Predictor {
|
|
return complete.PredictAnything
|
|
}
|
|
|
|
func (c *OperatorRaftJoinCommand) AutocompleteFlags() complete.Flags {
|
|
return c.Flags().Completions()
|
|
}
|
|
|
|
func (c *OperatorRaftJoinCommand) Run(args []string) int {
|
|
f := c.Flags()
|
|
|
|
if err := f.Parse(args); err != nil {
|
|
c.UI.Error(err.Error())
|
|
return 1
|
|
}
|
|
|
|
leaderAPIAddr := ""
|
|
|
|
args = f.Args()
|
|
switch len(args) {
|
|
case 0:
|
|
// No-op: This is acceptable if we're using raft for HA-only
|
|
case 1:
|
|
leaderAPIAddr = strings.TrimSpace(args[0])
|
|
default:
|
|
c.UI.Error(fmt.Sprintf("Too many arguments (expected 0-1, got %d)", len(args)))
|
|
return 1
|
|
}
|
|
|
|
leaderCACert, err := parseFlagFile(c.flagLeaderCACert)
|
|
if err != nil {
|
|
c.UI.Error(fmt.Sprintf("Failed to parse leader CA certificate: %s", err))
|
|
return 1
|
|
}
|
|
|
|
leaderClientCert, err := parseFlagFile(c.flagLeaderClientCert)
|
|
if err != nil {
|
|
c.UI.Error(fmt.Sprintf("Failed to parse leader client certificate: %s", err))
|
|
return 1
|
|
}
|
|
|
|
leaderClientKey, err := parseFlagFile(c.flagLeaderClientKey)
|
|
if err != nil {
|
|
c.UI.Error(fmt.Sprintf("Failed to parse leader client key: %s", err))
|
|
return 1
|
|
}
|
|
|
|
client, err := c.Client()
|
|
if err != nil {
|
|
c.UI.Error(err.Error())
|
|
return 2
|
|
}
|
|
|
|
resp, err := client.Sys().RaftJoin(&api.RaftJoinRequest{
|
|
LeaderAPIAddr: leaderAPIAddr,
|
|
LeaderCACert: leaderCACert,
|
|
LeaderClientCert: leaderClientCert,
|
|
LeaderClientKey: leaderClientKey,
|
|
Retry: c.flagRetry,
|
|
NonVoter: c.flagNonVoter,
|
|
})
|
|
if err != nil {
|
|
c.UI.Error(fmt.Sprintf("Error joining the node to the Raft cluster: %s", err))
|
|
return 2
|
|
}
|
|
|
|
switch Format(c.UI) {
|
|
case "table":
|
|
default:
|
|
return OutputData(c.UI, resp)
|
|
}
|
|
|
|
out := []string{
|
|
"Key | Value",
|
|
fmt.Sprintf("Joined | %t", resp.Joined),
|
|
}
|
|
c.UI.Output(tableOutput(out, nil))
|
|
|
|
return 0
|
|
}
|