| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 | 
							- package config
 
- import (
 
- 	"os"
 
- 	"path/filepath"
 
- 	"testing"
 
- 	"time"
 
- 	"dario.cat/mergo"
 
- 	"github.com/slackhq/nebula/test"
 
- 	"github.com/stretchr/testify/assert"
 
- 	"github.com/stretchr/testify/require"
 
- 	"gopkg.in/yaml.v2"
 
- )
 
- func TestConfig_Load(t *testing.T) {
 
- 	l := test.NewLogger()
 
- 	dir, err := os.MkdirTemp("", "config-test")
 
- 	// invalid yaml
 
- 	c := NewC(l)
 
- 	os.WriteFile(filepath.Join(dir, "01.yaml"), []byte(" invalid yaml"), 0644)
 
- 	assert.EqualError(t, c.Load(dir), "yaml: unmarshal errors:\n  line 1: cannot unmarshal !!str `invalid...` into map[interface {}]interface {}")
 
- 	// simple multi config merge
 
- 	c = NewC(l)
 
- 	os.RemoveAll(dir)
 
- 	os.Mkdir(dir, 0755)
 
- 	assert.Nil(t, err)
 
- 	os.WriteFile(filepath.Join(dir, "01.yaml"), []byte("outer:\n  inner: hi"), 0644)
 
- 	os.WriteFile(filepath.Join(dir, "02.yml"), []byte("outer:\n  inner: override\nnew: hi"), 0644)
 
- 	assert.Nil(t, c.Load(dir))
 
- 	expected := map[interface{}]interface{}{
 
- 		"outer": map[interface{}]interface{}{
 
- 			"inner": "override",
 
- 		},
 
- 		"new": "hi",
 
- 	}
 
- 	assert.Equal(t, expected, c.Settings)
 
- 	//TODO: test symlinked file
 
- 	//TODO: test symlinked directory
 
- }
 
- func TestConfig_Get(t *testing.T) {
 
- 	l := test.NewLogger()
 
- 	// test simple type
 
- 	c := NewC(l)
 
- 	c.Settings["firewall"] = map[interface{}]interface{}{"outbound": "hi"}
 
- 	assert.Equal(t, "hi", c.Get("firewall.outbound"))
 
- 	// test complex type
 
- 	inner := []map[interface{}]interface{}{{"port": "1", "code": "2"}}
 
- 	c.Settings["firewall"] = map[interface{}]interface{}{"outbound": inner}
 
- 	assert.EqualValues(t, inner, c.Get("firewall.outbound"))
 
- 	// test missing
 
- 	assert.Nil(t, c.Get("firewall.nope"))
 
- }
 
- func TestConfig_GetStringSlice(t *testing.T) {
 
- 	l := test.NewLogger()
 
- 	c := NewC(l)
 
- 	c.Settings["slice"] = []interface{}{"one", "two"}
 
- 	assert.Equal(t, []string{"one", "two"}, c.GetStringSlice("slice", []string{}))
 
- }
 
- func TestConfig_GetBool(t *testing.T) {
 
- 	l := test.NewLogger()
 
- 	c := NewC(l)
 
- 	c.Settings["bool"] = true
 
- 	assert.Equal(t, true, c.GetBool("bool", false))
 
- 	c.Settings["bool"] = "true"
 
- 	assert.Equal(t, true, c.GetBool("bool", false))
 
- 	c.Settings["bool"] = false
 
- 	assert.Equal(t, false, c.GetBool("bool", true))
 
- 	c.Settings["bool"] = "false"
 
- 	assert.Equal(t, false, c.GetBool("bool", true))
 
- 	c.Settings["bool"] = "Y"
 
- 	assert.Equal(t, true, c.GetBool("bool", false))
 
- 	c.Settings["bool"] = "yEs"
 
- 	assert.Equal(t, true, c.GetBool("bool", false))
 
- 	c.Settings["bool"] = "N"
 
- 	assert.Equal(t, false, c.GetBool("bool", true))
 
- 	c.Settings["bool"] = "nO"
 
- 	assert.Equal(t, false, c.GetBool("bool", true))
 
- }
 
- func TestConfig_HasChanged(t *testing.T) {
 
- 	l := test.NewLogger()
 
- 	// No reload has occurred, return false
 
- 	c := NewC(l)
 
- 	c.Settings["test"] = "hi"
 
- 	assert.False(t, c.HasChanged(""))
 
- 	// Test key change
 
- 	c = NewC(l)
 
- 	c.Settings["test"] = "hi"
 
- 	c.oldSettings = map[interface{}]interface{}{"test": "no"}
 
- 	assert.True(t, c.HasChanged("test"))
 
- 	assert.True(t, c.HasChanged(""))
 
- 	// No key change
 
- 	c = NewC(l)
 
- 	c.Settings["test"] = "hi"
 
- 	c.oldSettings = map[interface{}]interface{}{"test": "hi"}
 
- 	assert.False(t, c.HasChanged("test"))
 
- 	assert.False(t, c.HasChanged(""))
 
- }
 
- func TestConfig_ReloadConfig(t *testing.T) {
 
- 	l := test.NewLogger()
 
- 	done := make(chan bool, 1)
 
- 	dir, err := os.MkdirTemp("", "config-test")
 
- 	assert.Nil(t, err)
 
- 	os.WriteFile(filepath.Join(dir, "01.yaml"), []byte("outer:\n  inner: hi"), 0644)
 
- 	c := NewC(l)
 
- 	assert.Nil(t, c.Load(dir))
 
- 	assert.False(t, c.HasChanged("outer.inner"))
 
- 	assert.False(t, c.HasChanged("outer"))
 
- 	assert.False(t, c.HasChanged(""))
 
- 	os.WriteFile(filepath.Join(dir, "01.yaml"), []byte("outer:\n  inner: ho"), 0644)
 
- 	c.RegisterReloadCallback(func(c *C) {
 
- 		done <- true
 
- 	})
 
- 	c.ReloadConfig()
 
- 	assert.True(t, c.HasChanged("outer.inner"))
 
- 	assert.True(t, c.HasChanged("outer"))
 
- 	assert.True(t, c.HasChanged(""))
 
- 	// Make sure we call the callbacks
 
- 	select {
 
- 	case <-done:
 
- 	case <-time.After(1 * time.Second):
 
- 		panic("timeout")
 
- 	}
 
- }
 
- // Ensure mergo merges are done the way we expect.
 
- // This is needed to test for potential regressions, like:
 
- // - https://github.com/imdario/mergo/issues/187
 
- func TestConfig_MergoMerge(t *testing.T) {
 
- 	configs := [][]byte{
 
- 		[]byte(`
 
- listen:
 
-   port: 1234
 
- `),
 
- 		[]byte(`
 
- firewall:
 
-   inbound:
 
-     - port: 443
 
-       proto: tcp
 
-       groups:
 
-         - server
 
-     - port: 443
 
-       proto: tcp
 
-       groups:
 
-         - webapp
 
- `),
 
- 		[]byte(`
 
- listen:
 
-   host: 0.0.0.0
 
-   port: 4242
 
- firewall:
 
-   outbound:
 
-     - port: any
 
-       proto: any
 
-       host: any
 
-   inbound:
 
-     - port: any
 
-       proto: icmp
 
-       host: any
 
- `),
 
- 	}
 
- 	var m map[any]any
 
- 	// merge the same way config.parse() merges
 
- 	for _, b := range configs {
 
- 		var nm map[any]any
 
- 		err := yaml.Unmarshal(b, &nm)
 
- 		require.NoError(t, err)
 
- 		// We need to use WithAppendSlice so that firewall rules in separate
 
- 		// files are appended together
 
- 		err = mergo.Merge(&nm, m, mergo.WithAppendSlice)
 
- 		m = nm
 
- 		require.NoError(t, err)
 
- 	}
 
- 	t.Logf("Merged Config: %#v", m)
 
- 	mYaml, err := yaml.Marshal(m)
 
- 	require.NoError(t, err)
 
- 	t.Logf("Merged Config as YAML:\n%s", mYaml)
 
- 	// If a bug is present, some items might be replaced instead of merged like we expect
 
- 	expected := map[any]any{
 
- 		"firewall": map[any]any{
 
- 			"inbound": []any{
 
- 				map[any]any{"host": "any", "port": "any", "proto": "icmp"},
 
- 				map[any]any{"groups": []any{"server"}, "port": 443, "proto": "tcp"},
 
- 				map[any]any{"groups": []any{"webapp"}, "port": 443, "proto": "tcp"}},
 
- 			"outbound": []any{
 
- 				map[any]any{"host": "any", "port": "any", "proto": "any"}}},
 
- 		"listen": map[any]any{
 
- 			"host": "0.0.0.0",
 
- 			"port": 4242,
 
- 		},
 
- 	}
 
- 	assert.Equal(t, expected, m)
 
- }
 
 
  |