Skip to content

Commit 870007e

Browse files
vip: support multiple VIP (#705) (#825)
Co-authored-by: djshow832 <[email protected]>
1 parent 0de024e commit 870007e

File tree

6 files changed

+78
-27
lines changed

6 files changed

+78
-27
lines changed

pkg/balance/metricsreader/backend_reader_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1297,8 +1297,7 @@ func TestElection(t *testing.T) {
12971297
// test owner
12981298
suite.delKV(ownerKey)
12991299
require.Eventually(t, func() bool {
1300-
kvs := suite.getKV(ownerKey)
1301-
return len(kvs) == 1 && strings.HasSuffix(string(kvs[0].Value), "3080")
1300+
return br.isOwner.Load()
13021301
}, 3*time.Second, 10*time.Millisecond)
13031302
err = br.ReadMetrics(context.Background())
13041303
require.NoError(t, err)

pkg/manager/vip/manager.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package vip
55

66
import (
77
"context"
8+
"fmt"
89
"net"
910
"sync/atomic"
1011

@@ -15,8 +16,9 @@ import (
1516
)
1617

1718
const (
18-
// vipKey is the key in etcd for VIP election.
19-
vipKey = "/tiproxy/vip/owner"
19+
// vipKey is the key in etcd for VIP election. The key contains the VIP address so that
20+
// multiple VIP can coexist.
21+
vipKey = "/tiproxy/vip/%s/owner"
2022
// sessionTTL is the session's TTL in seconds for VIP election.
2123
// The etcd client keeps alive every TTL/3 seconds.
2224
// The TTL determines the failover time so it should be short.
@@ -74,7 +76,8 @@ func (vm *vipManager) Start(ctx context.Context, etcdCli *clientv3.Client) error
7476

7577
id := net.JoinHostPort(ip, port)
7678
electionCfg := elect.DefaultElectionConfig(sessionTTL)
77-
vm.election = elect.NewElection(vm.lg.Named("elect"), etcdCli, electionCfg, id, vipKey, vm)
79+
key := fmt.Sprintf(vipKey, vm.operation.Addr())
80+
vm.election = elect.NewElection(vm.lg.Named("elect"), etcdCli, electionCfg, id, key, vm)
7881
vm.election.Start(ctx)
7982
return nil
8083
}

pkg/manager/vip/manager_test.go

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ func TestNetworkOperation(t *testing.T) {
123123
operation := newMockNetworkOperation()
124124
vm := &vipManager{
125125
lg: lg,
126-
cfgGetter: newMockConfigGetter(&config.Config{HA: config.HA{VirtualIP: "10.10.10.10/24", Interface: "eth0"}}),
126+
cfgGetter: newMockConfigGetter(newMockConfig()),
127127
operation: operation,
128128
}
129129
vm.delOnRetire.Store(true)
@@ -149,11 +149,7 @@ func TestNetworkOperation(t *testing.T) {
149149

150150
func TestStartAndClose(t *testing.T) {
151151
lg, _ := logger.CreateLoggerForTest(t)
152-
vm, err := NewVIPManager(lg, newMockConfigGetter(&config.Config{
153-
Proxy: config.ProxyServer{Addr: "0.0.0.0:6000"},
154-
API: config.API{Addr: "0.0.0.0:3080"},
155-
HA: config.HA{VirtualIP: "127.0.0.2/24", Interface: "lo"},
156-
}))
152+
vm, err := NewVIPManager(lg, newMockConfigGetter(newMockConfig()))
157153
if runtime.GOOS != "linux" {
158154
require.Error(t, err)
159155
} else {
@@ -178,3 +174,44 @@ func TestStartAndClose(t *testing.T) {
178174
vm.Close()
179175
}
180176
}
177+
178+
func TestMultiVIP(t *testing.T) {
179+
if runtime.GOOS != "linux" {
180+
return
181+
}
182+
config1, config2 := newMockConfig(), newMockConfig()
183+
config1.HA.VirtualIP = "127.0.0.2/24"
184+
config2.HA.VirtualIP = "127.0.0.3/24"
185+
lg, text := logger.CreateLoggerForTest(t)
186+
vm1, err := NewVIPManager(lg, newMockConfigGetter(config1))
187+
// Maybe interface doesn't exist or lack of permission.
188+
if err != nil {
189+
return
190+
}
191+
vm2, err := NewVIPManager(lg, newMockConfigGetter(config2))
192+
require.NoError(t, err)
193+
194+
server, err := etcd.CreateEtcdServer("0.0.0.0:0", t.TempDir(), lg)
195+
require.NoError(t, err)
196+
endpoint := server.Clients[0].Addr().String()
197+
cfg := etcd.ConfigForEtcdTest(endpoint)
198+
199+
certMgr := cert.NewCertManager()
200+
err = certMgr.Init(cfg, lg, nil)
201+
require.NoError(t, err)
202+
client, err := etcd.InitEtcdClient(lg, cfg, certMgr)
203+
require.NoError(t, err)
204+
205+
err = vm1.Start(context.Background(), client)
206+
require.NoError(t, err)
207+
err = vm2.Start(context.Background(), client)
208+
require.NoError(t, err)
209+
require.Eventually(t, func() bool {
210+
return strings.Count(text.String(), "adding VIP success") >= 2 ||
211+
strings.Count(text.String(), "ip: command not found") >= 2
212+
}, 3*time.Second, 10*time.Millisecond)
213+
vm1.PreClose()
214+
vm2.PreClose()
215+
vm1.Close()
216+
vm2.Close()
217+
}

pkg/manager/vip/mock_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,15 @@ func (mno *mockNetworkOperation) SendARP() error {
121121
}
122122
return nil
123123
}
124+
125+
func (mno *mockNetworkOperation) Addr() string {
126+
return ""
127+
}
128+
129+
func newMockConfig() *config.Config {
130+
return &config.Config{
131+
Proxy: config.ProxyServer{Addr: "0.0.0.0:6000"},
132+
API: config.API{Addr: "0.0.0.0:3080"},
133+
HA: config.HA{VirtualIP: "127.0.0.2/24", Interface: "lo"},
134+
}
135+
}

pkg/manager/vip/network.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ type NetworkOperation interface {
2020
AddIP() error
2121
DeleteIP() error
2222
SendARP() error
23+
Addr() string
2324
}
2425

2526
var _ NetworkOperation = (*networkOperation)(nil)
@@ -93,3 +94,10 @@ func (no *networkOperation) SendARP() error {
9394
}
9495
return errors.WithStack(err)
9596
}
97+
98+
func (no *networkOperation) Addr() string {
99+
if no.address == nil {
100+
return ""
101+
}
102+
return no.address.IP.String()
103+
}

pkg/manager/vip/network_test.go

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,19 @@ func TestAddDelIP(t *testing.T) {
2121
tests := []struct {
2222
virtualIP string
2323
link string
24+
addr string
2425
initErr string
25-
addErr string
2626
delErr string
27-
sendErr string
2827
}{
2928
{
3029
virtualIP: "127.0.0.2/24",
30+
addr: "127.0.0.2",
3131
link: "lo",
3232
},
3333
{
3434
virtualIP: "0.0.0.0/24",
3535
link: "lo",
36+
addr: "0.0.0.0",
3637
delErr: "cannot assign requested address",
3738
},
3839
{
@@ -55,27 +56,18 @@ func TestAddDelIP(t *testing.T) {
5556
}
5657
require.NoError(t, err, "case %d", i)
5758
require.NotNil(t, operation, "case %d", i)
59+
require.Equal(t, test.addr, operation.Addr(), "case %d", i)
5860

5961
err = operation.AddIP()
6062
// Maybe the command is not installed.
61-
if err != nil && isOtherErr(err) {
63+
if err != nil {
64+
require.True(t, isOtherErr(err))
6265
continue
6366
}
64-
if test.addErr != "" {
65-
require.Error(t, err, "case %d", i)
66-
require.Contains(t, err.Error(), test.addErr, "case %d", i)
67-
} else {
68-
require.NoError(t, err, "case %d", i)
69-
}
7067

7168
err = operation.SendARP()
72-
if err == nil || !isOtherErr(err) {
73-
if test.sendErr != "" {
74-
require.Error(t, err, "case %d", i)
75-
require.Contains(t, err.Error(), test.sendErr, "case %d", i)
76-
} else {
77-
require.NoError(t, err, "case %d", i)
78-
}
69+
if err != nil {
70+
require.True(t, isOtherErr(err))
7971
}
8072

8173
if err := operation.DeleteIP(); test.delErr != "" {

0 commit comments

Comments
 (0)