Skip to content

Commit dc1fe5d

Browse files
DerekBumoleg-jukovec
authored andcommitted
pool: get instance status from WatchOnce request
Starting from Tarantool version >= 3.0.0 `WatchOnce` requset is supported. So we can get instance status using this request instead of calling `box.info`. This way user can add instances to the ConnectionPool without the `execute` access. Closes #380
1 parent c070b26 commit dc1fe5d

File tree

6 files changed

+115
-9
lines changed

6 files changed

+115
-9
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release.
1212

1313
### Changed
1414

15+
- `execute` access for `box.info` is no longer required for ConnectionPool
16+
for a Tarantool version >= 3.0.0 (#380)
17+
1518
### Fixed
1619

1720
- `ConnectionPool.Remove()` does not notify a `ConnectionHandler` after

pool/config.lua

+3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ box.once("init", function()
99
box.schema.user.create('test', { password = 'test' })
1010
box.schema.user.grant('test', 'read,write,execute', 'universe')
1111

12+
box.schema.user.create('test_noexec', { password = 'test' })
13+
box.schema.user.grant('test_noexec', 'read,write', 'universe')
14+
1215
local s = box.schema.space.create('testPool', {
1316
id = 520,
1417
if_not_exists = true,

pool/connection_pool.go

+15-2
Original file line numberDiff line numberDiff line change
@@ -1017,7 +1017,20 @@ func (p *ConnectionPool) DoInstance(req tarantool.Request, name string) *taranto
10171017
//
10181018

10191019
func (p *ConnectionPool) getConnectionRole(conn *tarantool.Connection) (Role, error) {
1020-
data, err := conn.Do(tarantool.NewCallRequest("box.info")).Get()
1020+
var (
1021+
roFieldName string
1022+
data []interface{}
1023+
err error
1024+
)
1025+
1026+
if isFeatureInSlice(iproto.IPROTO_FEATURE_WATCH_ONCE, conn.ProtocolInfo().Features) {
1027+
roFieldName = "is_ro"
1028+
data, err = conn.Do(tarantool.NewWatchOnceRequest("box.status")).Get()
1029+
} else {
1030+
roFieldName = "ro"
1031+
data, err = conn.Do(tarantool.NewCallRequest("box.info")).Get()
1032+
}
1033+
10211034
if err != nil {
10221035
return UnknownRole, err
10231036
}
@@ -1033,7 +1046,7 @@ func (p *ConnectionPool) getConnectionRole(conn *tarantool.Connection) (Role, er
10331046
return UnknownRole, ErrIncorrectStatus
10341047
}
10351048

1036-
replicaRole, ok := data[0].(map[interface{}]interface{})["ro"]
1049+
replicaRole, ok := data[0].(map[interface{}]interface{})[roFieldName]
10371050
if !ok {
10381051
return UnknownRole, ErrIncorrectResponse
10391052
}

pool/connection_pool_test.go

+85
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package pool_test
22

33
import (
4+
"bytes"
45
"context"
56
"fmt"
67
"log"
@@ -22,6 +23,7 @@ import (
2223
)
2324

2425
var user = "test"
26+
var userNoExec = "test_noexec"
2527
var pass = "test"
2628
var spaceNo = uint32(520)
2729
var spaceName = "testPool"
@@ -68,6 +70,18 @@ func makeInstance(server string, opts tarantool.Opts) pool.Instance {
6870
}
6971
}
7072

73+
func makeNoExecuteInstance(server string, opts tarantool.Opts) pool.Instance {
74+
return pool.Instance{
75+
Name: server,
76+
Dialer: tarantool.NetDialer{
77+
Address: server,
78+
User: userNoExec,
79+
Password: pass,
80+
},
81+
Opts: opts,
82+
}
83+
}
84+
7185
func makeInstances(servers []string, opts tarantool.Opts) []pool.Instance {
7286
var instances []pool.Instance
7387
for _, server := range servers {
@@ -130,6 +144,77 @@ func TestConnSuccessfully(t *testing.T) {
130144
require.Nil(t, err)
131145
}
132146

147+
func TestConn_no_execute_supported(t *testing.T) {
148+
test_helpers.SkipIfWatchOnceUnsupported(t)
149+
150+
healthyServ := servers[0]
151+
152+
ctx, cancel := test_helpers.GetPoolConnectContext()
153+
defer cancel()
154+
connPool, err := pool.Connect(ctx,
155+
[]pool.Instance{makeNoExecuteInstance(healthyServ, connOpts)})
156+
require.Nilf(t, err, "failed to connect")
157+
require.NotNilf(t, connPool, "conn is nil after Connect")
158+
159+
defer connPool.Close()
160+
161+
args := test_helpers.CheckStatusesArgs{
162+
ConnPool: connPool,
163+
Mode: pool.ANY,
164+
Servers: []string{healthyServ},
165+
ExpectedPoolStatus: true,
166+
ExpectedStatuses: map[string]bool{
167+
healthyServ: true,
168+
},
169+
}
170+
171+
err = test_helpers.CheckPoolStatuses(args)
172+
require.Nil(t, err)
173+
174+
_, err = connPool.Do(tarantool.NewPingRequest(), pool.ANY).Get()
175+
require.Nil(t, err)
176+
}
177+
178+
func TestConn_no_execute_unsupported(t *testing.T) {
179+
test_helpers.SkipIfWatchOnceSupported(t)
180+
181+
var buf bytes.Buffer
182+
log.SetOutput(&buf)
183+
defer log.SetOutput(os.Stderr)
184+
185+
healthyServ := servers[0]
186+
187+
ctx, cancel := test_helpers.GetPoolConnectContext()
188+
defer cancel()
189+
connPool, err := pool.Connect(ctx,
190+
[]pool.Instance{makeNoExecuteInstance(healthyServ, connOpts)})
191+
require.Nilf(t, err, "failed to connect")
192+
require.NotNilf(t, connPool, "conn is nil after Connect")
193+
194+
defer connPool.Close()
195+
196+
require.Contains(t, buf.String(),
197+
fmt.Sprintf("connect to %s failed: Execute access to function "+
198+
"'box.info' is denied for user '%s'", servers[0], userNoExec))
199+
200+
args := test_helpers.CheckStatusesArgs{
201+
ConnPool: connPool,
202+
Mode: pool.ANY,
203+
Servers: []string{healthyServ},
204+
ExpectedPoolStatus: false,
205+
ExpectedStatuses: map[string]bool{
206+
healthyServ: false,
207+
},
208+
}
209+
210+
err = test_helpers.CheckPoolStatuses(args)
211+
require.Nil(t, err)
212+
213+
_, err = connPool.Do(tarantool.NewPingRequest(), pool.ANY).Get()
214+
require.Error(t, err)
215+
require.Equal(t, "can't find healthy instance in pool", err.Error())
216+
}
217+
133218
func TestConnect_empty(t *testing.T) {
134219
cases := []struct {
135220
Name string

tarantool_test.go

+1-7
Original file line numberDiff line numberDiff line change
@@ -2591,13 +2591,7 @@ func TestConnectionDoWatchOnceRequest(t *testing.T) {
25912591
}
25922592

25932593
func TestConnectionDoWatchOnceOnEmptyKey(t *testing.T) {
2594-
watchOnceNotSupported, err := test_helpers.IsTarantoolVersionLess(3, 0, 0)
2595-
if err != nil {
2596-
log.Fatalf("Could not check the Tarantool version: %s", err)
2597-
}
2598-
if watchOnceNotSupported {
2599-
return
2600-
}
2594+
test_helpers.SkipIfWatchOnceUnsupported(t)
26012595

26022596
conn := test_helpers.ConnectWithValidation(t, dialer, opts)
26032597
defer conn.Close()

test_helpers/utils.go

+8
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,14 @@ func SkipIfWatchOnceUnsupported(t *testing.T) {
200200
SkipIfFeatureUnsupported(t, "watch once", 3, 0, 0)
201201
}
202202

203+
// SkipIfWatchOnceSupported skips test run if Tarantool with WatchOnce
204+
// request type is used.
205+
func SkipIfWatchOnceSupported(t *testing.T) {
206+
t.Helper()
207+
208+
SkipIfFeatureSupported(t, "watch once", 3, 0, 0)
209+
}
210+
203211
// SkipIfCrudSpliceBroken skips test run if splice operation is broken
204212
// on the crud side.
205213
// https://github.com/tarantool/crud/issues/397

0 commit comments

Comments
 (0)