fix(worker): Improper reload when running as controller and worker (#2438)
In cases where the server was started with both a controller and worker
stanza, the reload of the config was not properly setting the initial
upstream value on the new config. In this case the value is derived from
other config values and not necessarily set directly.
This now properly initializes the new config so that initial upstream is
set in the same manner as the first load.
Blame: 5d0cdf680f
This commit is contained in:
1 parent
0b942f4493
commit
047a66e87d
7 files changed
+653
-387
No files matched your search
@@ -286,316 +286,6 @@ func TestServer_AddEventerToContext(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupControllerPublicClusterAddress(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
inputConfig *config.Config
|
||||
inputFlagValue string
|
||||
stateFn func(t *testing.T)
|
||||
expErr bool
|
||||
expErrStr string
|
||||
expPublicClusterAddress string
|
||||
}{
|
||||
{
|
||||
name: "nil controller",
|
||||
inputConfig: &config.Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: nil,
|
||||
},
|
||||
inputFlagValue: "",
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expPublicClusterAddress: ":9201",
|
||||
},
|
||||
{
|
||||
name: "setting public cluster address directly with ip",
|
||||
inputConfig: &config.Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: &config.Controller{
|
||||
PublicClusterAddr: "127.0.0.1",
|
||||
},
|
||||
},
|
||||
inputFlagValue: "",
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expPublicClusterAddress: "127.0.0.1:9201",
|
||||
},
|
||||
{
|
||||
name: "setting public cluster address directly with ip:port",
|
||||
inputConfig: &config.Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: &config.Controller{
|
||||
PublicClusterAddr: "127.0.0.1:8080",
|
||||
},
|
||||
},
|
||||
inputFlagValue: "",
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expPublicClusterAddress: "127.0.0.1:8080",
|
||||
},
|
||||
{
|
||||
name: "setting public cluster address to env var",
|
||||
inputConfig: &config.Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: &config.Controller{
|
||||
PublicClusterAddr: "env://TEST_ENV_VAR_FOR_CONTROLLER_ADDR",
|
||||
},
|
||||
},
|
||||
inputFlagValue: "",
|
||||
stateFn: func(t *testing.T) {
|
||||
t.Setenv("TEST_ENV_VAR_FOR_CONTROLLER_ADDR", "127.0.0.1:8080")
|
||||
},
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expPublicClusterAddress: "127.0.0.1:8080",
|
||||
},
|
||||
{
|
||||
name: "setting public cluster address to env var that points to template",
|
||||
inputConfig: &config.Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: &config.Controller{
|
||||
PublicClusterAddr: "env://TEST_ENV_VAR_FOR_CONTROLLER_ADDR",
|
||||
},
|
||||
},
|
||||
inputFlagValue: "",
|
||||
stateFn: func(t *testing.T) {
|
||||
t.Setenv("TEST_ENV_VAR_FOR_CONTROLLER_ADDR", `{{ GetAllInterfaces | include "flags" "loopback" | include "type" "IPV4" | join "address" " " }}`)
|
||||
},
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expPublicClusterAddress: "127.0.0.1:9201",
|
||||
},
|
||||
{
|
||||
name: "setting public cluster address to ip template",
|
||||
inputConfig: &config.Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: &config.Controller{
|
||||
PublicClusterAddr: `{{ GetAllInterfaces | include "flags" "loopback" | include "type" "IPV4" | join "address" " " }}`,
|
||||
},
|
||||
},
|
||||
inputFlagValue: "",
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expPublicClusterAddress: "127.0.0.1:9201",
|
||||
},
|
||||
{
|
||||
name: "setting public cluster address to multiline ip template",
|
||||
inputConfig: &config.Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: &config.Controller{
|
||||
PublicClusterAddr: `{{ with $local := GetAllInterfaces | include "flags" "loopback" | include "type" "IPV4" -}}
|
||||
{{- $local | join "address" " " -}}
|
||||
{{- end }}`,
|
||||
},
|
||||
},
|
||||
inputFlagValue: "",
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expPublicClusterAddress: "127.0.0.1:9201",
|
||||
},
|
||||
{
|
||||
name: "using flag value with ip only",
|
||||
inputConfig: &config.Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: &config.Controller{},
|
||||
},
|
||||
inputFlagValue: "127.0.0.1",
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expPublicClusterAddress: "127.0.0.1:9201",
|
||||
},
|
||||
{
|
||||
name: "using flag value with ip:port",
|
||||
inputConfig: &config.Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: &config.Controller{},
|
||||
},
|
||||
inputFlagValue: "127.0.0.1:8080",
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expPublicClusterAddress: "127.0.0.1:8080",
|
||||
},
|
||||
{
|
||||
name: "using flag value with ip template",
|
||||
inputConfig: &config.Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: &config.Controller{},
|
||||
},
|
||||
inputFlagValue: `{{ GetAllInterfaces | include "flags" "loopback" | include "type" "IPV4" | join "address" " " }}`,
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expPublicClusterAddress: "127.0.0.1:9201",
|
||||
},
|
||||
{
|
||||
name: "using flag value with multiline ip template",
|
||||
inputConfig: &config.Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: &config.Controller{},
|
||||
},
|
||||
inputFlagValue: `{{ with $local := GetAllInterfaces | include "flags" "loopback" | include "type" "IPV4" -}}
|
||||
{{- $local | join "address" " " -}}
|
||||
{{- end }}`,
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expPublicClusterAddress: "127.0.0.1:9201",
|
||||
},
|
||||
{
|
||||
name: "using flag value to point to env var with ip only",
|
||||
inputConfig: &config.Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: &config.Controller{},
|
||||
},
|
||||
inputFlagValue: "env://TEST_ENV_VAR_FOR_CONTROLLER_ADDR",
|
||||
stateFn: func(t *testing.T) {
|
||||
t.Setenv("TEST_ENV_VAR_FOR_CONTROLLER_ADDR", "127.0.0.1")
|
||||
},
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expPublicClusterAddress: "127.0.0.1:9201",
|
||||
},
|
||||
{
|
||||
name: "using flag value to point to env var with ip:port",
|
||||
inputConfig: &config.Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: &config.Controller{},
|
||||
},
|
||||
inputFlagValue: "env://TEST_ENV_VAR_FOR_CONTROLLER_ADDR",
|
||||
stateFn: func(t *testing.T) {
|
||||
t.Setenv("TEST_ENV_VAR_FOR_CONTROLLER_ADDR", "127.0.0.1:8080")
|
||||
},
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expPublicClusterAddress: "127.0.0.1:8080",
|
||||
},
|
||||
{
|
||||
name: "read address from listeners ip only",
|
||||
inputConfig: &config.Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{
|
||||
{Purpose: []string{"cluster"}, Address: "127.0.0.1"},
|
||||
},
|
||||
},
|
||||
Controller: &config.Controller{},
|
||||
},
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expPublicClusterAddress: "127.0.0.1:9201",
|
||||
},
|
||||
{
|
||||
name: "read address from listeners ip:port",
|
||||
inputConfig: &config.Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{
|
||||
{Purpose: []string{"cluster"}, Address: "127.0.0.1:8080"},
|
||||
},
|
||||
},
|
||||
Controller: &config.Controller{},
|
||||
},
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expPublicClusterAddress: "127.0.0.1:8080",
|
||||
},
|
||||
{
|
||||
name: "read address from listeners is ignored on different purpose",
|
||||
inputConfig: &config.Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{
|
||||
{Purpose: []string{"somethingelse"}, Address: "127.0.0.1:8080"},
|
||||
},
|
||||
},
|
||||
Controller: &config.Controller{},
|
||||
},
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expPublicClusterAddress: ":9201",
|
||||
},
|
||||
{
|
||||
name: "using flag value to point to nonexistent file",
|
||||
inputConfig: &config.Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: &config.Controller{},
|
||||
},
|
||||
inputFlagValue: "file://this_doesnt_exist_for_sure",
|
||||
expErr: true,
|
||||
expErrStr: "Error parsing public cluster addr: error reading file at file://this_doesnt_exist_for_sure: open this_doesnt_exist_for_sure: no such file or directory",
|
||||
expPublicClusterAddress: "",
|
||||
},
|
||||
{
|
||||
name: "using flag value to provoke error in SplitHostPort",
|
||||
inputConfig: &config.Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: &config.Controller{},
|
||||
},
|
||||
inputFlagValue: "abc::123",
|
||||
expErr: true,
|
||||
expErrStr: "Error splitting public cluster adddress host/port: address abc::123: too many colons in address",
|
||||
expPublicClusterAddress: "",
|
||||
},
|
||||
{
|
||||
name: "bad ip template",
|
||||
inputConfig: &config.Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: &config.Controller{},
|
||||
},
|
||||
inputFlagValue: "{{ somethingthatdoesntexist }}",
|
||||
expErr: true,
|
||||
expErrStr: "Error parsing IP template on controller public cluster addr: unable to parse address template \"{{ somethingthatdoesntexist }}\": unable to parse template \"{{ somethingthatdoesntexist }}\": template: sockaddr.Parse:1: function \"somethingthatdoesntexist\" not defined",
|
||||
expPublicClusterAddress: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.stateFn != nil {
|
||||
tt.stateFn(t)
|
||||
}
|
||||
s := Server{}
|
||||
err := s.SetupControllerPublicClusterAddress(tt.inputConfig, tt.inputFlagValue)
|
||||
if tt.expErr {
|
||||
require.EqualError(t, err, tt.expErrStr)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, tt.inputConfig.Controller)
|
||||
require.Equal(t, tt.expPublicClusterAddress, tt.inputConfig.Controller.PublicClusterAddr)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupWorkerPublicAddress(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
|
||||
@@ -742,49 +742,6 @@ func (b *Server) DestroyDevDatabase(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Server) SetupControllerPublicClusterAddress(conf *config.Config, flagValue string) error {
|
||||
if conf.Controller == nil {
|
||||
conf.Controller = new(config.Controller)
|
||||
}
|
||||
if flagValue != "" {
|
||||
conf.Controller.PublicClusterAddr = flagValue
|
||||
}
|
||||
if conf.Controller.PublicClusterAddr == "" {
|
||||
FindAddr:
|
||||
for _, listener := range conf.Listeners {
|
||||
for _, purpose := range listener.Purpose {
|
||||
if purpose == "cluster" {
|
||||
conf.Controller.PublicClusterAddr = listener.Address
|
||||
break FindAddr
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var err error
|
||||
conf.Controller.PublicClusterAddr, err = parseutil.ParsePath(conf.Controller.PublicClusterAddr)
|
||||
if err != nil && !errors.Is(err, parseutil.ErrNotAUrl) {
|
||||
return fmt.Errorf("Error parsing public cluster addr: %w", err)
|
||||
}
|
||||
|
||||
conf.Controller.PublicClusterAddr, err = listenerutil.ParseSingleIPTemplate(conf.Controller.PublicClusterAddr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error parsing IP template on controller public cluster addr: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
host, port, err := net.SplitHostPort(conf.Controller.PublicClusterAddr)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "missing port") {
|
||||
port = "9201"
|
||||
host = conf.Controller.PublicClusterAddr
|
||||
} else {
|
||||
return fmt.Errorf("Error splitting public cluster adddress host/port: %w", err)
|
||||
}
|
||||
}
|
||||
conf.Controller.PublicClusterAddr = net.JoinHostPort(host, port)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Server) SetupWorkerPublicAddress(conf *config.Config, flagValue string) error {
|
||||
if conf.Worker == nil {
|
||||
conf.Worker = new(config.Worker)
|
||||
|
||||
@@ -517,7 +517,7 @@ func (c *Command) Run(args []string) int {
|
||||
}
|
||||
}
|
||||
|
||||
if err := c.SetupControllerPublicClusterAddress(c.Config, c.flagControllerPublicClusterAddr); err != nil {
|
||||
if err := c.Config.SetupControllerPublicClusterAddress(c.flagControllerPublicClusterAddr); err != nil {
|
||||
c.UI.Error(err.Error())
|
||||
return base.CommandUserError
|
||||
}
|
||||
|
||||
@@ -279,7 +279,7 @@ func (c *Command) Run(args []string) int {
|
||||
c.UI.Error(`Config activates controller but no listener with "cluster" purpose found`)
|
||||
return base.CommandUserError
|
||||
}
|
||||
if err := c.SetupControllerPublicClusterAddress(c.Config, ""); err != nil {
|
||||
if err := c.Config.SetupControllerPublicClusterAddress(""); err != nil {
|
||||
c.UI.Error(err.Error())
|
||||
return base.CommandUserError
|
||||
}
|
||||
@@ -304,36 +304,8 @@ func (c *Command) Run(args []string) int {
|
||||
c.Info["worker public proxy addr"] = c.Config.Worker.PublicAddr
|
||||
|
||||
if c.Config.Controller != nil {
|
||||
switch len(c.Config.Worker.InitialUpstreams) {
|
||||
case 0:
|
||||
if c.Config.Controller.PublicClusterAddr != "" {
|
||||
clusterAddr = c.Config.Controller.PublicClusterAddr
|
||||
}
|
||||
c.Config.Worker.InitialUpstreams = []string{clusterAddr}
|
||||
case 1:
|
||||
if c.Config.Worker.InitialUpstreams[0] == clusterAddr {
|
||||
break
|
||||
}
|
||||
if c.Config.Controller.PublicClusterAddr != "" &&
|
||||
c.Config.Worker.InitialUpstreams[0] == c.Config.Controller.PublicClusterAddr {
|
||||
break
|
||||
}
|
||||
// Best effort see if it's a domain name and if not assume it must match
|
||||
host, _, err := net.SplitHostPort(c.Config.Worker.InitialUpstreams[0])
|
||||
if err != nil && strings.Contains(err.Error(), "missing port in address") {
|
||||
err = nil
|
||||
host = c.Config.Worker.InitialUpstreams[0]
|
||||
}
|
||||
if err == nil {
|
||||
ip := net.ParseIP(host)
|
||||
if ip == nil {
|
||||
// Assume it's a domain name
|
||||
break
|
||||
}
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
c.UI.Error(`When running a combined controller and worker, it's invalid to specify a "initial_upstreams" or "controllers" key in the worker block with any values other than the controller cluster or upstream worker address/port when using IPs rather than DNS names`)
|
||||
if err := c.Config.SetupWorkerInitialUpstreams(); err != nil {
|
||||
c.UI.Error(err.Error())
|
||||
return base.CommandUserError
|
||||
}
|
||||
}
|
||||
@@ -846,7 +818,22 @@ func (c *Command) Reload(newConf *config.Config) error {
|
||||
}
|
||||
|
||||
if newConf != nil && c.worker != nil {
|
||||
c.worker.Reload(c.Context, newConf)
|
||||
workerReloadErr := func() error {
|
||||
if newConf.Controller != nil {
|
||||
if err := newConf.SetupControllerPublicClusterAddress(""); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := newConf.SetupWorkerInitialUpstreams(); err != nil {
|
||||
return err
|
||||
}
|
||||
c.worker.Reload(c.Context, newConf)
|
||||
return nil
|
||||
}()
|
||||
if workerReloadErr != nil {
|
||||
reloadErrors = multierror.Append(reloadErrors, fmt.Errorf("error encountered reloading worker initial upstreams: %w", workerReloadErr))
|
||||
}
|
||||
}
|
||||
|
||||
// Send a message that we reloaded. This prevents "guessing" sleep times
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -18,6 +19,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/observability/event"
|
||||
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
|
||||
configutil "github.com/hashicorp/go-secure-stdlib/configutil/v2"
|
||||
"github.com/hashicorp/go-secure-stdlib/listenerutil"
|
||||
"github.com/hashicorp/go-secure-stdlib/parseutil"
|
||||
"github.com/hashicorp/go-secure-stdlib/strutil"
|
||||
"github.com/hashicorp/hcl"
|
||||
@@ -788,3 +790,122 @@ func (c *Config) Sanitized() map[string]interface{} {
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// SetupControllerPublicClusterAddress will set the controller public address.
|
||||
// If the flagValue is provided it will be used. Otherwise this will use the
|
||||
// address from cluster listener. In either case it will check to see if no port
|
||||
// is included, and if not it will set the default port of 9201.
|
||||
//
|
||||
// If there are any errors parsing the address from the flag or listener,
|
||||
// and error is returned.
|
||||
func (c *Config) SetupControllerPublicClusterAddress(flagValue string) error {
|
||||
if c.Controller == nil {
|
||||
c.Controller = new(Controller)
|
||||
}
|
||||
if flagValue != "" {
|
||||
c.Controller.PublicClusterAddr = flagValue
|
||||
}
|
||||
if c.Controller.PublicClusterAddr == "" {
|
||||
FindAddr:
|
||||
for _, listener := range c.Listeners {
|
||||
for _, purpose := range listener.Purpose {
|
||||
if purpose == "cluster" {
|
||||
c.Controller.PublicClusterAddr = listener.Address
|
||||
break FindAddr
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var err error
|
||||
c.Controller.PublicClusterAddr, err = parseutil.ParsePath(c.Controller.PublicClusterAddr)
|
||||
if err != nil && !errors.Is(err, parseutil.ErrNotAUrl) {
|
||||
return fmt.Errorf("Error parsing public cluster addr: %w", err)
|
||||
}
|
||||
|
||||
c.Controller.PublicClusterAddr, err = listenerutil.ParseSingleIPTemplate(c.Controller.PublicClusterAddr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error parsing IP template on controller public cluster addr: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
host, port, err := net.SplitHostPort(c.Controller.PublicClusterAddr)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "missing port") {
|
||||
port = "9201"
|
||||
host = c.Controller.PublicClusterAddr
|
||||
} else {
|
||||
return fmt.Errorf("Error splitting public cluster adddress host/port: %w", err)
|
||||
}
|
||||
}
|
||||
c.Controller.PublicClusterAddr = net.JoinHostPort(host, port)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetupWorkerInitialUpstreams will set the worker initial upstreams in cases
|
||||
// where both a worker and controller stanza are provided. The initial upstreams
|
||||
// will be:
|
||||
// - The initialily provided value, if it is the same as the controller's cluster address
|
||||
// - The controller's public cluster address if it it was set
|
||||
// - The controller's cluster listener's address
|
||||
//
|
||||
// Any other value already set for iniital upstream will result in an error.
|
||||
func (c *Config) SetupWorkerInitialUpstreams() error {
|
||||
// nothing to do here
|
||||
if c.Worker == nil || c.Controller == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var clusterAddr string
|
||||
for _, lnConfig := range c.Listeners {
|
||||
switch len(lnConfig.Purpose) {
|
||||
case 0:
|
||||
return fmt.Errorf("Listener specified without a purpose")
|
||||
case 1:
|
||||
purpose := lnConfig.Purpose[0]
|
||||
switch purpose {
|
||||
case "cluster":
|
||||
clusterAddr = lnConfig.Address
|
||||
if clusterAddr == "" {
|
||||
clusterAddr = "127.0.0.1:9201"
|
||||
lnConfig.Address = clusterAddr
|
||||
}
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("Specifying a listener with more than one purpose is not supported")
|
||||
}
|
||||
}
|
||||
|
||||
switch len(c.Worker.InitialUpstreams) {
|
||||
case 0:
|
||||
if c.Controller.PublicClusterAddr != "" {
|
||||
clusterAddr = c.Controller.PublicClusterAddr
|
||||
}
|
||||
c.Worker.InitialUpstreams = []string{clusterAddr}
|
||||
case 1:
|
||||
if c.Worker.InitialUpstreams[0] == clusterAddr {
|
||||
break
|
||||
}
|
||||
if c.Controller.PublicClusterAddr != "" &&
|
||||
c.Worker.InitialUpstreams[0] == c.Controller.PublicClusterAddr {
|
||||
break
|
||||
}
|
||||
// Best effort see if it's a domain name and if not assume it must match
|
||||
host, _, err := net.SplitHostPort(c.Worker.InitialUpstreams[0])
|
||||
if err != nil && strings.Contains(err.Error(), "missing port in address") {
|
||||
err = nil
|
||||
host = c.Worker.InitialUpstreams[0]
|
||||
}
|
||||
if err == nil {
|
||||
ip := net.ParseIP(host)
|
||||
if ip == nil {
|
||||
// Assume it's a domain name
|
||||
break
|
||||
}
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
return fmt.Errorf(`When running a combined controller and worker, it's invalid to specify a "initial_upstreams" or "controllers" key in the worker block with any values other than the controller cluster or upstream worker address/port when using IPs rather than DNS names`)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1726,3 +1726,514 @@ func TestDatabaseSkipSharedLockAcquisition(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupControllerPublicClusterAddress(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
inputConfig *Config
|
||||
inputFlagValue string
|
||||
stateFn func(t *testing.T)
|
||||
expErr bool
|
||||
expErrStr string
|
||||
expPublicClusterAddress string
|
||||
}{
|
||||
{
|
||||
name: "nil controller",
|
||||
inputConfig: &Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: nil,
|
||||
},
|
||||
inputFlagValue: "",
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expPublicClusterAddress: ":9201",
|
||||
},
|
||||
{
|
||||
name: "setting public cluster address directly with ip",
|
||||
inputConfig: &Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: &Controller{
|
||||
PublicClusterAddr: "127.0.0.1",
|
||||
},
|
||||
},
|
||||
inputFlagValue: "",
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expPublicClusterAddress: "127.0.0.1:9201",
|
||||
},
|
||||
{
|
||||
name: "setting public cluster address directly with ip:port",
|
||||
inputConfig: &Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: &Controller{
|
||||
PublicClusterAddr: "127.0.0.1:8080",
|
||||
},
|
||||
},
|
||||
inputFlagValue: "",
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expPublicClusterAddress: "127.0.0.1:8080",
|
||||
},
|
||||
{
|
||||
name: "setting public cluster address to env var",
|
||||
inputConfig: &Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: &Controller{
|
||||
PublicClusterAddr: "env://TEST_ENV_VAR_FOR_CONTROLLER_ADDR",
|
||||
},
|
||||
},
|
||||
inputFlagValue: "",
|
||||
stateFn: func(t *testing.T) {
|
||||
t.Setenv("TEST_ENV_VAR_FOR_CONTROLLER_ADDR", "127.0.0.1:8080")
|
||||
},
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expPublicClusterAddress: "127.0.0.1:8080",
|
||||
},
|
||||
{
|
||||
name: "setting public cluster address to env var that points to template",
|
||||
inputConfig: &Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: &Controller{
|
||||
PublicClusterAddr: "env://TEST_ENV_VAR_FOR_CONTROLLER_ADDR",
|
||||
},
|
||||
},
|
||||
inputFlagValue: "",
|
||||
stateFn: func(t *testing.T) {
|
||||
t.Setenv("TEST_ENV_VAR_FOR_CONTROLLER_ADDR", `{{ GetAllInterfaces | include "flags" "loopback" | include "type" "IPV4" | join "address" " " }}`)
|
||||
},
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expPublicClusterAddress: "127.0.0.1:9201",
|
||||
},
|
||||
{
|
||||
name: "setting public cluster address to ip template",
|
||||
inputConfig: &Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: &Controller{
|
||||
PublicClusterAddr: `{{ GetAllInterfaces | include "flags" "loopback" | include "type" "IPV4" | join "address" " " }}`,
|
||||
},
|
||||
},
|
||||
inputFlagValue: "",
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expPublicClusterAddress: "127.0.0.1:9201",
|
||||
},
|
||||
{
|
||||
name: "setting public cluster address to multiline ip template",
|
||||
inputConfig: &Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: &Controller{
|
||||
PublicClusterAddr: `{{ with $local := GetAllInterfaces | include "flags" "loopback" | include "type" "IPV4" -}}
|
||||
{{- $local | join "address" " " -}}
|
||||
{{- end }}`,
|
||||
},
|
||||
},
|
||||
inputFlagValue: "",
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expPublicClusterAddress: "127.0.0.1:9201",
|
||||
},
|
||||
{
|
||||
name: "using flag value with ip only",
|
||||
inputConfig: &Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: &Controller{},
|
||||
},
|
||||
inputFlagValue: "127.0.0.1",
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expPublicClusterAddress: "127.0.0.1:9201",
|
||||
},
|
||||
{
|
||||
name: "using flag value with ip:port",
|
||||
inputConfig: &Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: &Controller{},
|
||||
},
|
||||
inputFlagValue: "127.0.0.1:8080",
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expPublicClusterAddress: "127.0.0.1:8080",
|
||||
},
|
||||
{
|
||||
name: "using flag value with ip template",
|
||||
inputConfig: &Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: &Controller{},
|
||||
},
|
||||
inputFlagValue: `{{ GetAllInterfaces | include "flags" "loopback" | include "type" "IPV4" | join "address" " " }}`,
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expPublicClusterAddress: "127.0.0.1:9201",
|
||||
},
|
||||
{
|
||||
name: "using flag value with multiline ip template",
|
||||
inputConfig: &Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: &Controller{},
|
||||
},
|
||||
inputFlagValue: `{{ with $local := GetAllInterfaces | include "flags" "loopback" | include "type" "IPV4" -}}
|
||||
{{- $local | join "address" " " -}}
|
||||
{{- end }}`,
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expPublicClusterAddress: "127.0.0.1:9201",
|
||||
},
|
||||
{
|
||||
name: "using flag value to point to env var with ip only",
|
||||
inputConfig: &Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: &Controller{},
|
||||
},
|
||||
inputFlagValue: "env://TEST_ENV_VAR_FOR_CONTROLLER_ADDR",
|
||||
stateFn: func(t *testing.T) {
|
||||
t.Setenv("TEST_ENV_VAR_FOR_CONTROLLER_ADDR", "127.0.0.1")
|
||||
},
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expPublicClusterAddress: "127.0.0.1:9201",
|
||||
},
|
||||
{
|
||||
name: "using flag value to point to env var with ip:port",
|
||||
inputConfig: &Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: &Controller{},
|
||||
},
|
||||
inputFlagValue: "env://TEST_ENV_VAR_FOR_CONTROLLER_ADDR",
|
||||
stateFn: func(t *testing.T) {
|
||||
t.Setenv("TEST_ENV_VAR_FOR_CONTROLLER_ADDR", "127.0.0.1:8080")
|
||||
},
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expPublicClusterAddress: "127.0.0.1:8080",
|
||||
},
|
||||
{
|
||||
name: "read address from listeners ip only",
|
||||
inputConfig: &Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{
|
||||
{Purpose: []string{"cluster"}, Address: "127.0.0.1"},
|
||||
},
|
||||
},
|
||||
Controller: &Controller{},
|
||||
},
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expPublicClusterAddress: "127.0.0.1:9201",
|
||||
},
|
||||
{
|
||||
name: "read address from listeners ip:port",
|
||||
inputConfig: &Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{
|
||||
{Purpose: []string{"cluster"}, Address: "127.0.0.1:8080"},
|
||||
},
|
||||
},
|
||||
Controller: &Controller{},
|
||||
},
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expPublicClusterAddress: "127.0.0.1:8080",
|
||||
},
|
||||
{
|
||||
name: "read address from listeners is ignored on different purpose",
|
||||
inputConfig: &Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{
|
||||
{Purpose: []string{"somethingelse"}, Address: "127.0.0.1:8080"},
|
||||
},
|
||||
},
|
||||
Controller: &Controller{},
|
||||
},
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expPublicClusterAddress: ":9201",
|
||||
},
|
||||
{
|
||||
name: "using flag value to point to nonexistent file",
|
||||
inputConfig: &Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: &Controller{},
|
||||
},
|
||||
inputFlagValue: "file://this_doesnt_exist_for_sure",
|
||||
expErr: true,
|
||||
expErrStr: "Error parsing public cluster addr: error reading file at file://this_doesnt_exist_for_sure: open this_doesnt_exist_for_sure: no such file or directory",
|
||||
expPublicClusterAddress: "",
|
||||
},
|
||||
{
|
||||
name: "using flag value to provoke error in SplitHostPort",
|
||||
inputConfig: &Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: &Controller{},
|
||||
},
|
||||
inputFlagValue: "abc::123",
|
||||
expErr: true,
|
||||
expErrStr: "Error splitting public cluster adddress host/port: address abc::123: too many colons in address",
|
||||
expPublicClusterAddress: "",
|
||||
},
|
||||
{
|
||||
name: "bad ip template",
|
||||
inputConfig: &Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: &Controller{},
|
||||
},
|
||||
inputFlagValue: "{{ somethingthatdoesntexist }}",
|
||||
expErr: true,
|
||||
expErrStr: "Error parsing IP template on controller public cluster addr: unable to parse address template \"{{ somethingthatdoesntexist }}\": unable to parse template \"{{ somethingthatdoesntexist }}\": template: sockaddr.Parse:1: function \"somethingthatdoesntexist\" not defined",
|
||||
expPublicClusterAddress: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.stateFn != nil {
|
||||
tt.stateFn(t)
|
||||
}
|
||||
err := tt.inputConfig.SetupControllerPublicClusterAddress(tt.inputFlagValue)
|
||||
if tt.expErr {
|
||||
require.EqualError(t, err, tt.expErrStr)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, tt.inputConfig.Controller)
|
||||
require.Equal(t, tt.expPublicClusterAddress, tt.inputConfig.Controller.PublicClusterAddr)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupWorkerInitialUpstreams(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
inputConfig *Config
|
||||
stateFn func(t *testing.T)
|
||||
expErr bool
|
||||
expErrStr string
|
||||
expInitialUpstreams []string
|
||||
}{
|
||||
{
|
||||
name: "NilController",
|
||||
inputConfig: &Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: nil,
|
||||
Worker: &Worker{
|
||||
InitialUpstreams: []string{"192.168.0.2:9201"},
|
||||
},
|
||||
},
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expInitialUpstreams: []string{"192.168.0.2:9201"},
|
||||
},
|
||||
{
|
||||
name: "NilWorker",
|
||||
inputConfig: &Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: &Controller{
|
||||
PublicClusterAddr: "192.168.0.3:9201",
|
||||
},
|
||||
Worker: nil,
|
||||
},
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expInitialUpstreams: nil,
|
||||
},
|
||||
{
|
||||
name: "PublicClusterAddr",
|
||||
inputConfig: &Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: &Controller{
|
||||
PublicClusterAddr: "192.168.0.4:9201",
|
||||
},
|
||||
Worker: &Worker{},
|
||||
},
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expInitialUpstreams: []string{"192.168.0.4:9201"},
|
||||
},
|
||||
{
|
||||
name: "ListenerNoAddr",
|
||||
inputConfig: &Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{
|
||||
{
|
||||
Purpose: []string{"cluster"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Controller: &Controller{},
|
||||
Worker: &Worker{},
|
||||
},
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expInitialUpstreams: []string{"127.0.0.1:9201"},
|
||||
},
|
||||
{
|
||||
name: "ListenerAddr",
|
||||
inputConfig: &Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{
|
||||
{
|
||||
Purpose: []string{"cluster"},
|
||||
Address: "192.168.0.5:9201",
|
||||
},
|
||||
},
|
||||
},
|
||||
Controller: &Controller{},
|
||||
Worker: &Worker{},
|
||||
},
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expInitialUpstreams: []string{"192.168.0.5:9201"},
|
||||
},
|
||||
{
|
||||
name: "ListenerAddrDomain",
|
||||
inputConfig: &Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{
|
||||
{
|
||||
Purpose: []string{"cluster"},
|
||||
Address: "foo.test",
|
||||
},
|
||||
},
|
||||
},
|
||||
Controller: &Controller{},
|
||||
Worker: &Worker{},
|
||||
},
|
||||
expErr: false,
|
||||
expErrStr: "",
|
||||
expInitialUpstreams: []string{"foo.test"},
|
||||
},
|
||||
{
|
||||
name: "ListenerAddrMultiplePurpose",
|
||||
inputConfig: &Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{
|
||||
{
|
||||
Purpose: []string{"cluster", "api"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Controller: &Controller{},
|
||||
Worker: &Worker{},
|
||||
},
|
||||
expErr: true,
|
||||
expErrStr: "Specifying a listener with more than one purpose is not supported",
|
||||
expInitialUpstreams: nil,
|
||||
},
|
||||
{
|
||||
name: "ListenerAddrNoPurposes",
|
||||
inputConfig: &Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{
|
||||
{
|
||||
Purpose: []string{},
|
||||
},
|
||||
},
|
||||
},
|
||||
Controller: &Controller{},
|
||||
Worker: &Worker{},
|
||||
},
|
||||
expErr: true,
|
||||
expErrStr: "Listener specified without a purpose",
|
||||
expInitialUpstreams: nil,
|
||||
},
|
||||
{
|
||||
name: "ListenerAddrMismatchAddress",
|
||||
inputConfig: &Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{
|
||||
{
|
||||
Purpose: []string{"cluster"},
|
||||
Address: "192.168.0.5:9201",
|
||||
},
|
||||
},
|
||||
},
|
||||
Controller: &Controller{},
|
||||
Worker: &Worker{
|
||||
InitialUpstreams: []string{"192.168.0.2:9201"},
|
||||
},
|
||||
},
|
||||
expErr: true,
|
||||
expErrStr: `When running a combined controller and worker, it's invalid to specify a "initial_upstreams" or "controllers" key in the worker block with any values other than the controller cluster or upstream worker address/port when using IPs rather than DNS names`,
|
||||
expInitialUpstreams: nil,
|
||||
},
|
||||
{
|
||||
name: "ClusterAddrMismatchAddress",
|
||||
inputConfig: &Config{
|
||||
SharedConfig: &configutil.SharedConfig{
|
||||
Listeners: []*listenerutil.ListenerConfig{},
|
||||
},
|
||||
Controller: &Controller{
|
||||
PublicClusterAddr: "192.168.0.3:9201",
|
||||
},
|
||||
Worker: &Worker{
|
||||
InitialUpstreams: []string{"192.168.0.2:9201"},
|
||||
},
|
||||
},
|
||||
expErr: true,
|
||||
expErrStr: `When running a combined controller and worker, it's invalid to specify a "initial_upstreams" or "controllers" key in the worker block with any values other than the controller cluster or upstream worker address/port when using IPs rather than DNS names`,
|
||||
expInitialUpstreams: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.stateFn != nil {
|
||||
tt.stateFn(t)
|
||||
}
|
||||
err := tt.inputConfig.SetupWorkerInitialUpstreams()
|
||||
if tt.expErr {
|
||||
require.EqualError(t, err, tt.expErrStr)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
if tt.inputConfig.Worker == nil {
|
||||
require.Empty(t, tt.expInitialUpstreams)
|
||||
} else {
|
||||
require.ElementsMatch(t, tt.expInitialUpstreams, tt.inputConfig.Worker.InitialUpstreams)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -650,7 +650,7 @@ func TestControllerConfig(t testing.TB, ctx context.Context, tc *TestController,
|
||||
if err := tc.b.SetupListeners(nil, opts.Config.SharedConfig, []string{"api", "cluster", "ops"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := tc.b.SetupControllerPublicClusterAddress(opts.Config, ""); err != nil {
|
||||
if err := opts.Config.SetupControllerPublicClusterAddress(""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user