Skip to content

Commit 236eba8

Browse files
committed
lib: common cmd parts to connect package
Moves the common URL parsing logic, connect to etcd and tarantool from the `cmd` package to the `connect` package. Part of #TNTP-1081
1 parent 0d17f18 commit 236eba8

File tree

12 files changed

+423
-333
lines changed

12 files changed

+423
-333
lines changed

cli/cluster/cmd/common.go

Lines changed: 10 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package cmd
33
import (
44
"errors"
55
"fmt"
6-
"os"
76

87
clientv3 "go.etcd.io/etcd/client/v3"
98

@@ -142,59 +141,17 @@ type connectOpts struct {
142141
Password string
143142
}
144143

145-
// connectTarantool establishes a connection to Tarantool.
146-
func connectTarantool(uriOpts UriOpts, connOpts connectOpts) (tarantool.Connector, error) {
147-
addr, connectorOpts := MakeConnectOptsFromUriOpts(uriOpts)
148-
if connectorOpts.User == "" && connectorOpts.Pass == "" {
149-
connectorOpts.User = connOpts.Username
150-
connectorOpts.Pass = connOpts.Password
151-
if connectorOpts.User == "" {
152-
connectorOpts.User = os.Getenv(connect.TarantoolUsernameEnv)
153-
}
154-
if connectorOpts.Pass == "" {
155-
connectorOpts.Pass = os.Getenv(connect.TarantoolPasswordEnv)
156-
}
157-
}
158-
159-
conn, err := tarantool.Connect(addr, connectorOpts)
160-
if err != nil {
161-
return nil, fmt.Errorf("failed to connect to tarantool: %w", err)
162-
}
163-
return conn, nil
164-
}
165-
166-
// connectEtcd establishes a connection to etcd.
167-
func connectEtcd(uriOpts UriOpts, connOpts connectOpts) (*clientv3.Client, error) {
168-
etcdOpts := MakeEtcdOptsFromUriOpts(uriOpts)
169-
if etcdOpts.Username == "" && etcdOpts.Password == "" {
170-
etcdOpts.Username = connOpts.Username
171-
etcdOpts.Password = connOpts.Password
172-
if etcdOpts.Username == "" {
173-
etcdOpts.Username = os.Getenv(connect.EtcdUsernameEnv)
174-
}
175-
if etcdOpts.Password == "" {
176-
etcdOpts.Password = os.Getenv(connect.EtcdPasswordEnv)
177-
}
178-
}
179-
180-
etcdcli, err := libcluster.ConnectEtcd(etcdOpts)
181-
if err != nil {
182-
return nil, fmt.Errorf("failed to connect to etcd: %w", err)
183-
}
184-
return etcdcli, nil
185-
}
186-
187144
// doOnStorage determines a storage based on the opts.
188-
func doOnStorage(connOpts connectOpts, opts UriOpts,
189-
tarantoolFunc func(tarantool.Connector) error, etcdFunc func(*clientv3.Client) error) error {
190-
etcdcli, errEtcd := connectEtcd(opts, connOpts)
191-
if errEtcd == nil {
192-
return etcdFunc(etcdcli)
145+
func doOnStorage(opts connect.UriOpts,
146+
tarantoolFunc connect.TarantoolFunc, etcdFunc connect.EtcdFunc) error {
147+
done, errEtcd := connect.RunOnEtcd(opts, etcdFunc)
148+
if done {
149+
return errEtcd
193150
}
194151

195-
conn, errTarantool := connectTarantool(opts, connOpts)
196-
if errTarantool == nil {
197-
return tarantoolFunc(conn)
152+
done, errTarantool := connect.RunOnTarantool(opts, tarantoolFunc)
153+
if done {
154+
return errTarantool
198155
}
199156

200157
return fmt.Errorf("failed to establish a connection to tarantool or etcd: %w, %w",
@@ -205,8 +162,7 @@ func doOnStorage(connOpts connectOpts, opts UriOpts,
205162
func createPublisherAndCollector(
206163
publishers libcluster.DataPublisherFactory,
207164
collectors libcluster.CollectorFactory,
208-
connOpts connectOpts,
209-
opts UriOpts) (libcluster.DataPublisher, libcluster.Collector, func(), error) {
165+
opts connect.UriOpts) (libcluster.DataPublisher, libcluster.Collector, func(), error) {
210166
prefix, key, timeout := opts.Prefix, opts.Key, opts.Timeout
211167

212168
var (
@@ -254,7 +210,7 @@ func createPublisherAndCollector(
254210
return nil
255211
}
256212

257-
if err := doOnStorage(connOpts, opts, tarantoolFunc, etcdFunc); err != nil {
213+
if err := doOnStorage(opts, tarantoolFunc, etcdFunc); err != nil {
258214
return nil, nil, nil, err
259215
}
260216

cli/cluster/cmd/failover.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/apex/log"
1010
"github.com/google/uuid"
1111
libcluster "github.com/tarantool/tt/lib/cluster"
12+
"github.com/tarantool/tt/lib/connect"
1213
"go.etcd.io/etcd/api/v3/mvccpb"
1314
clientv3 "go.etcd.io/etcd/client/v3"
1415
"gopkg.in/yaml.v2"
@@ -63,7 +64,7 @@ type SwitchStatusCtx struct {
6364
TaskID string
6465
}
6566

66-
func makeEtcdOpts(uriOpts UriOpts) libcluster.EtcdOpts {
67+
func makeEtcdOpts(uriOpts connect.UriOpts) libcluster.EtcdOpts {
6768
opts := libcluster.EtcdOpts{
6869
Endpoints: []string{uriOpts.Endpoint},
6970
Username: uriOpts.Username,
@@ -81,7 +82,7 @@ func makeEtcdOpts(uriOpts UriOpts) libcluster.EtcdOpts {
8182

8283
// Switch master instance.
8384
func Switch(uri *url.URL, switchCtx SwitchCtx) error {
84-
uriOpts, err := ParseUriOpts(uri)
85+
uriOpts, err := connect.ParseUriOpts(uri, "", "")
8586
if err != nil {
8687
return fmt.Errorf("invalid URL %q: %w", uri, err)
8788
}
@@ -173,7 +174,7 @@ func Switch(uri *url.URL, switchCtx SwitchCtx) error {
173174

174175
// SwitchStatus shows master switching status.
175176
func SwitchStatus(uri *url.URL, switchCtx SwitchStatusCtx) error {
176-
uriOpts, err := ParseUriOpts(uri)
177+
uriOpts, err := connect.ParseUriOpts(uri, "", "")
177178
if err != nil {
178179
return fmt.Errorf("invalid URL %q: %w", uri, err)
179180
}

cli/cluster/cmd/publish.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"net/url"
66

77
libcluster "github.com/tarantool/tt/lib/cluster"
8+
"github.com/tarantool/tt/lib/connect"
89
)
910

1011
// PublishCtx contains information abould cluster publish command execution
@@ -33,7 +34,8 @@ type PublishCtx struct {
3334

3435
// PublishUri publishes a configuration to URI.
3536
func PublishUri(publishCtx PublishCtx, uri *url.URL) error {
36-
uriOpts, err := ParseUriOpts(uri)
37+
uriOpts, err := connect.ParseUriOpts(uri,
38+
publishCtx.Username, publishCtx.Password)
3739
if err != nil {
3840
return fmt.Errorf("invalid URL %q: %w", uri, err)
3941
}
@@ -43,14 +45,10 @@ func PublishUri(publishCtx PublishCtx, uri *url.URL) error {
4345
return err
4446
}
4547

46-
connOpts := connectOpts{
47-
Username: publishCtx.Username,
48-
Password: publishCtx.Password,
49-
}
5048
publisher, collector, cancel, err := createPublisherAndCollector(
5149
publishCtx.Publishers,
5250
publishCtx.Collectors,
53-
connOpts, uriOpts)
51+
uriOpts)
5452
if err != nil {
5553
return err
5654
}

cli/cluster/cmd/replicaset.go

Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/tarantool/go-tarantool"
1111
"github.com/tarantool/tt/cli/replicaset"
1212
libcluster "github.com/tarantool/tt/lib/cluster"
13+
"github.com/tarantool/tt/lib/connect"
1314
clientv3 "go.etcd.io/etcd/client/v3"
1415
)
1516

@@ -107,7 +108,7 @@ func pickPatchKey(keys []string, force bool, pathMsg string) (int, error) {
107108
func createDataCollectorAndKeyPublisher(
108109
collectors libcluster.DataCollectorFactory,
109110
publishers libcluster.DataPublisherFactory,
110-
opts UriOpts, connOpts connectOpts) (
111+
opts connect.UriOpts) (
111112
libcluster.DataCollector, replicaset.DataPublisher, func(), error) {
112113
prefix, key, timeout := opts.Prefix, opts.Key, opts.Timeout
113114
var (
@@ -145,7 +146,7 @@ func createDataCollectorAndKeyPublisher(
145146
return nil
146147
}
147148

148-
if err := doOnStorage(connOpts, opts, tarantoolFunc, etcdFunc); err != nil {
149+
if err := doOnStorage(opts, tarantoolFunc, etcdFunc); err != nil {
149150
return nil, nil, nil, err
150151
}
151152

@@ -154,17 +155,14 @@ func createDataCollectorAndKeyPublisher(
154155

155156
// Promote promotes an instance by patching the cluster config.
156157
func Promote(uri *url.URL, ctx PromoteCtx) error {
157-
opts, err := ParseUriOpts(uri)
158+
opts, err := connect.ParseUriOpts(uri,
159+
ctx.Username, ctx.Password)
158160
if err != nil {
159161
return fmt.Errorf("invalid URL %q: %w", uri, err)
160162
}
161-
connOpts := connectOpts{
162-
Username: ctx.Username,
163-
Password: ctx.Password,
164-
}
165163

166164
collector, publisher, closeFunc, err := createDataCollectorAndKeyPublisher(
167-
ctx.Collectors, ctx.Publishers, opts, connOpts)
165+
ctx.Collectors, ctx.Publishers, opts)
168166
if err != nil {
169167
return err
170168
}
@@ -201,17 +199,14 @@ type DemoteCtx struct {
201199

202200
// Demote demotes an instance by patching the cluster config.
203201
func Demote(uri *url.URL, ctx DemoteCtx) error {
204-
opts, err := ParseUriOpts(uri)
202+
opts, err := connect.ParseUriOpts(uri,
203+
ctx.Username, ctx.Password)
205204
if err != nil {
206205
return fmt.Errorf("invalid URL %q: %w", uri, err)
207206
}
208-
connOpts := connectOpts{
209-
Username: ctx.Username,
210-
Password: ctx.Password,
211-
}
212207

213208
collector, publisher, closeFunc, err := createDataCollectorAndKeyPublisher(
214-
ctx.Collectors, ctx.Publishers, opts, connOpts)
209+
ctx.Collectors, ctx.Publishers, opts)
215210
if err != nil {
216211
return err
217212
}
@@ -248,17 +243,14 @@ type ExpelCtx struct {
248243

249244
// Expel expels an instance by patching the cluster config.
250245
func Expel(uri *url.URL, ctx ExpelCtx) error {
251-
opts, err := ParseUriOpts(uri)
246+
opts, err := connect.ParseUriOpts(uri,
247+
ctx.Username, ctx.Password)
252248
if err != nil {
253249
return fmt.Errorf("invalid URL %q: %w", uri, err)
254250
}
255-
connOpts := connectOpts{
256-
Username: ctx.Username,
257-
Password: ctx.Password,
258-
}
259251

260252
collector, publisher, closeFunc, err := createDataCollectorAndKeyPublisher(
261-
ctx.Collectors, ctx.Publishers, opts, connOpts)
253+
ctx.Collectors, ctx.Publishers, opts)
262254
if err != nil {
263255
return err
264256
}
@@ -302,17 +294,15 @@ type RolesChangeCtx struct {
302294

303295
// ChangeRole adds/removes a role by patching the cluster config.
304296
func ChangeRole(uri *url.URL, ctx RolesChangeCtx, action replicaset.RolesChangerAction) error {
305-
opts, err := ParseUriOpts(uri)
297+
opts, err := connect.ParseUriOpts(uri,
298+
ctx.Username, ctx.Password)
299+
306300
if err != nil {
307301
return fmt.Errorf("invalid URL %q: %w", uri, err)
308302
}
309-
connOpts := connectOpts{
310-
Username: ctx.Username,
311-
Password: ctx.Password,
312-
}
313303

314304
collector, publisher, closeFunc, err := createDataCollectorAndKeyPublisher(
315-
ctx.Collectors, ctx.Publishers, opts, connOpts)
305+
ctx.Collectors, ctx.Publishers, opts)
316306
if err != nil {
317307
return err
318308
}

cli/cluster/cmd/show.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66

77
"github.com/tarantool/tt/cli/cluster"
88
libcluster "github.com/tarantool/tt/lib/cluster"
9+
"github.com/tarantool/tt/lib/connect"
910
)
1011

1112
// ShowCtx contains information about cluster show command execution context.
@@ -23,19 +24,16 @@ type ShowCtx struct {
2324

2425
// ShowUri shows a configuration from URI.
2526
func ShowUri(showCtx ShowCtx, uri *url.URL) error {
26-
uriOpts, err := ParseUriOpts(uri)
27+
uriOpts, err := connect.ParseUriOpts(uri,
28+
showCtx.Username, showCtx.Password)
2729
if err != nil {
2830
return fmt.Errorf("invalid URL %q: %w", uri, err)
2931
}
3032

31-
connOpts := connectOpts{
32-
Username: showCtx.Username,
33-
Password: showCtx.Password,
34-
}
3533
_, collector, cancel, err := createPublisherAndCollector(
3634
nil,
3735
showCtx.Collectors,
38-
connOpts, uriOpts)
36+
uriOpts)
3937
if err != nil {
4038
return err
4139
}

cli/cmd/cluster.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ You could also specify etcd/tarantool username and password with environment var
100100
101101
The priority of credentials:
102102
environment variables < command flags < URL credentials.
103-
`, float64(clustercmd.DefaultUriTimeout)/float64(time.Second),
103+
`, float64(libconnect.DefaultUriTimeout)/float64(time.Second),
104104
libconnect.EtcdUsernameEnv, libconnect.EtcdPasswordEnv,
105105
libconnect.TarantoolUsernameEnv, libconnect.TarantoolPasswordEnv)
106106
failoverUriHelp = fmt.Sprintf(
@@ -129,7 +129,7 @@ You could also specify etcd/tarantool username and password with environment var
129129
130130
The priority of credentials:
131131
environment variables < command flags < URL credentials.
132-
`, float64(clustercmd.DefaultUriTimeout)/float64(time.Second),
132+
`, float64(libconnect.DefaultUriTimeout)/float64(time.Second),
133133
libconnect.EtcdUsernameEnv, libconnect.EtcdPasswordEnv,
134134
libconnect.TarantoolUsernameEnv, libconnect.TarantoolPasswordEnv)
135135
)

lib/connect/etcd.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package connect
2+
3+
import (
4+
"fmt"
5+
"os"
6+
7+
libcluster "github.com/tarantool/tt/lib/cluster"
8+
clientv3 "go.etcd.io/etcd/client/v3"
9+
)
10+
11+
// EtcdFunc is a function that can be called on an `etcd` connection.
12+
type EtcdFunc func(*clientv3.Client) error
13+
14+
// makeEtcdOptsFromUriOpts create etcd connect options from URI options.
15+
func makeEtcdOptsFromUriOpts(src UriOpts) libcluster.EtcdOpts {
16+
var endpoints []string
17+
if src.Endpoint != "" {
18+
endpoints = []string{src.Endpoint}
19+
}
20+
21+
return libcluster.EtcdOpts{
22+
Endpoints: endpoints,
23+
Username: src.Username,
24+
Password: src.Password,
25+
KeyFile: src.KeyFile,
26+
CertFile: src.CertFile,
27+
CaPath: src.CaPath,
28+
CaFile: src.CaFile,
29+
SkipHostVerify: src.SkipHostVerify || src.SkipPeerVerify,
30+
Timeout: src.Timeout,
31+
}
32+
}
33+
34+
// connectEtcd establishes a connection to etcd.
35+
func connectEtcd(uriOpts UriOpts) (*clientv3.Client, error) {
36+
etcdOpts := makeEtcdOptsFromUriOpts(uriOpts)
37+
if etcdOpts.Username == "" && etcdOpts.Password == "" {
38+
if etcdOpts.Username == "" {
39+
etcdOpts.Username = os.Getenv(EtcdUsernameEnv)
40+
}
41+
if etcdOpts.Password == "" {
42+
etcdOpts.Password = os.Getenv(EtcdPasswordEnv)
43+
}
44+
}
45+
46+
c, err := libcluster.ConnectEtcd(etcdOpts)
47+
if err != nil {
48+
return nil, fmt.Errorf("failed to connect to etcd: %w", err)
49+
}
50+
return c, nil
51+
}
52+
53+
// RunOnEtcd runs the provided function with etcd connection.
54+
// Returns true if the function was executed.
55+
func RunOnEtcd(opts UriOpts, f EtcdFunc) (bool, error) {
56+
if f != nil {
57+
c, err := connectEtcd(opts)
58+
if err != nil {
59+
return false, fmt.Errorf("failed to establish a connection to etcd: %w", err)
60+
}
61+
return true, f(c)
62+
}
63+
return false, nil
64+
}

0 commit comments

Comments
 (0)