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/config/config_test.go

414 lines
9.4 KiB

package config
import (
"fmt"
"os"
"testing"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/observability/event"
"github.com/hashicorp/go-secure-stdlib/configutil"
"github.com/hashicorp/go-secure-stdlib/listenerutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestDevController(t *testing.T) {
actual, err := DevController()
if err != nil {
t.Fatal(err)
}
truePointer := new(bool)
*truePointer = true
exp := &Config{
Eventing: event.DefaultEventerConfig(),
SharedConfig: &configutil.SharedConfig{
DisableMlock: true,
Listeners: []*listenerutil.ListenerConfig{
{
Type: "tcp",
Purpose: []string{"api"},
TLSDisable: true,
CorsEnabled: truePointer,
CorsAllowedOrigins: []string{"*"},
},
{
Type: "tcp",
Purpose: []string{"cluster"},
},
},
Seals: []*configutil.KMS{
{
Type: "aead",
Purpose: []string{"root"},
Config: map[string]string{
"aead_type": "aes-gcm",
"key_id": "global_root",
},
},
{
Type: "aead",
Purpose: []string{"worker-auth"},
Config: map[string]string{
"aead_type": "aes-gcm",
"key_id": "global_worker-auth",
},
},
{
Type: "aead",
Purpose: []string{"recovery"},
Config: map[string]string{
"aead_type": "aes-gcm",
"key_id": "global_recovery",
},
},
},
},
Controller: &Controller{
Name: "dev-controller",
Description: "A default controller created in dev mode",
},
DevController: true,
}
exp.Listeners[0].RawConfig = actual.Listeners[0].RawConfig
exp.Listeners[1].RawConfig = actual.Listeners[1].RawConfig
exp.Seals[0].Config["key"] = actual.Seals[0].Config["key"]
exp.Seals[1].Config["key"] = actual.Seals[1].Config["key"]
exp.Seals[2].Config["key"] = actual.Seals[2].Config["key"]
exp.DevControllerKey = actual.Seals[0].Config["key"]
exp.DevWorkerAuthKey = actual.Seals[1].Config["key"]
exp.DevRecoveryKey = actual.Seals[2].Config["key"]
assert.Equal(t, exp, actual)
// Do some CORS-specific testing
{
// CORS disabled
conf := `
listener "tcp" {
purpose = "api"
cors_enabled = false
}
`
actual, err = Parse(conf)
assert.NoError(t, err)
l0 := actual.Listeners[0]
assert.False(t, *l0.CorsEnabled)
assert.Empty(t, l0.CorsAllowedHeaders)
// Enabled with a wildcard
conf = `
listener "tcp" {
purpose = "api"
cors_enabled = true
cors_allowed_origins = ["*"]
}
`
actual, err = Parse(conf)
assert.NoError(t, err)
l0 = actual.Listeners[0]
assert.True(t, *l0.CorsEnabled)
assert.Equal(t, []string{"*"}, l0.CorsAllowedOrigins)
assert.Nil(t, l0.CorsDisableDefaultAllowedOriginValues)
// Disabled, default behavior
conf = `
listener "tcp" {
purpose = "api"
}
`
actual, err = Parse(conf)
assert.NoError(t, err)
l0 = actual.Listeners[0]
assert.True(t, *l0.CorsEnabled)
assert.Equal(t, []string{"*"}, l0.CorsAllowedOrigins)
assert.Nil(t, l0.CorsDisableDefaultAllowedOriginValues)
// Disabled, default behavior
conf = `
listener "tcp" {
purpose = "api"
cors_disable_default_allowed_origin_values = true
}
`
actual, err = Parse(conf)
assert.NoError(t, err)
l0 = actual.Listeners[0]
assert.Nil(t, l0.CorsEnabled)
assert.Empty(t, l0.CorsAllowedOrigins)
assert.True(t, *l0.CorsDisableDefaultAllowedOriginValues)
}
}
func TestDevWorker(t *testing.T) {
actual, err := DevWorker()
if err != nil {
t.Fatal(err)
}
exp := &Config{
Eventing: event.DefaultEventerConfig(),
SharedConfig: &configutil.SharedConfig{
DisableMlock: true,
Listeners: []*listenerutil.ListenerConfig{
{
Type: "tcp",
Purpose: []string{"proxy"},
},
},
},
Worker: &Worker{
Name: "dev-worker",
Description: "A default worker created in dev mode",
Controllers: []string{"127.0.0.1"},
Tags: map[string][]string{
"type": {"dev", "local"},
},
},
}
exp.Listeners[0].RawConfig = actual.Listeners[0].RawConfig
exp.Worker.TagsRaw = actual.Worker.TagsRaw
assert.Equal(t, exp, actual)
// Redo it with key=value syntax for tags
devWorkerKeyValueConfig := `
listener "tcp" {
purpose = "proxy"
}
worker {
name = "dev-worker"
description = "A default worker created in dev mode"
controllers = ["127.0.0.1"]
tags = ["type=dev", "type=local"]
}
`
actual, err = Parse(devConfig + devWorkerKeyValueConfig)
assert.NoError(t, err)
exp.Listeners[0].RawConfig = actual.Listeners[0].RawConfig
exp.Worker.TagsRaw = actual.Worker.TagsRaw
assert.Equal(t, exp, actual)
// Handle when there is a singular value not indicated as a slice
devWorkerKeyValueConfig = `
listener "tcp" {
purpose = "proxy"
}
worker {
name = "dev-worker"
description = "A default worker created in dev mode"
controllers = ["127.0.0.1"]
tags {
type = "local"
}
}
`
actual, err = Parse(devConfig + devWorkerKeyValueConfig)
assert.NoError(t, err)
exp.Listeners[0].RawConfig = actual.Listeners[0].RawConfig
exp.Worker.TagsRaw = actual.Worker.TagsRaw
prevTags := exp.Worker.Tags
exp.Worker.Tags = map[string][]string{"type": {"local"}}
assert.Equal(t, exp, actual)
exp.Worker.Tags = prevTags
// Redo it with non-lower-cased keys
devWorkerKeyValueConfig = `
listener "tcp" {
purpose = "proxy"
}
worker {
name = "dev-worker"
description = "A default worker created in dev mode"
controllers = ["127.0.0.1"]
tags = ["tyPe=dev", "type=local"]
}
`
_, err = Parse(devConfig + devWorkerKeyValueConfig)
assert.Error(t, err)
// Redo it with non-lower-cased values
devWorkerKeyValueConfig = `
listener "tcp" {
purpose = "proxy"
}
worker {
name = "dev-worker"
description = "A default worker created in dev mode"
controllers = ["127.0.0.1"]
tags = ["type=dev", "type=loCal"]
}
`
_, err = Parse(devConfig + devWorkerKeyValueConfig)
assert.Error(t, err)
// Redo with non-printable characters to validate the strutil function
devWorkerKeyValueConfig = `
listener "tcp" {
purpose = "proxy"
}
worker {
name = "dev-work\u0000er"
description = "A default worker created in dev mode"
controllers = ["127.0.0.1"]
tags = ["type=dev", "type=local"]
}
`
_, err = Parse(devConfig + devWorkerKeyValueConfig)
assert.Error(t, err)
}
func TestParsingName(t *testing.T) {
t.Parallel()
config := `
controller {
name = "%s"
}
worker {
name = "%s"
}
`
controllerEnv := "FOOENV"
workerEnv := "BARENV"
cases := []struct {
name string
templateController string
templateWorker string
envController string
envWorker string
expectedController string
expectedWorker string
}{
{
name: "no env",
templateController: "foobar",
templateWorker: "barfoo",
expectedController: "foobar",
expectedWorker: "barfoo",
},
{
name: "env",
templateController: fmt.Sprintf("env://%s", controllerEnv),
templateWorker: fmt.Sprintf("env://%s", workerEnv),
envController: "foobar2",
envWorker: "barfoo2",
expectedController: "foobar2",
expectedWorker: "barfoo2",
},
}
for _, tt := range cases {
t.Run(tt.name, func(t *testing.T) {
if tt.envController != "" {
os.Setenv(controllerEnv, tt.envController)
}
if tt.envWorker != "" {
os.Setenv(workerEnv, tt.envWorker)
}
out, err := Parse(fmt.Sprintf(config, tt.templateController, tt.templateWorker))
require.NoError(t, err)
assert.Equal(t, tt.expectedController, out.Controller.Name)
assert.Equal(t, tt.expectedWorker, out.Worker.Name)
})
}
}
func TestController_EventingConfig(t *testing.T) {
t.Parallel()
tests := []struct {
name string
config string
wantEventerConfig *event.EventerConfig
wantErrMatch *errors.Template
}{
{
name: "default",
wantEventerConfig: event.DefaultEventerConfig(),
},
{
name: "audit-enabled",
config: `
events {
audit_enabled = true
}
`,
wantEventerConfig: &event.EventerConfig{
AuditEnabled: true,
ObservationsEnabled: false,
Sinks: []event.SinkConfig{
event.DefaultSink(),
},
},
},
{
name: "observations-enabled",
config: `
events {
observations_enabled = true
}
`,
wantEventerConfig: &event.EventerConfig{
AuditEnabled: false,
ObservationsEnabled: true,
Sinks: []event.SinkConfig{
event.DefaultSink(),
},
},
},
{
name: "sinks-configured",
config: `
events {
audit_enabled = false
observations_enabled = true
sinks = [
{
name = "configured-sink"
file_name = "file-name"
event_types = [ "audit", "observation" ]
}
]
}
`,
wantEventerConfig: &event.EventerConfig{
AuditEnabled: false,
ObservationsEnabled: true,
Sinks: []event.SinkConfig{
{
Name: "configured-sink",
FileName: "file-name",
EventTypes: []event.Type{"audit", "observation"},
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
c, err := Parse(tt.config)
if tt.wantErrMatch != nil {
require.NoError(err)
assert.Empty(c)
assert.Truef(errors.Match(tt.wantErrMatch, err), "want %q and got %q", tt.wantErrMatch.Code, err.Error())
return
}
require.NoError(err)
assert.NotEmpty(c)
assert.Equal(tt.wantEventerConfig, c.Eventing)
})
}
}