Allow separate HA physical backend.

With no separate backend specified, HA will be attempted on the normal
physical backend.

Fixes #395.
This commit is contained in:
Jeff Mitchell 2015-12-11 15:58:10 -05:00
parent 546b3add9c
commit ced0835574
9 changed files with 199 additions and 22 deletions

View File

@ -39,12 +39,13 @@ type ServerCommand struct {
}
func (c *ServerCommand) Run(args []string) int {
var dev bool
var dev, verifyOnly bool
var configPath []string
var logLevel string
flags := c.Meta.FlagSet("server", FlagSetDefault)
flags.BoolVar(&dev, "dev", false, "")
flags.StringVar(&logLevel, "log-level", "info", "")
flags.BoolVar(&verifyOnly, "verify-only", false, "")
flags.Usage = func() { c.Ui.Error(c.Help()) }
flags.Var((*sliceflag.StringFlag)(&configPath), "config", "config")
if err := flags.Parse(args); err != nil {
@ -113,22 +114,47 @@ func (c *ServerCommand) Run(args []string) int {
return 1
}
var advertiseAddr string = config.Backend.AdvertiseAddr
// Note that "habackend" is a backend that *may* support HA;
// it defaults to the same backend as normal operations
var habackend physical.Backend = backend
// Initialize the separate HA physical backend, if it exists
if config.HABackend != nil {
habackend, err = physical.NewBackend(
config.HABackend.Type, config.HABackend.Config)
if err != nil {
c.Ui.Error(fmt.Sprintf(
"Error initializing backend of type %s: %s",
config.HABackend.Type, err))
return 1
}
if _, ok := habackend.(physical.HABackend); !ok {
c.Ui.Error("Specified HA backend does not support HA")
return 1
}
advertiseAddr = config.HABackend.AdvertiseAddr
}
// Attempt to detect the advertise address possible
if detect, ok := backend.(physical.AdvertiseDetect); ok && config.Backend.AdvertiseAddr == "" {
if detect, ok := habackend.(physical.AdvertiseDetect); ok && advertiseAddr == "" {
advertise, err := c.detectAdvertise(detect, config)
if err != nil {
c.Ui.Error(fmt.Sprintf("Error detecting advertise address: %s", err))
} else if advertise == "" {
c.Ui.Error("Failed to detect advertise address.")
} else {
config.Backend.AdvertiseAddr = advertise
advertiseAddr = advertise
}
}
// Initialize the core
core, err := vault.NewCore(&vault.CoreConfig{
AdvertiseAddr: config.Backend.AdvertiseAddr,
AdvertiseAddr: advertiseAddr,
Physical: backend,
HAPhysical: habackend,
AuditBackends: c.AuditBackends,
CredentialBackends: c.CredentialBackends,
LogicalBackends: c.LogicalBackends,
@ -186,11 +212,17 @@ func (c *ServerCommand) Run(args []string) int {
mlock.Supported(), !config.DisableMlock)
infoKeys = append(infoKeys, "log level", "mlock", "backend")
// If the backend supports HA, then note it
if _, ok := backend.(physical.HABackend); ok {
info["backend"] += " (HA available)"
info["advertise address"] = config.Backend.AdvertiseAddr
infoKeys = append(infoKeys, "advertise address")
if config.HABackend != nil {
info["HA backend"] = config.HABackend.Type
info["advertise address"] = advertiseAddr
infoKeys = append(infoKeys, "HA backend", "advertise address")
} else {
// If the backend supports HA, then note it
if _, ok := habackend.(physical.HABackend); ok {
info["backend"] += " (HA available)"
info["advertise address"] = advertiseAddr
infoKeys = append(infoKeys, "advertise address")
}
}
// Initialize the telemetry
@ -225,6 +257,10 @@ func (c *ServerCommand) Run(args []string) int {
lns = append(lns, ln)
}
if verifyOnly {
return 0
}
// Initialize the HTTP server
server := &http.Server{}
server.Handler = vaulthttp.Handler(core)

View File

@ -17,6 +17,7 @@ import (
type Config struct {
Listeners []*Listener `hcl:"-"`
Backend *Backend `hcl:"-"`
HABackend *Backend `hcl:"-"`
DisableCache bool `hcl:"disable_cache"`
DisableMlock bool `hcl:"disable_mlock"`
@ -191,6 +192,12 @@ func LoadConfigFile(path string) (*Config, error) {
return nil, err
}
}
if objs := obj.Get("ha_backend", false); objs != nil {
result.HABackend, err = loadBackend(objs)
if err != nil {
return nil, err
}
}
// A little hacky but upgrades the old stats config directives to the new way
if result.Telemetry == nil {

View File

@ -30,6 +30,14 @@ func TestLoadConfigFile(t *testing.T) {
},
},
HABackend: &Backend{
Type: "consul",
AdvertiseAddr: "snafu",
Config: map[string]string{
"bar": "baz",
},
},
Telemetry: &Telemetry{
StatsdAddr: "bar",
StatsiteAddr: "foo",
@ -111,6 +119,13 @@ func TestLoadConfigFile_json2(t *testing.T) {
},
},
HABackend: &Backend{
Type: "consul",
Config: map[string]string{
"bar": "baz",
},
},
Telemetry: &Telemetry{
StatsiteAddr: "foo",
StatsdAddr: "bar",

View File

@ -12,5 +12,10 @@ backend "consul" {
advertise_addr = "foo"
}
ha_backend "consul" {
bar = "baz"
advertise_addr = "snafu"
}
max_lease_ttl = "10h"
default_lease_ttl = "10h"

View File

@ -11,6 +11,12 @@
}
},
"ha_backend": {
"consul": {
"bar": "baz"
}
},
"telemetry": {
"statsd_address": "bar",
"statsite_address": "foo",

87
command/server_test.go Normal file
View File

@ -0,0 +1,87 @@
package command
import (
"io/ioutil"
"os"
"testing"
"github.com/mitchellh/cli"
)
var (
basehcl = `
disable_mlock = true
listener "tcp" {
address = "127.0.0.1:8200"
tls_disable = "true"
}
`
consulhcl = `
backend "consul" {
prefix = "foo/"
advertise_addr = "http://127.0.0.1:8200"
}
`
haconsulhcl = `
ha_backend "consul" {
prefix = "bar/"
advertise_addr = "http://127.0.0.1:8200"
}
`
badhaconsulhcl = `
ha_backend "file" {
path = "/dev/null"
}
`
)
func TestServer_GoodSeparateHA(t *testing.T) {
ui := new(cli.MockUi)
c := &ServerCommand{
Meta: Meta{
Ui: ui,
},
}
tmpfile, err := ioutil.TempFile("", "")
if err != nil {
t.Fatalf("error creating temp dir: %v", err)
}
tmpfile.WriteString(basehcl + consulhcl + haconsulhcl)
tmpfile.Close()
defer os.Remove(tmpfile.Name())
args := []string{"-config", tmpfile.Name(), "-verify-only", "true"}
if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
}
}
func TestServer_BadSeparateHA(t *testing.T) {
ui := new(cli.MockUi)
c := &ServerCommand{
Meta: Meta{
Ui: ui,
},
}
tmpfile, err := ioutil.TempFile("", "")
if err != nil {
t.Fatalf("error creating temp dir: %v", err)
}
tmpfile.WriteString(basehcl + consulhcl + badhaconsulhcl)
tmpfile.Close()
defer os.Remove(tmpfile.Name())
args := []string{"-config", tmpfile.Name()}
if code := c.Run(args); code == 0 {
t.Fatalf("bad: should have gotten an error on a bad HA config")
}
}

View File

@ -69,9 +69,11 @@ func TestLogical_StandbyRedirect(t *testing.T) {
defer ln2.Close()
// Create an HA Vault
inm := physical.NewInmemHA()
inm := physical.NewInmem()
inmha := physical.NewInmemHA()
conf := &vault.CoreConfig{
Physical: inm,
HAPhysical: inmha,
AdvertiseAddr: addr1,
DisableMlock: true,
}
@ -87,6 +89,7 @@ func TestLogical_StandbyRedirect(t *testing.T) {
// Create a second HA Vault
conf2 := &vault.CoreConfig{
Physical: inm,
HAPhysical: inmha,
AdvertiseAddr: addr2,
DisableMlock: true,
}

View File

@ -273,20 +273,26 @@ type CoreConfig struct {
CredentialBackends map[string]logical.Factory
AuditBackends map[string]audit.Factory
Physical physical.Backend
Logger *log.Logger
DisableCache bool // Disables the LRU cache on the physical backend
DisableMlock bool // Disables mlock syscall
CacheSize int // Custom cache size of zero for default
AdvertiseAddr string // Set as the leader address for HA
DefaultLeaseTTL time.Duration
MaxLeaseTTL time.Duration
// Defaults to the same backend as Physical. This is not a backend that
// necessarily supports HA; it is merely the one that will be attempted
// for HA operations
HAPhysical physical.Backend
Logger *log.Logger
DisableCache bool // Disables the LRU cache on the physical backend
DisableMlock bool // Disables mlock syscall
CacheSize int // Custom cache size of zero for default
AdvertiseAddr string // Set as the leader address for HA
DefaultLeaseTTL time.Duration
MaxLeaseTTL time.Duration
}
// NewCore is used to construct a new core
func NewCore(conf *CoreConfig) (*Core, error) {
// Check if this backend supports an HA configuraiton
var haBackend physical.HABackend
if ha, ok := conf.Physical.(physical.HABackend); ok {
if ha, ok := conf.HAPhysical.(physical.HABackend); ok {
haBackend = ha
}
if haBackend != nil && conf.AdvertiseAddr == "" {

View File

@ -1110,10 +1110,12 @@ func TestCore_LimitedUseToken(t *testing.T) {
func TestCore_CleanLeaderPrefix(t *testing.T) {
// Create the first core and initialize it
inm := physical.NewInmemHA()
inm := physical.NewInmem()
inmha := physical.NewInmemHA()
advertiseOriginal := "http://127.0.0.1:8200"
core, err := NewCore(&CoreConfig{
Physical: inm,
HAPhysical: inmha,
AdvertiseAddr: advertiseOriginal,
DisableMlock: true,
})
@ -1172,6 +1174,7 @@ func TestCore_CleanLeaderPrefix(t *testing.T) {
advertiseOriginal2 := "http://127.0.0.1:8500"
core2, err := NewCore(&CoreConfig{
Physical: inm,
HAPhysical: inmha,
AdvertiseAddr: advertiseOriginal2,
DisableMlock: true,
})
@ -1256,10 +1259,12 @@ func TestCore_CleanLeaderPrefix(t *testing.T) {
func TestCore_Standby(t *testing.T) {
// Create the first core and initialize it
inm := physical.NewInmemHA()
inm := physical.NewInmem()
inmha := physical.NewInmemHA()
advertiseOriginal := "http://127.0.0.1:8200"
core, err := NewCore(&CoreConfig{
Physical: inm,
HAPhysical: inmha,
AdvertiseAddr: advertiseOriginal,
DisableMlock: true,
})
@ -1313,6 +1318,7 @@ func TestCore_Standby(t *testing.T) {
advertiseOriginal2 := "http://127.0.0.1:8500"
core2, err := NewCore(&CoreConfig{
Physical: inm,
HAPhysical: inmha,
AdvertiseAddr: advertiseOriginal2,
DisableMlock: true,
})
@ -2003,10 +2009,12 @@ func testWaitActive(t *testing.T, core *Core) {
func TestCore_Standby_Rotate(t *testing.T) {
// Create the first core and initialize it
inm := physical.NewInmemHA()
inm := physical.NewInmem()
inmha := physical.NewInmemHA()
advertiseOriginal := "http://127.0.0.1:8200"
core, err := NewCore(&CoreConfig{
Physical: inm,
HAPhysical: inmha,
AdvertiseAddr: advertiseOriginal,
DisableMlock: true,
})
@ -2025,6 +2033,7 @@ func TestCore_Standby_Rotate(t *testing.T) {
advertiseOriginal2 := "http://127.0.0.1:8500"
core2, err := NewCore(&CoreConfig{
Physical: inm,
HAPhysical: inmha,
AdvertiseAddr: advertiseOriginal2,
DisableMlock: true,
})
@ -2074,10 +2083,12 @@ func TestCore_Standby_Rotate(t *testing.T) {
func TestCore_Standby_Rekey(t *testing.T) {
// Create the first core and initialize it
inm := physical.NewInmemHA()
inm := physical.NewInmem()
inmha := physical.NewInmemHA()
advertiseOriginal := "http://127.0.0.1:8200"
core, err := NewCore(&CoreConfig{
Physical: inm,
HAPhysical: inmha,
AdvertiseAddr: advertiseOriginal,
DisableMlock: true,
})
@ -2096,6 +2107,7 @@ func TestCore_Standby_Rekey(t *testing.T) {
advertiseOriginal2 := "http://127.0.0.1:8500"
core2, err := NewCore(&CoreConfig{
Physical: inm,
HAPhysical: inmha,
AdvertiseAddr: advertiseOriginal2,
DisableMlock: true,
})