From f229a5eaa5432cbbbb7111b47c114d5532d2b3fa Mon Sep 17 00:00:00 2001 From: Edward Wibowo Date: Fri, 11 Aug 2023 19:44:22 -0400 Subject: [PATCH] fix: ensure config does not have duplicate hosts --- .golangci.yml | 3 +++ config/config.go | 9 +++++++++ config/config_test.go | 36 ++++++++++++++---------------------- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 3997441..8e6e45b 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -6,7 +6,10 @@ linters: disable-all: true enable: - bodyclose + - errname + - errorlint - errcheck + - exhaustive - gofumpt - gosec - gosimple diff --git a/config/config.go b/config/config.go index 98cb26f..3b5d5f0 100644 --- a/config/config.go +++ b/config/config.go @@ -17,6 +17,7 @@ var ( errInvalidConfig = fmt.Errorf("invalid config") errMustHaveOneService = fmt.Errorf("must have exactly one service") errNoServiceWithName = fmt.Errorf("no service with name") + errDuplicateHost = fmt.Errorf("duplicate host") ) type serviceMap map[string]struct { @@ -32,11 +33,19 @@ type Config struct { ReadTimeout time.Duration `yaml:"readTimeout"` } +// validate returns true if every service has exactly one service and +// there are no duplicate hosts. func (s *serviceMap) validate() error { + hosts := make(map[string]bool) for name, service := range *s { if !service.Services.Validate() { return fmt.Errorf("%s: %w", name, errMustHaveOneService) } + + if _, ok := hosts[service.Host]; ok { + return fmt.Errorf("%w %s", errDuplicateHost, service.Host) + } + hosts[service.Host] = true } return nil } diff --git a/config/config_test.go b/config/config_test.go index b44e996..89c8455 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -81,6 +81,19 @@ func TestServiceMapValidate(t *testing.T) { }, errMustHaveOneService, }, + "services with duplicate host": { + serviceMap{ + "a": { + Config: services.Config{Host: "b"}, + Services: services.Services{Redirect: "c"}, + }, + "d": { + Config: services.Config{Host: "b"}, + Services: services.Services{Redirect: "c"}, + }, + }, + errDuplicateHost, + }, } for name, test := range tests { t.Run(name, func(t *testing.T) { @@ -410,7 +423,7 @@ func TestConfigServiceListWithContainers(t *testing.T) { } } -func TestServiceListLoadBalancerError(t *testing.T) { +func TestConfigServiceListLoadBalancerError(t *testing.T) { tests := map[string]struct { lbInfo services.LoadBalancerInfo }{ @@ -453,27 +466,6 @@ func TestServiceListLoadBalancerError(t *testing.T) { } } -func TestServiceList(t *testing.T) { - conf := Config{ - Services: serviceMap{ - "foo": { - Config: services.Config{Host: "a"}, - Services: services.Services{ - LoadBalancer: &services.LoadBalancerInfo{ - Strategy: "not a valid strategy", - }, - }, - }, - }, - } - - _, err := conf.ServiceList(dockerapi.NewMock(nil)) - - if !errors.Is(err, errInvalidConfig) { - t.Errorf("expected error %v got error %v", errInvalidConfig, err) - } -} - func TestConfigTLSHosts(t *testing.T) { tests := map[string]struct { conf Config