Merge pull request #1765 from hashicorp/hugoamvieira-env-file-workercontrollers
feat(config): Add env and file support to Worker Controllers
This commit is contained in:
5 files changed
+210
-16
No files matched your search
+4
-1
@@ -5,13 +5,16 @@ Canonical reference for changes, improvements, and bugfixes for Boundary.
|
||||
## Next
|
||||
|
||||
### New and Improved
|
||||
|
||||
* config: The `execution_dir` field for plugins now supports being set from environment variables
|
||||
or a file on disk.([PR](https://github.com/hashicorp/boundary/pull/1772))
|
||||
* config: Add support for reading worker controllers off of environment
|
||||
variables as well as files. ([PR](https://github.com/hashicorp/boundary/pull/1765))
|
||||
* config: The `description` field for controllers now supports being set
|
||||
from environment variables or a file on disk
|
||||
([PR](https://github.com/hashicorp/boundary/pull/1766))
|
||||
* config: Add support for reading worker tags off of environment variables
|
||||
as well as files. ([PR](https://github.com/hashicorp/boundary/pull/1758))
|
||||
as well as files. ([PR](https://github.com/hashicorp/boundary/pull/1758))
|
||||
* config: Add support for go-sockaddr templates to Worker and Controller
|
||||
addresses. ([PR](https://github.com/hashicorp/boundary/pull/1731))
|
||||
* host: Plugin-based host catalogs will now schedule updates for all
|
||||
|
||||
@@ -270,14 +270,12 @@ func (c *Command) Run(args []string) int {
|
||||
return base.CommandUserError
|
||||
}
|
||||
|
||||
if c.Config.Worker != nil {
|
||||
if err := c.SetupWorkerPublicAddress(c.Config, ""); err != nil {
|
||||
c.UI.Error(err.Error())
|
||||
return base.CommandUserError
|
||||
}
|
||||
c.InfoKeys = append(c.InfoKeys, "worker public proxy addr")
|
||||
c.Info["worker public proxy addr"] = c.Config.Worker.PublicAddr
|
||||
if err := c.SetupWorkerPublicAddress(c.Config, ""); err != nil {
|
||||
c.UI.Error(err.Error())
|
||||
return base.CommandUserError
|
||||
}
|
||||
c.InfoKeys = append(c.InfoKeys, "worker public proxy addr")
|
||||
c.Info["worker public proxy addr"] = c.Config.Worker.PublicAddr
|
||||
|
||||
if c.Config.Controller != nil {
|
||||
switch len(c.Config.Worker.Controllers) {
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -151,10 +152,15 @@ func (c *Controller) InitNameIfEmpty() (string, error) {
|
||||
}
|
||||
|
||||
type Worker struct {
|
||||
Name string `hcl:"name"`
|
||||
Description string `hcl:"description"`
|
||||
Controllers []string `hcl:"controllers"`
|
||||
PublicAddr string `hcl:"public_addr"`
|
||||
Name string `hcl:"name"`
|
||||
Description string `hcl:"description"`
|
||||
PublicAddr string `hcl:"public_addr"`
|
||||
|
||||
// We use a raw interface here so that we can take in a string
|
||||
// value pointing to an env var or file. We then resolve that
|
||||
// and get the actual controller addresses.
|
||||
Controllers []string `hcl:"-"`
|
||||
ControllersRaw interface{} `hcl:"controllers"`
|
||||
|
||||
// We use a raw interface for parsing so that people can use JSON-like
|
||||
// syntax that maps directly to the filter input or possibly more familiar
|
||||
@@ -448,6 +454,11 @@ func Parse(d string) (*Config, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.Worker.Controllers, err = parseWorkerControllers(result)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to parse worker controllers: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
sharedConfig, err := configutil.ParseConfig(d)
|
||||
@@ -506,6 +517,42 @@ func Parse(d string) (*Config, error) {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func parseWorkerControllers(c *Config) ([]string, error) {
|
||||
if c == nil || c.Worker == nil {
|
||||
return nil, fmt.Errorf("config or worker field is nil")
|
||||
}
|
||||
if c.Worker.ControllersRaw == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
switch t := c.Worker.ControllersRaw.(type) {
|
||||
case []interface{}: // An array was configured directly in Boundary's HCL Config file.
|
||||
var controllers []string
|
||||
err := mapstructure.WeakDecode(c.Worker.ControllersRaw, &controllers)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode worker controllers block into config field: %w", err)
|
||||
}
|
||||
return controllers, nil
|
||||
|
||||
case string:
|
||||
controllersStr, err := parseutil.ParsePath(t)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("bad env var or file pointer: %w", err)
|
||||
}
|
||||
|
||||
var addrs []string
|
||||
err = json.Unmarshal([]byte(controllersStr), &addrs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal env/file contents: %w", err)
|
||||
}
|
||||
return addrs, nil
|
||||
|
||||
default:
|
||||
typ := reflect.TypeOf(t)
|
||||
return nil, fmt.Errorf("unexpected type %q", typ.String())
|
||||
}
|
||||
}
|
||||
|
||||
func parseEventing(eventObj *ast.ObjectItem) (*event.EventerConfig, error) {
|
||||
// Decode the outside struct
|
||||
var result event.EventerConfig
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/observability/event"
|
||||
"github.com/hashicorp/go-secure-stdlib/configutil"
|
||||
"github.com/hashicorp/go-secure-stdlib/listenerutil"
|
||||
"github.com/hashicorp/go-secure-stdlib/parseutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -173,9 +174,10 @@ func TestDevWorker(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Worker: &Worker{
|
||||
Name: "dev-worker",
|
||||
Description: "A default worker created in dev mode",
|
||||
Controllers: []string{"127.0.0.1"},
|
||||
Name: "dev-worker",
|
||||
Description: "A default worker created in dev mode",
|
||||
Controllers: []string{"127.0.0.1"},
|
||||
ControllersRaw: []interface{}{"127.0.0.1"},
|
||||
Tags: map[string][]string{
|
||||
"type": {"dev", "local"},
|
||||
},
|
||||
@@ -811,6 +813,146 @@ func TestController_EventingConfig(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWorkerControllers(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
in string
|
||||
stateFn func(t *testing.T)
|
||||
expWorkerControllers []string
|
||||
expErr bool
|
||||
expErrIs error
|
||||
expErrStr string
|
||||
}{
|
||||
{
|
||||
name: "No Controllers",
|
||||
in: `
|
||||
worker {
|
||||
name = "test"
|
||||
}
|
||||
`,
|
||||
expWorkerControllers: nil,
|
||||
expErr: false,
|
||||
},
|
||||
{
|
||||
name: "One Controller",
|
||||
in: `
|
||||
worker {
|
||||
name = "test"
|
||||
controllers = ["127.0.0.1"]
|
||||
}
|
||||
`,
|
||||
expWorkerControllers: []string{"127.0.0.1"},
|
||||
expErr: false,
|
||||
},
|
||||
{
|
||||
name: "Multiple controllers",
|
||||
in: `
|
||||
worker {
|
||||
name = "test"
|
||||
controllers = ["127.0.0.1", "127.0.0.2", "127.0.0.3"]
|
||||
}
|
||||
`,
|
||||
expWorkerControllers: []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"},
|
||||
expErr: false,
|
||||
},
|
||||
{
|
||||
name: "Using env var",
|
||||
in: `
|
||||
worker {
|
||||
name = "test"
|
||||
controllers = "env://BOUNDARY_WORKER_CONTROLLERS"
|
||||
}
|
||||
`,
|
||||
stateFn: func(t *testing.T) { t.Setenv("BOUNDARY_WORKER_CONTROLLERS", `["127.0.0.1", "127.0.0.2", "127.0.0.3"]`) },
|
||||
expWorkerControllers: []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"},
|
||||
expErr: false,
|
||||
},
|
||||
{
|
||||
name: "Using env var - invalid input 1",
|
||||
in: `
|
||||
worker {
|
||||
name = "test"
|
||||
controllers = "env://BOUNDARY_WORKER_CONTROLLERS"
|
||||
}
|
||||
`,
|
||||
stateFn: func(t *testing.T) {
|
||||
controllers := `
|
||||
worker {
|
||||
controllers = ["127.0.0.1"]
|
||||
}
|
||||
`
|
||||
t.Setenv("BOUNDARY_WORKER_CONTROLLERS", controllers)
|
||||
},
|
||||
expWorkerControllers: nil,
|
||||
expErr: true,
|
||||
expErrStr: "Failed to parse worker controllers: failed to unmarshal env/file contents: invalid character 'w' looking for beginning of value",
|
||||
},
|
||||
{
|
||||
name: "Using env var - invalid input 2",
|
||||
in: `
|
||||
worker {
|
||||
name = "test"
|
||||
controllers = "env://BOUNDARY_WORKER_CONTROLLERS"
|
||||
}
|
||||
`,
|
||||
stateFn: func(t *testing.T) { t.Setenv("BOUNDARY_WORKER_CONTROLLERS", `controllers = ["127.0.0.1"]`) },
|
||||
expWorkerControllers: nil,
|
||||
expErr: true,
|
||||
expErrStr: "Failed to parse worker controllers: failed to unmarshal env/file contents: invalid character 'c' looking for beginning of value",
|
||||
},
|
||||
{
|
||||
name: "Unsupported object",
|
||||
in: `
|
||||
worker {
|
||||
name = "test"
|
||||
controllers = {
|
||||
ip = "127.0.0.1"
|
||||
ip = "127.0.0.2"
|
||||
}
|
||||
}
|
||||
`,
|
||||
expWorkerControllers: nil,
|
||||
expErr: true,
|
||||
expErrStr: "Failed to parse worker controllers: unexpected type \"[]map[string]interface {}\"",
|
||||
},
|
||||
{
|
||||
name: "worker controllers set to invalid url",
|
||||
in: `
|
||||
worker {
|
||||
name = "test"
|
||||
controllers = "env://\x00"
|
||||
}`,
|
||||
expWorkerControllers: nil,
|
||||
expErr: true,
|
||||
expErrIs: parseutil.ErrNotAUrl,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.stateFn != nil {
|
||||
tt.stateFn(t)
|
||||
}
|
||||
|
||||
c, err := Parse(tt.in)
|
||||
if tt.expErr {
|
||||
if tt.expErrIs != nil {
|
||||
require.ErrorIs(t, err, tt.expErrIs)
|
||||
} else {
|
||||
require.EqualError(t, err, tt.expErrStr)
|
||||
}
|
||||
require.Nil(t, c)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, c)
|
||||
require.NotNil(t, c.Worker)
|
||||
require.EqualValues(t, tt.expWorkerControllers, c.Worker.Controllers)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestControllerDescription(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
@@ -34,7 +34,11 @@ worker {
|
||||
or a [go-sockaddr template](https://godoc.org/github.com/hashicorp/go-sockaddr/template).
|
||||
|
||||
- `controllers` - A list of hosts/IP addresses and optionally ports for reaching
|
||||
controllers. The port will default to :9201 if not specified.
|
||||
controllers. The port will default to :9201 if not specified. This value can be
|
||||
a direct access string array with the addresses, or it can refer to a file on
|
||||
disk (file://) from which the addresses will be read, or an env var (env://) from
|
||||
which the addresses will be read. When using env or file, their contents
|
||||
must formatted as a JSON array: `["127.0.0.1", "192.168.0.1", "10.0.0.1"]`
|
||||
|
||||
- `tags` - A map of key-value pairs where values are an array of strings. Most
|
||||
commonly used for [filtering](/docs/concepts/filtering) targets a worker can
|
||||
|
||||
Reference in new issue
Block a user