You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
boundary/internal/cmd/base/server_test.go

886 lines
26 KiB

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package base
import (
"context"
"fmt"
"sync"
"testing"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/cmd/config"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/event"
"github.com/hashicorp/go-hclog"
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
"github.com/hashicorp/go-kms-wrapping/v2/extras/multi"
"github.com/hashicorp/go-secure-stdlib/configutil/v2"
"github.com/hashicorp/go-secure-stdlib/listenerutil"
"github.com/mitchellh/cli"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_NewServer(t *testing.T) {
t.Run("assert-require-no-nil-fields", func(t *testing.T) {
assert := assert.New(t)
s := NewServer(&Command{})
assert.Equal(s.Command, &Command{})
assert.NotNil(s.InfoKeys)
assert.NotNil(s.Info)
assert.NotNil(s.SecureRandomReader)
assert.NotNil(s.ReloadFuncsLock)
assert.NotNil(s.ReloadFuncs)
assert.NotNil(s.StderrLock)
})
}
func TestServer_SetupKMSes_Purposes(t *testing.T) {
tests := []struct {
name string
purposes []string
wantErrContains string
}{
{
name: "nil purposes",
},
{
name: "empty purpose",
purposes: []string{""},
wantErrContains: "KMS block missing 'purpose'",
},
{
name: "unknown purpose",
purposes: []string{"foobar"},
wantErrContains: "Unknown KMS purpose",
},
{
name: "single purpose",
purposes: []string{globals.KmsPurposeRoot},
},
{
name: "multi purpose",
purposes: []string{
globals.KmsPurposeRoot, globals.KmsPurposeRecovery, globals.KmsPurposeWorkerAuth, globals.KmsPurposeDownstreamWorkerAuth,
globals.KmsPurposeWorkerAuthStorage, globals.KmsPurposeConfig, globals.KmsPurposeBsr,
},
},
{
name: "previous root without root",
purposes: []string{globals.KmsPurposePreviousRoot},
wantErrContains: fmt.Sprintf("KMS block contains '%s' without '%s'", globals.KmsPurposePreviousRoot, globals.KmsPurposeRoot),
},
{
name: "root and previous in the same stanza",
purposes: []string{globals.KmsPurposeRoot, globals.KmsPurposePreviousRoot},
wantErrContains: fmt.Sprintf("KMS blocks with purposes '%s' and '%s' must have different key IDs", globals.KmsPurposeRoot, globals.KmsPurposePreviousRoot),
},
{
name: "duplicate root purposes",
purposes: []string{globals.KmsPurposeRoot, globals.KmsPurposeRoot},
wantErrContains: fmt.Sprintf("Duplicate KMS block for purpose '%s'", globals.KmsPurposeRoot),
},
{
name: "duplicate previous root purposes",
purposes: []string{globals.KmsPurposePreviousRoot, globals.KmsPurposePreviousRoot},
wantErrContains: fmt.Sprintf("Duplicate KMS block for purpose '%s'", globals.KmsPurposePreviousRoot),
},
{
name: "duplicate worker auth purposes",
purposes: []string{globals.KmsPurposeWorkerAuth, globals.KmsPurposeWorkerAuth},
wantErrContains: fmt.Sprintf("Duplicate KMS block for purpose '%s'", globals.KmsPurposeWorkerAuth),
},
{
name: "duplicate worker auth storage purposes",
purposes: []string{globals.KmsPurposeWorkerAuthStorage, globals.KmsPurposeWorkerAuthStorage},
wantErrContains: fmt.Sprintf("Duplicate KMS block for purpose '%s'", globals.KmsPurposeWorkerAuthStorage),
},
{
name: "duplicate bsr kms purposes",
purposes: []string{globals.KmsPurposeBsr, globals.KmsPurposeBsr},
wantErrContains: fmt.Sprintf("Duplicate KMS block for purpose '%s'", globals.KmsPurposeBsr),
},
{
name: "duplicate recovery purposes",
purposes: []string{globals.KmsPurposeRecovery, globals.KmsPurposeRecovery},
wantErrContains: fmt.Sprintf("Duplicate KMS block for purpose '%s'", globals.KmsPurposeRecovery),
},
}
logger := hclog.Default()
serLock := new(sync.Mutex)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
conf := &configutil.SharedConfig{
Seals: []*configutil.KMS{
{
Type: "aead",
Purpose: tt.purposes,
},
},
}
s := NewServer(&Command{Context: context.Background()})
require.NoError(s.SetupEventing(s.Context, logger, serLock, "setup-kms-testing"))
err := s.SetupKMSes(s.Context, cli.NewMockUi(), &config.Config{SharedConfig: conf})
if tt.wantErrContains != "" {
require.Error(err)
assert.Contains(err.Error(), tt.wantErrContains)
return
}
require.NoError(err)
for _, purpose := range tt.purposes {
switch purpose {
case globals.KmsPurposeRoot:
assert.NotNil(s.RootKms)
case globals.KmsPurposeWorkerAuth:
assert.NotNil(s.WorkerAuthKms)
case globals.KmsPurposeWorkerAuthStorage:
assert.NotNil(s.WorkerAuthStorageKms)
case globals.KmsPurposeRecovery:
assert.NotNil(s.RecoveryKms)
case globals.KmsPurposeBsr:
assert.NotNil(s.BsrKms)
}
}
})
}
}
func TestServer_SetupKMSes_RootMigration(t *testing.T) {
t.Parallel()
t.Run("correctly-pools-root-and-previous", func(t *testing.T) {
t.Parallel()
assert, require := assert.New(t), require.New(t)
logger := hclog.Default()
serLock := new(sync.Mutex)
conf := &configutil.SharedConfig{
Seals: []*configutil.KMS{
{
Type: "aead",
Purpose: []string{
globals.KmsPurposeRoot,
},
Config: map[string]string{
"key_id": "root",
},
},
{
Type: "aead",
Purpose: []string{
globals.KmsPurposePreviousRoot,
},
Config: map[string]string{
"key_id": "previous_root",
},
},
},
}
s := NewServer(&Command{Context: context.Background()})
require.NoError(s.SetupEventing(s.Context, logger, serLock, "setup-kms-testing"))
err := s.SetupKMSes(s.Context, cli.NewMockUi(), &config.Config{SharedConfig: conf})
require.NoError(err)
require.NotNil(s.RootKms)
typ, err := s.RootKms.Type(s.Context)
require.NoError(err)
assert.Equal(wrapping.WrapperTypePooled, typ)
// Ensure that the root is the encryptor
keyId, err := s.RootKms.KeyId(s.Context)
require.NoError(err)
assert.Equal("root", keyId)
// Ensure that the previous root is in the wrapper too
assert.Equal([]string{"previous_root", "root"}, s.RootKms.(*multi.PooledWrapper).AllKeyIds())
})
t.Run("errors-on-previous-without-root", func(t *testing.T) {
t.Parallel()
require := require.New(t)
logger := hclog.Default()
serLock := new(sync.Mutex)
conf := &configutil.SharedConfig{
Seals: []*configutil.KMS{
{
Type: "aead",
Purpose: []string{
globals.KmsPurposePreviousRoot,
},
},
},
}
s := NewServer(&Command{Context: context.Background()})
require.NoError(s.SetupEventing(s.Context, logger, serLock, "setup-kms-testing"))
err := s.SetupKMSes(s.Context, cli.NewMockUi(), &config.Config{SharedConfig: conf})
require.Error(err)
})
t.Run("errors-on-previous-and-root-with-same-key-id", func(t *testing.T) {
t.Parallel()
require := require.New(t)
logger := hclog.Default()
serLock := new(sync.Mutex)
conf := &configutil.SharedConfig{
Seals: []*configutil.KMS{
{
Type: "aead",
Purpose: []string{
globals.KmsPurposeRoot,
},
Config: map[string]string{
"key_id": "root",
},
},
{
Type: "aead",
Purpose: []string{
globals.KmsPurposePreviousRoot,
},
Config: map[string]string{
"key_id": "root",
},
},
},
}
s := NewServer(&Command{Context: context.Background()})
require.NoError(s.SetupEventing(s.Context, logger, serLock, "setup-kms-testing"))
err := s.SetupKMSes(s.Context, cli.NewMockUi(), &config.Config{SharedConfig: conf})
require.Error(err)
})
}
func TestServer_SetupEventing(t *testing.T) {
// DO NOT run these test in parallel since they have a dependency on
// event.sysEventer
testLock := &sync.Mutex{}
testLogger := hclog.New(&hclog.LoggerOptions{
Mutex: testLock,
})
setTrue := true
setFalse := false
tests := []struct {
name string
s *Server
logger hclog.Logger
lock *sync.Mutex
opt []Option
want event.EventerConfig
wantErrMatch *errors.Template
wantErrIs error
wantErrContains string
}{
{
name: "missing-logger",
s: &Server{},
lock: testLock,
wantErrMatch: errors.T(errors.InvalidParameter),
wantErrContains: "missing logger",
},
{
name: "missing-serialization-lock",
s: &Server{},
logger: testLogger,
wantErrMatch: errors.T(errors.InvalidParameter),
wantErrContains: "missing serialization lock",
},
{
name: "opts-none",
s: &Server{},
logger: testLogger,
lock: testLock,
want: *event.DefaultEventerConfig(),
},
{
name: "opts-event-flags",
s: &Server{},
logger: testLogger,
lock: testLock,
opt: []Option{WithEventFlags(&EventFlags{
Format: event.JSONSinkFormat,
AuditEnabled: &setTrue,
ObservationsEnabled: &setFalse,
SysEventsEnabled: &setFalse,
TelemetryEnabled: &setFalse,
})},
want: func() event.EventerConfig {
c := event.DefaultEventerConfig()
c.AuditEnabled = true
c.ObservationsEnabled = false
c.SysEventsEnabled = false
c.TelemetryEnabled = false
return *c
}(),
},
{
name: "opts-event-flags-invalid",
s: &Server{},
logger: testLogger,
lock: testLock,
opt: []Option{WithEventFlags(&EventFlags{
Format: "invalid-format",
})},
wantErrIs: event.ErrInvalidParameter,
wantErrContains: "not a valid sink format",
},
{
name: "opts-eventer-config",
s: &Server{},
logger: testLogger,
lock: testLock,
opt: []Option{WithEventerConfig(&event.EventerConfig{
ObservationsEnabled: false,
SysEventsEnabled: false,
AuditEnabled: true,
TelemetryEnabled: false,
})},
want: func() event.EventerConfig {
c := event.DefaultEventerConfig()
c.AuditEnabled = true
c.ObservationsEnabled = false
c.SysEventsEnabled = false
c.TelemetryEnabled = false
return *c
}(),
},
{
name: "opts-eventer-config-invalid",
s: &Server{},
logger: testLogger,
lock: testLock,
opt: []Option{WithEventerConfig(&event.EventerConfig{
Sinks: []*event.SinkConfig{
{
Format: "invalid-format",
},
},
})},
wantErrIs: event.ErrInvalidParameter,
wantErrContains: "sink 0 is invalid",
},
{
name: "opts-eventer-config-observation-telemetry-invalid",
s: &Server{},
logger: testLogger,
lock: testLock,
opt: []Option{WithEventFlags(&EventFlags{
Format: event.JSONSinkFormat,
AuditEnabled: &setTrue,
ObservationsEnabled: &setFalse,
SysEventsEnabled: &setFalse,
TelemetryEnabled: &setTrue,
})},
wantErrIs: event.ErrInvalidParameter,
wantErrContains: "telemetry events require observation event to be enabled",
},
{
name: "opts-eventer-config-observation-on-telemetry-off",
s: &Server{},
logger: testLogger,
lock: testLock,
opt: []Option{WithEventFlags(&EventFlags{
Format: event.JSONSinkFormat,
AuditEnabled: &setFalse,
ObservationsEnabled: &setTrue,
SysEventsEnabled: &setFalse,
TelemetryEnabled: &setFalse,
})},
want: func() event.EventerConfig {
c := event.DefaultEventerConfig()
c.AuditEnabled = false
c.ObservationsEnabled = true
c.SysEventsEnabled = false
c.TelemetryEnabled = false
return *c
}(),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
event.TestResetSystEventer(t)
err := tt.s.SetupEventing(context.Background(), tt.logger, tt.lock, tt.name, tt.opt...)
if tt.wantErrMatch != nil || tt.wantErrIs != nil {
require.Error(err)
assert.Nil(tt.s.Eventer)
assert.Nil(event.SysEventer())
if tt.wantErrMatch != nil {
assert.Truef(errors.Match(tt.wantErrMatch, err), "want %q and got %q", tt.wantErrMatch.Code, err.Error())
}
if tt.wantErrIs != nil {
assert.ErrorIs(err, tt.wantErrIs)
}
if tt.wantErrContains != "" {
assert.Contains(err.Error(), tt.wantErrContains)
}
return
}
require.NoError(err)
assert.Equal(tt.want, event.TestGetEventerConfig(t, tt.s.Eventer))
})
}
}
func TestServer_AddEventerToContext(t *testing.T) {
testLock := &sync.Mutex{}
testLogger := hclog.New(&hclog.LoggerOptions{
Mutex: testLock,
})
testEventer, err := event.NewEventer(testLogger, testLock, "TestServer_AddEventerToContext", event.EventerConfig{})
require.NoError(t, err)
tests := []struct {
name string
s Server
ctx context.Context
wantErrMatch *errors.Template
wantErrContains string
}{
{
name: "missing-eventer",
s: Server{},
ctx: context.Background(),
wantErrMatch: errors.T(errors.InvalidParameter),
wantErrContains: "missing server eventer",
},
{
name: "valid",
s: Server{Eventer: testEventer},
ctx: context.Background(),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
gotCtx, err := tt.s.AddEventerToContext(tt.ctx)
if tt.wantErrMatch != nil {
require.Error(err)
assert.Nil(gotCtx)
assert.Truef(errors.Match(tt.wantErrMatch, err), "want %q and got %q", tt.wantErrMatch.Code, err.Error())
if tt.wantErrContains != "" {
assert.Contains(err.Error(), tt.wantErrContains)
}
return
}
require.NoError(err)
e, ok := event.EventerFromContext(gotCtx)
require.Truef(ok, "unable to get eventer from context")
assert.NotNil(e)
assert.Equal(tt.s.Eventer, e)
})
}
}
func TestSetupWorkerPublicAddress(t *testing.T) {
tests := []struct {
name string
inputConfig *config.Config
inputFlagValue string
stateFn func(t *testing.T)
expErr bool
expErrStr string
expPublicAddress string
}{
{
name: "nil worker",
inputConfig: &config.Config{
SharedConfig: &configutil.SharedConfig{
Listeners: []*listenerutil.ListenerConfig{},
},
Worker: nil,
},
inputFlagValue: "",
expErr: false,
expErrStr: "",
expPublicAddress: ":9202",
},
{
name: "setting public address directly with ipv4",
inputConfig: &config.Config{
SharedConfig: &configutil.SharedConfig{
Listeners: []*listenerutil.ListenerConfig{},
},
Worker: &config.Worker{
PublicAddr: "127.0.0.1",
},
},
inputFlagValue: "",
expErr: false,
expErrStr: "",
expPublicAddress: "127.0.0.1:9202",
},
{
name: "setting public address directly with ipv4:port",
inputConfig: &config.Config{
SharedConfig: &configutil.SharedConfig{
Listeners: []*listenerutil.ListenerConfig{},
},
Worker: &config.Worker{
PublicAddr: "127.0.0.1:8080",
},
},
inputFlagValue: "",
expErr: false,
expErrStr: "",
expPublicAddress: "127.0.0.1:8080",
},
{
name: "setting public address directly with invalid ipv6",
inputConfig: &config.Config{
SharedConfig: &configutil.SharedConfig{
Listeners: []*listenerutil.ListenerConfig{},
},
Worker: &config.Worker{
PublicAddr: "[2001:4860:4860:0:0:0:8888]",
},
},
inputFlagValue: "",
expErr: true,
expErrStr: "Error normalizing worker address",
},
{
name: "setting public address directly with ipv6 but no brackets",
inputConfig: &config.Config{
SharedConfig: &configutil.SharedConfig{
Listeners: []*listenerutil.ListenerConfig{},
},
Worker: &config.Worker{
PublicAddr: "2001:4860:4860:0:0:0:0:8888",
},
},
inputFlagValue: "",
expErr: false,
expErrStr: "",
expPublicAddress: "[2001:4860:4860::8888]:9202",
},
{
name: "setting public address directly with ipv6",
inputConfig: &config.Config{
SharedConfig: &configutil.SharedConfig{
Listeners: []*listenerutil.ListenerConfig{},
},
Worker: &config.Worker{
PublicAddr: "2001:4860:4860:0:0:0:0:8888",
},
},
inputFlagValue: "",
expErr: false,
expErrStr: "",
expPublicAddress: "[2001:4860:4860::8888]:9202",
},
{
name: "setting public address directly with ipv6:port",
inputConfig: &config.Config{
SharedConfig: &configutil.SharedConfig{
Listeners: []*listenerutil.ListenerConfig{},
},
Worker: &config.Worker{
PublicAddr: "[2001:4860:4860:0:0:0:0:8888]:8080",
},
},
inputFlagValue: "",
expErr: false,
expErrStr: "",
expPublicAddress: "[2001:4860:4860::8888]:8080",
},
{
name: "setting public address directly with abbreviated ipv6",
inputConfig: &config.Config{
SharedConfig: &configutil.SharedConfig{
Listeners: []*listenerutil.ListenerConfig{},
},
Worker: &config.Worker{
PublicAddr: "2001:4860:4860::8888",
},
},
inputFlagValue: "",
expErr: false,
expErrStr: "",
expPublicAddress: "[2001:4860:4860::8888]:9202",
},
{
name: "setting public address directly with abbreviated ipv6:port",
inputConfig: &config.Config{
SharedConfig: &configutil.SharedConfig{
Listeners: []*listenerutil.ListenerConfig{},
},
Worker: &config.Worker{
PublicAddr: "[2001:4860:4860::8888]:8080",
},
},
inputFlagValue: "",
expErr: false,
expErrStr: "",
expPublicAddress: "[2001:4860:4860::8888]:8080",
},
{
name: "setting public address to env var",
inputConfig: &config.Config{
SharedConfig: &configutil.SharedConfig{
Listeners: []*listenerutil.ListenerConfig{},
},
Worker: &config.Worker{
PublicAddr: "env://TEST_ENV_VAR_FOR_WORKER_ADDR",
},
},
inputFlagValue: "",
stateFn: func(t *testing.T) {
t.Setenv("TEST_ENV_VAR_FOR_WORKER_ADDR", "127.0.0.1:8080")
},
expErr: false,
expErrStr: "",
expPublicAddress: "127.0.0.1:8080",
},
{
name: "setting public address to env var that points to template",
inputConfig: &config.Config{
SharedConfig: &configutil.SharedConfig{
Listeners: []*listenerutil.ListenerConfig{},
},
Worker: &config.Worker{
PublicAddr: "env://TEST_ENV_VAR_FOR_WORKER_ADDR",
},
},
inputFlagValue: "",
stateFn: func(t *testing.T) {
t.Setenv("TEST_ENV_VAR_FOR_WORKER_ADDR", `{{ GetAllInterfaces | include "flags" "loopback" | include "type" "IPV4" | join "address" " " }}`)
},
expErr: false,
expErrStr: "",
expPublicAddress: "127.0.0.1:9202",
},
{
name: "setting public address to ip template",
inputConfig: &config.Config{
SharedConfig: &configutil.SharedConfig{
Listeners: []*listenerutil.ListenerConfig{},
},
Worker: &config.Worker{
PublicAddr: `{{ GetAllInterfaces | include "flags" "loopback" | include "type" "IPV4" | join "address" " " }}`,
},
},
inputFlagValue: "",
expErr: false,
expErrStr: "",
expPublicAddress: "127.0.0.1:9202",
},
{
name: "setting public address to multiline ip template",
inputConfig: &config.Config{
SharedConfig: &configutil.SharedConfig{
Listeners: []*listenerutil.ListenerConfig{},
},
Worker: &config.Worker{
PublicAddr: `{{ with $local := GetAllInterfaces | include "flags" "loopback" | include "type" "IPV4" -}}
{{- $local | join "address" " " -}}
{{- end }}`,
},
},
inputFlagValue: "",
expErr: false,
expErrStr: "",
expPublicAddress: "127.0.0.1:9202",
},
{
name: "using flag value with ip only",
inputConfig: &config.Config{
SharedConfig: &configutil.SharedConfig{
Listeners: []*listenerutil.ListenerConfig{},
},
Worker: &config.Worker{},
},
inputFlagValue: "127.0.0.1",
expErr: false,
expErrStr: "",
expPublicAddress: "127.0.0.1:9202",
},
{
name: "using flag value with ip:port",
inputConfig: &config.Config{
SharedConfig: &configutil.SharedConfig{
Listeners: []*listenerutil.ListenerConfig{},
},
Worker: &config.Worker{},
},
inputFlagValue: "127.0.0.1:8080",
expErr: false,
expErrStr: "",
expPublicAddress: "127.0.0.1:8080",
},
{
name: "using flag value to point to env var with ip only",
inputConfig: &config.Config{
SharedConfig: &configutil.SharedConfig{
Listeners: []*listenerutil.ListenerConfig{},
},
Worker: &config.Worker{},
},
inputFlagValue: "env://TEST_ENV_VAR_FOR_WORKER_ADDR",
stateFn: func(t *testing.T) {
t.Setenv("TEST_ENV_VAR_FOR_WORKER_ADDR", "127.0.0.1")
},
expErr: false,
expErrStr: "",
expPublicAddress: "127.0.0.1:9202",
},
{
name: "using flag value to point to env var with ip:port",
inputConfig: &config.Config{
SharedConfig: &configutil.SharedConfig{
Listeners: []*listenerutil.ListenerConfig{},
},
Worker: &config.Worker{},
},
inputFlagValue: "env://TEST_ENV_VAR_FOR_WORKER_ADDR",
stateFn: func(t *testing.T) {
t.Setenv("TEST_ENV_VAR_FOR_WORKER_ADDR", "127.0.0.1:8080")
},
expErr: false,
expErrStr: "",
expPublicAddress: "127.0.0.1:8080",
},
{
name: "using flag value with ip template",
inputConfig: &config.Config{
SharedConfig: &configutil.SharedConfig{
Listeners: []*listenerutil.ListenerConfig{},
},
Worker: &config.Worker{},
},
inputFlagValue: `{{ GetAllInterfaces | include "flags" "loopback" | include "type" "IPV4" | join "address" " " }}`,
expErr: false,
expErrStr: "",
expPublicAddress: "127.0.0.1:9202",
},
{
name: "using flag value with multiline ip template",
inputConfig: &config.Config{
SharedConfig: &configutil.SharedConfig{
Listeners: []*listenerutil.ListenerConfig{},
},
Worker: &config.Worker{},
},
inputFlagValue: `{{ with $local := GetAllInterfaces | include "flags" "loopback" | include "type" "IPV4" -}}
{{- $local | join "address" " " -}}
{{- end }}`,
expErr: false,
expErrStr: "",
expPublicAddress: "127.0.0.1:9202",
},
{
name: "read address from listeners ip only",
inputConfig: &config.Config{
SharedConfig: &configutil.SharedConfig{
Listeners: []*listenerutil.ListenerConfig{
{Purpose: []string{"proxy"}, Address: "127.0.0.1"},
},
},
Worker: &config.Worker{},
},
expErr: false,
expErrStr: "",
expPublicAddress: "127.0.0.1:9202",
},
{
name: "read address from listeners ip:port",
inputConfig: &config.Config{
SharedConfig: &configutil.SharedConfig{
Listeners: []*listenerutil.ListenerConfig{
{Purpose: []string{"proxy"}, Address: "127.0.0.1:8080"},
},
},
Worker: &config.Worker{},
},
expErr: false,
expErrStr: "",
expPublicAddress: "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"},
},
},
Worker: &config.Worker{},
},
expErr: false,
expErrStr: "",
expPublicAddress: ":9202",
},
{
name: "read unix address from listeners ip only",
inputConfig: &config.Config{
SharedConfig: &configutil.SharedConfig{
Listeners: []*listenerutil.ListenerConfig{
{Purpose: []string{"proxy"}, Address: "someaddr", Type: "unix"},
},
},
Worker: &config.Worker{},
},
expErr: false,
expErrStr: "",
expPublicAddress: "someaddr:9202",
},
{
name: "using flag value to point to nonexistent file",
inputConfig: &config.Config{
SharedConfig: &configutil.SharedConfig{
Listeners: []*listenerutil.ListenerConfig{},
},
Worker: &config.Worker{},
},
inputFlagValue: "file://this_doesnt_exist_for_sure",
expErr: true,
expErrStr: "Error parsing public addr: error reading file at file://this_doesnt_exist_for_sure: open this_doesnt_exist_for_sure: no such file or directory",
expPublicAddress: "",
},
{
name: "using flag value to provoke error in SplitHostPort",
inputConfig: &config.Config{
SharedConfig: &configutil.SharedConfig{
Listeners: []*listenerutil.ListenerConfig{},
},
Worker: &config.Worker{},
},
inputFlagValue: "abc::123:::",
expErr: true,
expErrStr: "Error splitting public adddress host/port: too many colons in address",
expPublicAddress: "",
},
{
name: "bad ip template",
inputConfig: &config.Config{
SharedConfig: &configutil.SharedConfig{
Listeners: []*listenerutil.ListenerConfig{},
},
Worker: &config.Worker{},
},
inputFlagValue: "{{ somethingthatdoesntexist }}",
expErr: true,
expErrStr: "Error parsing IP template on worker public addr: unable to parse address template \"{{ somethingthatdoesntexist }}\": unable to parse template \"{{ somethingthatdoesntexist }}\": template: sockaddr.Parse:1: function \"somethingthatdoesntexist\" not defined",
expPublicAddress: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.stateFn != nil {
tt.stateFn(t)
}
s := Server{}
err := s.SetupWorkerPublicAddress(tt.inputConfig, tt.inputFlagValue)
if tt.expErr {
require.EqualError(t, err, tt.expErrStr)
return
}
require.NoError(t, err)
require.NotNil(t, tt.inputConfig.Worker)
require.Equal(t, tt.expPublicAddress, tt.inputConfig.Worker.PublicAddr)
})
}
}