diff --git a/Makefile b/Makefile index 7b26ee36..8a65af72 100644 --- a/Makefile +++ b/Makefile @@ -10,10 +10,11 @@ generate-mocks: ## Generate mocks for tests @echo "> generating mocks..." # Monday - mockery -name=ProxyInterface -dir=pkg/proxy/ -output internal/tests/mocks - mockery -name=RunnerInterface -dir=pkg/runner/ -output internal/tests/mocks - mockery -name=ForwarderInterface -dir=pkg/forwarder/ -output internal/tests/mocks - mockery -name=WatcherInterface -dir=pkg/watcher/ -output internal/tests/mocks + mockery -name=HostfileInterface -dir=pkg/hostfile/ -output internal/tests/mocks/hostfile + mockery -name=ProxyInterface -dir=pkg/proxy/ -output internal/tests/mocks/proxy + mockery -name=RunnerInterface -dir=pkg/runner/ -output internal/tests/mocks/runner + mockery -name=ForwarderInterface -dir=pkg/forwarder/ -output internal/tests/mocks/forwarder + mockery -name=WatcherInterface -dir=pkg/watcher/ -output internal/tests/mocks/watcher # Kubernetes AppsV1 mockery -name=Interface -dir=vendor/k8s.io/client-go/kubernetes/ -output internal/tests/mocks/kubernetes/client diff --git a/cmd/main.go b/cmd/main.go index faae3518..e1e68e3b 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -8,6 +8,7 @@ import ( "github.com/eko/monday/internal/config" "github.com/eko/monday/pkg/forwarder" + "github.com/eko/monday/pkg/hostfile" "github.com/eko/monday/pkg/proxy" "github.com/eko/monday/pkg/runner" "github.com/eko/monday/pkg/watcher" @@ -79,8 +80,14 @@ func run(conf *config.Config, choice string) { panic(err) } + // Initializes hosts file manager + hostfile, err := hostfile.NewClient() + if err != nil { + panic(err) + } + // Initializes proxy - proxyComponent = proxy.NewProxy() + proxyComponent = proxy.NewProxy(hostfile) // Initializes runner runnerComponent = runner.NewRunner(proxyComponent, project) diff --git a/internal/tests/mocks/ForwarderInterface.go b/internal/tests/mocks/forwarder/ForwarderInterface.go similarity index 100% rename from internal/tests/mocks/ForwarderInterface.go rename to internal/tests/mocks/forwarder/ForwarderInterface.go diff --git a/internal/tests/mocks/hostfile/HostfileInterface.go b/internal/tests/mocks/hostfile/HostfileInterface.go new file mode 100644 index 00000000..9470a78f --- /dev/null +++ b/internal/tests/mocks/hostfile/HostfileInterface.go @@ -0,0 +1,38 @@ +// Code generated by mockery v1.0.0. DO NOT EDIT. + +package mocks + +import mock "github.com/stretchr/testify/mock" + +// HostfileInterface is an autogenerated mock type for the HostfileInterface type +type HostfileInterface struct { + mock.Mock +} + +// AddHost provides a mock function with given fields: ip, hostname +func (_m *HostfileInterface) AddHost(ip string, hostname string) error { + ret := _m.Called(ip, hostname) + + var r0 error + if rf, ok := ret.Get(0).(func(string, string) error); ok { + r0 = rf(ip, hostname) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// RemoveHost provides a mock function with given fields: hostname +func (_m *HostfileInterface) RemoveHost(hostname string) error { + ret := _m.Called(hostname) + + var r0 error + if rf, ok := ret.Get(0).(func(string) error); ok { + r0 = rf(hostname) + } else { + r0 = ret.Error(0) + } + + return r0 +} diff --git a/internal/tests/mocks/ProxyInterface.go b/internal/tests/mocks/proxy/ProxyInterface.go similarity index 100% rename from internal/tests/mocks/ProxyInterface.go rename to internal/tests/mocks/proxy/ProxyInterface.go diff --git a/internal/tests/mocks/RunnerInterface.go b/internal/tests/mocks/runner/RunnerInterface.go similarity index 100% rename from internal/tests/mocks/RunnerInterface.go rename to internal/tests/mocks/runner/RunnerInterface.go diff --git a/internal/tests/mocks/WatcherInterface.go b/internal/tests/mocks/watcher/WatcherInterface.go similarity index 100% rename from internal/tests/mocks/WatcherInterface.go rename to internal/tests/mocks/watcher/WatcherInterface.go diff --git a/pkg/forwarder/forwarder_test.go b/pkg/forwarder/forwarder_test.go index 8dc5f97d..ac6cb80d 100644 --- a/pkg/forwarder/forwarder_test.go +++ b/pkg/forwarder/forwarder_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/eko/monday/internal/config" - "github.com/eko/monday/internal/tests/mocks" + mocks "github.com/eko/monday/internal/tests/mocks/proxy" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) diff --git a/pkg/hostfile/client.go b/pkg/hostfile/client.go new file mode 100644 index 00000000..41e9f78f --- /dev/null +++ b/pkg/hostfile/client.go @@ -0,0 +1,46 @@ +package hostfile + +import "github.com/txn2/txeh" + +type HostfileInterface interface { + AddHost(ip, hostname string) error + RemoveHost(hostname string) error +} + +// Hostfile represents the host file manager client +type Hostfile struct { + hosts *txeh.Hosts +} + +// NewClient returns a new Hostfile manager client +func NewClient() (*Hostfile, error) { + hosts, err := txeh.NewHostsDefault() + if err != nil { + panic(err) + } + + return &Hostfile{ + hosts: hosts, + }, err +} + +// AddHost adds a new host / ip entry into the hosts file +func (h *Hostfile) AddHost(ip, hostname string) error { + h.hosts.Reload() + h.hosts.AddHost(ip, hostname) + err := h.hosts.Save() + if err != nil { + return err + } + + return nil +} + +// RemoveHost removes a given hostname from the hosts file +func (h *Hostfile) RemoveHost(hostname string) error { + h.hosts.Reload() + h.hosts.RemoveHost(hostname) + h.hosts.Save() + + return nil +} diff --git a/pkg/hostfile/hostfile.go b/pkg/hostfile/hostfile.go deleted file mode 100644 index 15dd1724..00000000 --- a/pkg/hostfile/hostfile.go +++ /dev/null @@ -1,42 +0,0 @@ -package hostfile - -import "github.com/txn2/txeh" - -func initClient() (*txeh.Hosts, error) { - hosts, err := txeh.NewHostsDefault() - if err != nil { - panic(err) - } - - // Always reload to ensure we have the latest version - hosts.Reload() - - return hosts, err -} - -func AddHost(ip, hostname string) error { - client, err := initClient() - if err != nil { - return err - } - - client.AddHost(ip, hostname) - err = client.Save() - if err != nil { - return err - } - - return nil -} - -func RemoveHost(hostname string) error { - client, err := initClient() - if err != nil { - return err - } - - client.RemoveHost(hostname) - client.Save() - - return nil -} diff --git a/pkg/proxy/proxy.go b/pkg/proxy/proxy.go index 3bc7d027..04a2e61a 100644 --- a/pkg/proxy/proxy.go +++ b/pkg/proxy/proxy.go @@ -20,8 +20,10 @@ type ProxyInterface interface { AddProxyForward(name string, proxyForward *ProxyForward) } +// Proxy represents the proxy component instance type Proxy struct { ProxyForwards map[string][]*ProxyForward + hostfile hostfile.HostfileInterface listeners map[string]net.Listener listening bool addProxyForwardMux sync.Mutex @@ -31,9 +33,11 @@ type Proxy struct { ipLastByte int } -func NewProxy() *Proxy { +// NewProxy initializes a new proxy component instance +func NewProxy(hostfile hostfile.HostfileInterface) *Proxy { return &Proxy{ ProxyForwards: make(map[string][]*ProxyForward, 0), + hostfile: hostfile, listeners: make(map[string]net.Listener), listening: true, latestPort: ProxyPortStart, @@ -131,7 +135,7 @@ func (p *Proxy) AddProxyForward(name string, proxyForward *ProxyForward) { p.generateProxyPort(proxyForward) } - err := hostfile.AddHost(proxyForward.LocalIP, proxyForward.GetHostname()) + err := p.hostfile.AddHost(proxyForward.LocalIP, proxyForward.GetHostname()) if err != nil { fmt.Printf("❌ An error has occured while trying to write host file for application '%s' (ip: %s): %v\n", proxyForward.Name, proxyForward.LocalIP, err) } diff --git a/pkg/proxy/proxy_test.go b/pkg/proxy/proxy_test.go index 3d028f9d..1d3cd396 100644 --- a/pkg/proxy/proxy_test.go +++ b/pkg/proxy/proxy_test.go @@ -3,16 +3,118 @@ package proxy import ( "testing" + mocks "github.com/eko/monday/internal/tests/mocks/hostfile" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) func TestNewProxy(t *testing.T) { + // Given + + hostfileMock := &mocks.HostfileInterface{} + // When - proxy := NewProxy() + proxy := NewProxy(hostfileMock) // Then assert.IsType(t, new(Proxy), proxy) assert.Len(t, proxy.ProxyForwards, 0) assert.Len(t, proxy.attributedIPs, 0) + assert.Equal(t, proxy.latestPort, "9400") + assert.Equal(t, proxy.ipLastByte, 1) +} + +func TestAddProxyForward(t *testing.T) { + // Given + pf := NewProxyForward("test", "hostname.svc.local", "", "8080", "8080") + + hostfileMock := &mocks.HostfileInterface{} + hostfileMock.On("AddHost", "127.1.2.1", "hostname.svc.local").Return(nil) + + proxy := NewProxy(hostfileMock) + + // When + proxy.AddProxyForward("test", pf) + + // Then + assert.Len(t, proxy.ProxyForwards, 1) + assert.Len(t, proxy.attributedIPs, 1) + assert.Equal(t, proxy.latestPort, "9401") + assert.Equal(t, proxy.ipLastByte, 2) +} + +func TestAddProxyForwardWhenMultiple(t *testing.T) { + // Given + testCases := []struct { + name string + hostname string + localPort string + forwardPort string + }{ + {name: "test", hostname: "hostname.svc.local", localPort: "8080", forwardPort: "8081"}, + {name: "test-2", hostname: "hostname2.svc.local", localPort: "8080", forwardPort: "8081"}, + {name: "test-2", hostname: "hostname3.svc.local", localPort: "8081", forwardPort: "8082"}, + } + + hostfileMock := &mocks.HostfileInterface{} + hostfileMock.ExpectedCalls = []*mock.Call{ + &mock.Call{ + Method: "AddHost", + Arguments: mock.Arguments{ + "127.1.2.1", "hostname.svc.local", + }, + ReturnArguments: mock.Arguments{nil}, + }, + &mock.Call{ + Method: "AddHost", + Arguments: mock.Arguments{ + "127.1.2.2", "hostname2.svc.local", + }, + ReturnArguments: mock.Arguments{nil}, + }, + &mock.Call{ + Method: "AddHost", + Arguments: mock.Arguments{ + "127.1.2.2", "hostname3.svc.local", + }, + ReturnArguments: mock.Arguments{nil}, + }, + } + + proxy := NewProxy(hostfileMock) + + // When + for _, testCase := range testCases { + pf := NewProxyForward(testCase.name, testCase.hostname, "", testCase.localPort, testCase.forwardPort) + proxy.AddProxyForward(testCase.name, pf) + } + + // Then + assert.Len(t, proxy.ProxyForwards, 2) + assert.Len(t, proxy.attributedIPs, 2) + assert.Equal(t, proxy.latestPort, "9403") + assert.Equal(t, proxy.ipLastByte, 3) +} + +func TestListen(t *testing.T) { + // Given + pf := NewProxyForward("test", "hostname.svc.local", "", "8080", "8080") + + hostfileMock := &mocks.HostfileInterface{} + hostfileMock.On("AddHost", "127.1.2.1", "hostname.svc.local").Return(nil) + + proxy := NewProxy(hostfileMock) + proxy.AddProxyForward("test", pf) + + // When + err := proxy.Listen() + + // Then + assert.Nil(t, err) + + assert.Len(t, proxy.ProxyForwards, 1) + assert.Len(t, proxy.attributedIPs, 1) + assert.Equal(t, proxy.latestPort, "9401") + assert.Equal(t, proxy.ipLastByte, 2) } diff --git a/pkg/runner/runner_test.go b/pkg/runner/runner_test.go index 075aab78..45b94b3d 100644 --- a/pkg/runner/runner_test.go +++ b/pkg/runner/runner_test.go @@ -7,7 +7,7 @@ import ( "time" "github.com/eko/monday/internal/config" - "github.com/eko/monday/internal/tests/mocks" + mocks "github.com/eko/monday/internal/tests/mocks/proxy" "github.com/stretchr/testify/assert" ) diff --git a/pkg/watcher/watcher_test.go b/pkg/watcher/watcher_test.go index cd7602d5..747a5157 100644 --- a/pkg/watcher/watcher_test.go +++ b/pkg/watcher/watcher_test.go @@ -6,15 +6,16 @@ import ( "time" "github.com/eko/monday/internal/config" - "github.com/eko/monday/internal/tests/mocks" + forwardermocks "github.com/eko/monday/internal/tests/mocks/forwarder" + runnermocks "github.com/eko/monday/internal/tests/mocks/runner" watcherlib "github.com/radovskyb/watcher" "github.com/stretchr/testify/assert" ) func TestNewWatcher(t *testing.T) { // Given - runner := &mocks.RunnerInterface{} - forwarder := &mocks.ForwarderInterface{} + runner := &runnermocks.RunnerInterface{} + forwarder := &forwardermocks.ForwarderInterface{} project := getProjectMock() @@ -43,11 +44,11 @@ func TestNewWatcher(t *testing.T) { func TestWatch(t *testing.T) { // Given - runner := &mocks.RunnerInterface{} + runner := &runnermocks.RunnerInterface{} runner.On("SetupAll").Once() runner.On("RunAll").Once() - forwarder := &mocks.ForwarderInterface{} + forwarder := &forwardermocks.ForwarderInterface{} forwarder.On("ForwardAll").Once() project := getProjectMock() @@ -60,11 +61,11 @@ func TestWatch(t *testing.T) { func TestWatchWhenFileChange(t *testing.T) { // Given - runner := &mocks.RunnerInterface{} + runner := &runnermocks.RunnerInterface{} runner.On("SetupAll").Once() runner.On("RunAll").Once() - forwarder := &mocks.ForwarderInterface{} + forwarder := &forwardermocks.ForwarderInterface{} forwarder.On("ForwardAll").Once() project := getProjectMock() @@ -100,11 +101,11 @@ func TestWatchWhenFileChange(t *testing.T) { func TestStop(t *testing.T) { // Given - runner := &mocks.RunnerInterface{} + runner := &runnermocks.RunnerInterface{} runner.On("SetupAll").Once() runner.On("RunAll").Once() - forwarder := &mocks.ForwarderInterface{} + forwarder := &forwardermocks.ForwarderInterface{} forwarder.On("ForwardAll").Once() project := getProjectMock()