Skip to content

Commit

Permalink
Add Nomad Service Discovery Support
Browse files Browse the repository at this point in the history
Adds 2 functions for retrieving services from Nomad:

- nomadServices - retrieves all services from Nomad
- nomadService - retrieves all registrations for a service

Tag filtering for `nomadService` is currently implemented in
consul-template as the Nomad API does not support it directly.

Tests use a Nomad binary on path and require Nomad 1.3.0-beta or a
binary built from the PR: hashicorp/nomad#12458

Tests also require Docker.
  • Loading branch information
schmichael committed Apr 12, 2022
1 parent 0012f40 commit 5a20fdd
Show file tree
Hide file tree
Showing 19 changed files with 1,764 additions and 52 deletions.
5 changes: 5 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ jobs:
GO111MODULE: "on"
CONSUL_VERSION: 1.10.7
VAULT_VERSION: 1.8.8
NOMAD_VERSION: "1.3.0-beta.1"
docker:
- image: docker.mirror.hashicorp.services/cimg/go:1.17
steps:
Expand All @@ -20,6 +21,10 @@ jobs:
curl -sLo vault.zip https://releases.hashicorp.com/vault/${VAULT_VERSION}/vault_${VAULT_VERSION}_linux_amd64.zip
unzip vault.zip
sudo cp vault /usr/local/bin/
- run: |
curl -sLo nomad.zip https://releases.hashicorp.com/nomad/${NOMAD_VERSION}/nomad_${NOMAD_VERSION}_linux_amd64.zip
unzip nomad.zip
sudo cp nomad /usr/local/bin/
- run: |
make test
- save_cache:
Expand Down
20 changes: 20 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ type Config struct {
// Vault is the configuration for connecting to a vault server.
Vault *VaultConfig `mapstructure:"vault"`

// Nomad is the configuration for connecting to a Nomad agent.
Nomad *NomadConfig `mapstructure:"nomad"`

// Wait is the quiescence timers.
Wait *WaitConfig `mapstructure:"wait"`

Expand Down Expand Up @@ -171,6 +174,10 @@ func (c *Config) Copy() *Config {

o.BlockQueryWaitTime = c.BlockQueryWaitTime

if c.Nomad != nil {
o.Nomad = o.Nomad.Copy()
}

return &o
}

Expand Down Expand Up @@ -257,6 +264,10 @@ func (c *Config) Merge(o *Config) *Config {
r.Once = o.Once
r.ParseOnly = o.ParseOnly

if o.Nomad != nil {
r.Nomad = r.Nomad.Merge(o.Nomad)
}

return r
}

Expand Down Expand Up @@ -286,6 +297,9 @@ func Parse(s string) (*Config, error) {
"exec",
"exec.env",
"log_file",
"nomad",
"nomad.ssl",
"nomad.transport",
"ssl",
"syslog",
"vault",
Expand Down Expand Up @@ -509,6 +523,7 @@ func DefaultConfig() *Config {
DefaultDelims: DefaultDefaultDelims(),
Exec: DefaultExecConfig(),
FileLog: DefaultLogFileConfig(),
Nomad: DefaultNomadConfig(),
Syslog: DefaultSyslogConfig(),
Templates: DefaultTemplateConfigs(),
Vault: DefaultVaultConfig(),
Expand Down Expand Up @@ -573,6 +588,11 @@ func (c *Config) Finalize() {
}
c.FileLog.Finalize()

if c.Nomad == nil {
c.Nomad = DefaultNomadConfig()
}
c.Nomad.Finalize()

if c.Syslog == nil {
c.Syslog = DefaultSyslogConfig()
}
Expand Down
68 changes: 68 additions & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1695,6 +1695,74 @@ func TestParse(t *testing.T) {
nil,
true,
},
{
"nomad",
`nomad {}`,
&Config{
Nomad: &NomadConfig{},
},
false,
},
{
"nomad_address",
`nomad {
address = "http://10.10.10.2:4646"
}`,
&Config{
Nomad: &NomadConfig{
Address: String("http://10.10.10.2:4646"),
},
},
false,
},
{
"nomad_namespace",
`nomad {
namespace = "platform"
}`,
&Config{
Nomad: &NomadConfig{
Namespace: String("platform"),
},
},
false,
},
{
"nomad_token",
`nomad {
token = "sssssshhhhhh"
}`,
&Config{
Nomad: &NomadConfig{
Token: String("sssssshhhhhh"),
},
},
false,
},
{
"nomad_auth_username",
`nomad {
auth_username = "admin"
}`,
&Config{
Nomad: &NomadConfig{
AuthUsername: String("admin"),
},
},
false,
},
{
"nomad_auth_password",
`nomad {
auth_password = "admin"
}`,
&Config{
Nomad: &NomadConfig{
AuthPassword: String("admin"),
},
},
false,
},
}

for i, tc := range cases {
Expand Down
72 changes: 29 additions & 43 deletions config/mapstructure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,37 +12,35 @@ import (

func TestStringToFileModeFunc(t *testing.T) {

f := StringToFileModeFunc()
strType := reflect.TypeOf("")
fmType := reflect.TypeOf(os.FileMode(0))
u32Type := reflect.TypeOf(uint32(0))
hookFunc := StringToFileModeFunc()
fileModeVal := reflect.ValueOf(os.FileMode(0))

cases := []struct {
f, t reflect.Type
data interface{}
name string
f, t reflect.Value
expected interface{}
err bool
}{
{strType, fmType, "0600", os.FileMode(0600), false},
{strType, fmType, "4600", os.FileMode(04600), false},
{"owner_only", reflect.ValueOf("0600"), fileModeVal, os.FileMode(0600), false},
{"high_bits", reflect.ValueOf("4600"), fileModeVal, os.FileMode(04600), false},

// Prepends 0 automatically
{strType, fmType, "600", os.FileMode(0600), false},
{"add_zero", reflect.ValueOf("600"), fileModeVal, os.FileMode(0600), false},

// Invalid file mode
{strType, fmType, "12345", "12345", true},
{"bad_mode", reflect.ValueOf("12345"), fileModeVal, "12345", true},

// Invalid syntax
{strType, fmType, "abcd", "abcd", true},
{"bad_syntax", reflect.ValueOf("abcd"), fileModeVal, "abcd", true},

// Different type
{strType, strType, "0600", "0600", false},
{strType, u32Type, "0600", "0600", false},
{"two_strs", reflect.ValueOf("0600"), reflect.ValueOf(""), "0600", false},
{"uint32", reflect.ValueOf("0600"), reflect.ValueOf(uint32(0)), "0600", false},
}

for i, tc := range cases {
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
actual, err := mapstructure.DecodeHookExec(f, tc.f, tc.t, tc.data)
t.Run(fmt.Sprintf("%d_%s", i, tc.name), func(t *testing.T) {
actual, err := mapstructure.DecodeHookExec(hookFunc, tc.f, tc.t)
if (err != nil) != tc.err {
t.Fatalf("%s", err)
}
Expand All @@ -56,20 +54,17 @@ func TestStringToFileModeFunc(t *testing.T) {
func TestStringToWaitDurationHookFunc(t *testing.T) {

f := StringToWaitDurationHookFunc()
strType := reflect.TypeOf("")
waitType := reflect.TypeOf(WaitConfig{})
waitVal := reflect.ValueOf(WaitConfig{})

cases := []struct {
name string
f, t reflect.Type
data interface{}
f, t reflect.Value
expected interface{}
err bool
}{
{
"min",
strType, waitType,
"5s",
reflect.ValueOf("5s"), waitVal,
&WaitConfig{
Min: TimeDuration(5 * time.Second),
Max: TimeDuration(20 * time.Second),
Expand All @@ -78,8 +73,7 @@ func TestStringToWaitDurationHookFunc(t *testing.T) {
},
{
"min_max",
strType, waitType,
"5s:10s",
reflect.ValueOf("5s:10s"), waitVal,
&WaitConfig{
Min: TimeDuration(5 * time.Second),
Max: TimeDuration(10 * time.Second),
Expand All @@ -88,30 +82,27 @@ func TestStringToWaitDurationHookFunc(t *testing.T) {
},
{
"not_string",
waitType, waitType,
&WaitConfig{},
&WaitConfig{},
waitVal, waitVal,
WaitConfig{},
false,
},
{
"not_wait",
strType, strType,
"test",
reflect.ValueOf("test"), reflect.ValueOf(""),
"test",
false,
},
{
"bad_wait",
strType, waitType,
"nope",
reflect.ValueOf("nope"), waitVal,
(*WaitConfig)(nil),
true,
},
}

for i, tc := range cases {
t.Run(fmt.Sprintf("%d_%s", i, tc.name), func(t *testing.T) {
actual, err := mapstructure.DecodeHookExec(f, tc.f, tc.t, tc.data)
actual, err := mapstructure.DecodeHookExec(f, tc.f, tc.t)
if (err != nil) != tc.err {
t.Fatalf("%s", err)
}
Expand All @@ -125,44 +116,39 @@ func TestStringToWaitDurationHookFunc(t *testing.T) {
func TestConsulStringToStructFunc(t *testing.T) {

f := ConsulStringToStructFunc()
strType := reflect.TypeOf("")
consulType := reflect.TypeOf(ConsulConfig{})
consulVal := reflect.ValueOf(ConsulConfig{})

cases := []struct {
name string
f, t reflect.Type
data interface{}
f, t reflect.Value
expected interface{}
err bool
}{
{
"address",
strType, consulType,
"1.2.3.4",
reflect.ValueOf("1.2.3.4"), consulVal,
&ConsulConfig{
Address: String("1.2.3.4"),
},
false,
},
{
"not_string",
consulType, consulType,
&ConsulConfig{},
&ConsulConfig{},
consulVal, consulVal,
ConsulConfig{},
false,
},
{
"not_consul",
strType, strType,
"test",
reflect.ValueOf("test"), reflect.ValueOf(""),
"test",
false,
},
}

for i, tc := range cases {
t.Run(fmt.Sprintf("%d_%s", i, tc.name), func(t *testing.T) {
actual, err := mapstructure.DecodeHookExec(f, tc.f, tc.t, tc.data)
actual, err := mapstructure.DecodeHookExec(f, tc.f, tc.t)
if (err != nil) != tc.err {
t.Fatalf("%s", err)
}
Expand Down
Loading

0 comments on commit 5a20fdd

Please sign in to comment.