diff --git a/agent/consul/internal_endpoint.go b/agent/consul/internal_endpoint.go index 074f7ffe09..5a165737af 100644 --- a/agent/consul/internal_endpoint.go +++ b/agent/consul/internal_endpoint.go @@ -8,6 +8,7 @@ import ( "net" hashstructure_v2 "github.com/mitchellh/hashstructure/v2" + "golang.org/x/exp/maps" "github.com/hashicorp/go-bexpr" "github.com/hashicorp/go-hclog" @@ -778,11 +779,10 @@ func (m *Internal) AssignManualServiceVIPs(args *structs.AssignServiceManualVIPs if parsedIP == nil || parsedIP.To4() == nil { return fmt.Errorf("%q is not a valid IPv4 address", parsedIP.String()) } - if _, ok := vipMap[ip]; ok { - return fmt.Errorf("duplicate manual ip found: %q", ip) - } vipMap[ip] = struct{}{} } + // Silently ignore duplicates. + args.ManualVIPs = maps.Keys(vipMap) psn := structs.PeeredServiceName{ ServiceName: structs.NewServiceName(args.Service, &args.EnterpriseMeta), diff --git a/agent/consul/internal_endpoint_test.go b/agent/consul/internal_endpoint_test.go index c5a8a76fb0..caa32393c9 100644 --- a/agent/consul/internal_endpoint_test.go +++ b/agent/consul/internal_endpoint_test.go @@ -3721,6 +3721,7 @@ func TestInternal_AssignManualServiceVIPs(t *testing.T) { expect structs.AssignServiceManualVIPsResponse expectAgain structs.AssignServiceManualVIPsResponse expectErr string + expectIPs []string } run := func(t *testing.T, tc testcase, again bool) { @@ -3741,6 +3742,12 @@ func TestInternal_AssignManualServiceVIPs(t *testing.T) { } else { require.Equal(t, tc.expect, resp) } + + psn := structs.PeeredServiceName{ServiceName: structs.NewServiceName(tc.req.Service, nil)} + got, err := s1.fsm.State().ServiceManualVIPs(psn) + require.NoError(t, err) + require.NotNil(t, got) + require.Equal(t, tc.expectIPs, got.ManualIPs) } } @@ -3751,6 +3758,17 @@ func TestInternal_AssignManualServiceVIPs(t *testing.T) { Service: "web", ManualVIPs: []string{"1.1.1.1", "2.2.2.2"}, }, + expectIPs: []string{"1.1.1.1", "2.2.2.2"}, + expect: structs.AssignServiceManualVIPsResponse{Found: true}, + expectAgain: structs.AssignServiceManualVIPsResponse{Found: true}, + }, + { + name: "successfully ignoring duplicates", + req: structs.AssignServiceManualVIPsRequest{ + Service: "web", + ManualVIPs: []string{"1.2.3.4", "5.6.7.8", "1.2.3.4", "5.6.7.8"}, + }, + expectIPs: []string{"1.2.3.4", "5.6.7.8"}, expect: structs.AssignServiceManualVIPsResponse{Found: true}, expectAgain: structs.AssignServiceManualVIPsResponse{Found: true}, }, @@ -3760,6 +3778,7 @@ func TestInternal_AssignManualServiceVIPs(t *testing.T) { Service: "web", ManualVIPs: []string{"8.8.8.8"}, }, + expectIPs: []string{"8.8.8.8"}, expect: structs.AssignServiceManualVIPsResponse{ Found: true, UnassignedFrom: []structs.PeeredServiceName{ @@ -3777,7 +3796,6 @@ func TestInternal_AssignManualServiceVIPs(t *testing.T) { Service: "web", ManualVIPs: []string{"3.3.3.3", "invalid"}, }, - expect: structs.AssignServiceManualVIPsResponse{}, expectErr: "not a valid", }, }