Skip to content

Commit 7a02afd

Browse files
mpenickpolandll
andauthored
CLI and README updates (#76)
Cleaned up CLI options (Astra-specific flags are prefixed with `--astra`). Updated the `README` to favor the use of Astra tokens over using the bundle because it's much easier for users. Also, * Logging wasn't working in `Session` and `Cluster` because it wasn't being set * Added new flag for `--num-conns` to increase the number of connection per pool. It's possible that it could increase performance in some scenarios, but users should be wary of increasing this value too much. * Added addition logging to the pool and cluster to aid in debugging outage testing Co-authored-by: Lorina Poland <[email protected]>
1 parent 4ec58ec commit 7a02afd

File tree

6 files changed

+82
-47
lines changed

6 files changed

+82
-47
lines changed

README.md

Lines changed: 46 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,6 @@
1919

2020
`cql-proxy` is designed to forward your application's CQL traffic to an appropriate database service. It listens on a local address and securely forwards that traffic.
2121

22-
**Warning**: `cql-proxy` in its current state works well, and you should give it a try. However, it is still under development, so things might break or change.
23-
24-
Please give it a try and let us know what you think!
2522
## When to use `cql-proxy`
2623

2724
The `cql-proxy` sidecar enables unsupported CQL drivers to work with [DataStax Astra][astra]. These drivers include both legacy DataStax [drivers] and community-maintained CQL drivers, such as the [gocql] driver and the [rust-driver].
@@ -39,32 +36,43 @@ $ ./cql-proxy -h
3936
Usage: cql-proxy
4037

4138
Flags:
42-
-h, --help Show context-sensitive help.
43-
-b, --bundle=STRING Path to secure connect bundle ($BUNDLE)
44-
-u, --username=STRING Username to use for authentication ($USERNAME)
45-
-p, --password=STRING Password to use for authentication ($PASSWORD)
46-
-c, --contact-points=CONTACT-POINTS,...
47-
Contact points for cluster. Ignored if using the bundle path
48-
option ($CONTACT_POINTS).
49-
-a, --bind=STRING Address to use to bind serve ($BIND)
50-
--debug Show debug logging ($DEBUG)
51-
--profiling Enable profiling ($PROFILING)
39+
-h, --help Show context-sensitive help.
40+
-b, --astra-bundle=STRING Path to secure connect bundle for an Astra database. Requires '--username' and '--password'. Ignored if using the token or contact points option
41+
($ASTRA_BUNDLE).
42+
-t, --astra-token=STRING Token used to authenticate to an Astra database. Requires '--astra-database-id'. Ignored if using the bundle path or contact points option
43+
($ASTRA_TOKEN).
44+
-i, --astra-database-id=STRING Database ID of the Astra database. Requires '--astra-token' ($ASTRA_DATABASE_ID)
45+
-c, --contact-points=CONTACT-POINTS,... Contact points for cluster. Ignored if using the bundle path or token option ($CONTACT_POINTS).
46+
-u, --username=STRING Username to use for authentication ($USERNAME)
47+
-p, --password=STRING Password to use for authentication ($PASSWORD)
48+
-r, --port=9042 Default port to use when connecting to cluster ($PORT)
49+
-n, --protocol-version="v4" Initial protocol version to use when connecting to the backend cluster (default: v4, options: v3, v4, v5, DSEv1, DSEv2) ($PROTOCOL_VERSION)
50+
-m, --max-protocol-version="v4" Max protocol version supported by the backend cluster (default: v4, options: v3, v4, v5, DSEv1, DSEv2) ($MAX_PROTOCOL_VERSION)
51+
-a, --bind=":9042" Address to use to bind server ($BIND)
52+
--debug Show debug logging ($DEBUG)
53+
--health-check Enable liveness and readiness checks ($HEALTH_CHECK)
54+
--http-bind=":8000" Address to use to bind HTTP server used for health checks ($HTTP_BIND)
55+
--heartbeat-interval=30s Interval between performing heartbeats to the cluster ($HEARTBEAT_INTERVAL)
56+
--idle-timeout=60s Duration between successful heartbeats before a connection to the cluster is considered unresponsive and closed ($IDLE_TIMEOUT)
57+
--readiness-timeout=30s Duration the proxy is unable to connect to the backend cluster before it is considered not ready ($READINESS_TIMEOUT)
58+
--num-conns=1 Number of connection to create to each node of the backend cluster ($NUM_CONNS)
5259
```
5360
54-
To pass configuration to `cql-proxy`, either command-line flags or environment variables can be used. Using the `docker` method as an example, the follwing samples show how username, password and bundle are defined with each method.
61+
To pass configuration to `cql-proxy`, either command-line flags or environment variables can be used. Using the `docker` method as an example, the following samples show how the token and database ID are defined with each method.
5562
### Using flags
5663
5764
```sh
58-
docker run -v <your-secure-connect-bundle.zip>:/tmp/scb.zip -p 9042:9042 \
65+
docker run -p 9042:9042 \
5966
--rm datastax/cql-proxy:v0.0.4 \
60-
--bundle /tmp/scb.zip --username <astra-client-id> --password <astra-client-secret>
67+
--astra-token <astra-token> --astra-database-id <astra-datbase-id>
6168
```
69+
6270
### Using environment variables
6371
6472
```sh
65-
docker run -v <your-secure-connect-bundle.zip>:/tmp/scb.zip -p 9042:9042 \
73+
docker run -p 9042:9042 \
6674
--rm datastax/cql-proxy:v0.0.4 \
67-
-e BUNDLE=/tmp/scb.zip -e USERNAME=<astra-client-id> -e PASSWORD=<astra-client-secret>
75+
-e ASTRA_TOKEN=<astra-token> -e ASTRA_DATABASE_ID=<astra-datbase-id>
6876
```
6977
## Getting started
7078
@@ -86,13 +94,20 @@ There are three methods for using `cql-proxy`:
8694
- [DataStax Astra][astra] cluster:
8795
8896
```sh
97+
./cql-proxy --astra-token <astra-token> --astra-database-id <astra-database-id>
98+
```
99+
100+
The `<astra-token>` can be generated using these [instructions]. The proxy also supports using the [Astra Secure Connect Bundle][bundle] along with a client ID and secret generated using these [instructions]:
101+
102+
```
89103
./cql-proxy --bundle <your-secure-connect-zip> \
90104
--username <astra-client-id> --password <astra-client-secret>
91105
```
106+
92107
- [Apache Cassandra][cassandra] cluster:
93108
94109
```sh
95-
./cql-proxy --contact-points <cluster node IPs or DNS names>
110+
./cql-proxy --contact-points <cluster node IPs or DNS names> [--username <username>] [--password <password>]
96111
```
97112
### Run a `cql-proxy` docker image
98113
@@ -101,18 +116,24 @@ There are three methods for using `cql-proxy`:
101116
- [DataStax Astra][astra] cluster:
102117
103118
```sh
104-
docker run -v <your-secure-connect-bundle.zip>:/tmp/scb.zip -p 9042:9042 \
119+
docker run -p 9042:9042 \
105120
datastax/cql-proxy:v0.0.4 \
106-
--bundle /tmp/scb.zip --username <astra-client-id> --password <astra-client-secret>
121+
--astra-token <astra-token> --astra-database-id <astra-database-id>
107122
```
108-
The `<astra-client-id>` and `<astra-client-secret>` can be generated using these [instructions].
109123
124+
The `<astra-token>` can be generated using these [instructions]. The proxy also supports using the [Astra Secure Connect Bundle][bundle], but it requires mounting the bundle to a volume in the container:
125+
126+
```sh
127+
docker run -v <your-secure-connect-bundle.zip>:/tmp/scb.zip -p 9042:9042 \
128+
--rm datastax/cql-proxy:v0.0.4 \
129+
--astra-bundle /tmp/scb.zip --username <astra-client-id> --password <astra-client-secret>
130+
```
110131
- [Apache Cassandra][cassandra] cluster:
111132
112133
```sh
113134
docker run -p 9042:9042 \
114135
datastax/cql-proxy:v0.0.4 \
115-
--contact-points <cluster node IPs or DNS names>
136+
--contact-points <cluster node IPs or DNS names> [--username <username>] [--password <password>]
116137
```
117138
If you wish to have the docker image removed after you are done with it, add `--rm` before the image name `datastax/cql-proxy:v0.0.4 `.
118139
@@ -128,7 +149,7 @@ Using Kubernetes with `cql-proxy` requires a number of steps:
128149
129150
```
130151
command: ["./cql-proxy"]
131-
args: ["--bundle=/tmp/scb.zip","--username=Client ID","--password=Client Secret"]
152+
args: ["--astra-bundle=/tmp/scb.zip","--username=Client ID","--password=Client Secret"]
132153
```
133154
134155
- Volume mounts: Modify `/tmp/` as a volume mount as required.
@@ -188,6 +209,6 @@ Using Kubernetes with `cql-proxy` requires a number of steps:
188209
[cassandra]: https://cassandra.apache.org/
189210
[dse]: https://www.datastax.com/products/datastax-enterprise
190211
[instructions]: https://docs.datastax.com/en/astra/docs/manage-application-tokens.html
191-
212+
[bundle]: https://docs.datastax.com/en/astra/docs/obtaining-database-credentials.html#_getting_your_secure_connect_bundle
192213
193214

astra/bundle.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,6 @@ import (
3232
"github.com/datastax/astra-client-go/v2/astra"
3333
)
3434

35-
const URL = "https://api.astra.datastax.com"
36-
3735
type Bundle struct {
3836
tlsConfig *tls.Config
3937
host string

proxy/proxy.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ func (p *Proxy) Listen(address string) error {
140140
ReconnectPolicy: p.config.ReconnectPolicy,
141141
HeartBeatInterval: p.config.HeartBeatInterval,
142142
IdleTimeout: p.config.IdleTimeout,
143+
Logger: p.logger,
143144
})
144145

145146
if err != nil {
@@ -167,6 +168,7 @@ func (p *Proxy) Listen(address string) error {
167168
HeartBeatInterval: p.config.HeartBeatInterval,
168169
IdleTimeout: p.config.IdleTimeout,
169170
PreparedCache: p.preparedCache,
171+
Logger: p.logger,
170172
})
171173

172174
if err != nil {
@@ -245,6 +247,7 @@ func (p *Proxy) maybeCreateSession(version primitive.ProtocolVersion, keyspace s
245247
Keyspace: keyspace,
246248
HeartBeatInterval: p.config.HeartBeatInterval,
247249
IdleTimeout: p.config.IdleTimeout,
250+
Logger: p.logger,
248251
})
249252
if err != nil {
250253
return nil, err

proxy/run.go

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,14 @@ const livenessPath = "/liveness"
3535
const readinessPath = "/readiness"
3636

3737
var cli struct {
38-
Bundle string `help:"Path to secure connect bundle" short:"b" env:"BUNDLE"`
38+
AstraBundle string `help:"Path to secure connect bundle for an Astra database. Requires '--username' and '--password'. Ignored if using the token or contact points option." short:"b" env:"ASTRA_BUNDLE"`
39+
AstraToken string `help:"Token used to authenticate to an Astra database. Requires '--astra-database-id'. Ignored if using the bundle path or contact points option." short:"t" env:"ASTRA_TOKEN"`
40+
AstraDatabaseID string `help:"Database ID of the Astra database. Requires '--astra-token'" short:"i" env:"ASTRA_DATABASE_ID"`
41+
AstraApiURL string `help:"URL for the Astra API" default:"https://api.astra.datastax.com" env:"ASTRA_API_URL"`
42+
ContactPoints []string `help:"Contact points for cluster. Ignored if using the bundle path or token option." short:"c" env:"CONTACT_POINTS"`
3943
Username string `help:"Username to use for authentication" short:"u" env:"USERNAME"`
4044
Password string `help:"Password to use for authentication" short:"p" env:"PASSWORD"`
41-
Token string `help:"Token" short:"t" env:"TOKEN"`
42-
DatabaseID string `help:"Database ID" short:"i" env:"DATABASE_ID"`
43-
ContactPoints []string `help:"Contact points for cluster. Ignored if using the bundle path option." short:"c" env:"CONTACT_POINTS"`
44-
Port int `help:"Default port to use when connecting to cluster" default:"9042" short:"t" env:"PORT"`
45+
Port int `help:"Default port to use when connecting to cluster" default:"9042" short:"r" env:"PORT"`
4546
ProtocolVersion string `help:"Initial protocol version to use when connecting to the backend cluster (default: v4, options: v3, v4, v5, DSEv1, DSEv2)" default:"v4" short:"n" env:"PROTOCOL_VERSION"`
4647
MaxProtocolVersion string `help:"Max protocol version supported by the backend cluster (default: v4, options: v3, v4, v5, DSEv1, DSEv2)" default:"v4" short:"m" env:"MAX_PROTOCOL_VERSION"`
4748
Bind string `help:"Address to use to bind server" short:"a" default:":9042" env:"BIND"`
@@ -51,6 +52,7 @@ var cli struct {
5152
HeartbeatInterval time.Duration `help:"Interval between performing heartbeats to the cluster" default:"30s" env:"HEARTBEAT_INTERVAL"`
5253
IdleTimeout time.Duration `help:"Duration between successful heartbeats before a connection to the cluster is considered unresponsive and closed" default:"60s" env:"IDLE_TIMEOUT"`
5354
ReadinessTimeout time.Duration `help:"Duration the proxy is unable to connect to the backend cluster before it is considered not ready" default:"30s" env:"READINESS_TIMEOUT"`
55+
NumConns int `help:"Number of connection to create to each node of the backend cluster" default:"1" env:"NUM_CONNS"`
5456
}
5557

5658
// Run starts the proxy command. 'args' shouldn't include the executable (i.e. os.Args[1:]). It returns the exit code
@@ -70,33 +72,39 @@ func Run(ctx context.Context, args []string) int {
7072
}
7173

7274
var resolver proxycore.EndpointResolver
73-
if len(cli.Bundle) > 0 {
74-
if bundle, err := astra.LoadBundleZipFromPath(cli.Bundle); err != nil {
75-
cliCtx.Errorf("unable to open bundle %s from file: %v", cli.Bundle, err)
75+
if len(cli.AstraBundle) > 0 {
76+
if bundle, err := astra.LoadBundleZipFromPath(cli.AstraBundle); err != nil {
77+
cliCtx.Errorf("unable to open bundle %s from file: %v", cli.AstraBundle, err)
7678
return 1
7779
} else {
7880
resolver = astra.NewResolver(bundle)
7981
}
80-
} else if len(cli.ContactPoints) > 0 {
81-
resolver = proxycore.NewResolverWithDefaultPort(cli.ContactPoints, cli.Port)
82-
} else if len(cli.Token) > 0 {
83-
if len(cli.DatabaseID) == 0 {
82+
} else if len(cli.AstraToken) > 0 {
83+
if len(cli.AstraDatabaseID) == 0 {
8484
cliCtx.Fatalf("database ID is required when using a token")
8585
}
86-
bundle, err := astra.LoadBundleZipFromURL(astra.URL, cli.DatabaseID, cli.Token, 10*time.Second)
86+
bundle, err := astra.LoadBundleZipFromURL(cli.AstraApiURL, cli.AstraDatabaseID, cli.AstraToken, 10*time.Second)
8787
if err != nil {
88-
cliCtx.Fatalf("unable to load bundle %s from astra: %v", cli.Bundle, err)
88+
cliCtx.Fatalf("unable to load bundle %s from astra: %v", cli.AstraBundle, err)
8989
}
9090
resolver = astra.NewResolver(bundle)
9191
cli.Username = "token"
92-
cli.Password = cli.Token
92+
cli.Password = cli.AstraToken
93+
} else if len(cli.ContactPoints) > 0 {
94+
resolver = proxycore.NewResolverWithDefaultPort(cli.ContactPoints, cli.Port)
9395
} else {
94-
cliCtx.Errorf("must provide either bundle path or contact points")
96+
cliCtx.Errorf("must provide either bundle path, token, or contact points")
9597
return 1
9698
}
9799

98100
if cli.HeartbeatInterval >= cli.IdleTimeout {
99-
cliCtx.Errorf("idle-timeout must be greater than heartbeat-interval")
101+
cliCtx.Errorf("idle-timeout must be greater than heartbeat-interval (heartbeat interval: %s, idle timeout: %s)",
102+
cli.HeartbeatInterval, cli.IdleTimeout)
103+
return 1
104+
}
105+
106+
if cli.NumConns < 1 {
107+
cliCtx.Errorf("invalid number of connections, must be greater than 0 (provided: %d)", cli.NumConns)
100108
return 1
101109
}
102110

@@ -140,7 +148,7 @@ func Run(ctx context.Context, args []string) int {
140148
MaxVersion: maxVersion,
141149
Resolver: resolver,
142150
ReconnectPolicy: proxycore.NewReconnectPolicy(),
143-
NumConns: 1,
151+
NumConns: cli.NumConns,
144152
Auth: auth,
145153
Logger: logger,
146154
HeartBeatInterval: cli.HeartbeatInterval,

proxycore/cluster.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,11 +259,13 @@ func (c *Cluster) mergeHosts(hosts []*Host) error {
259259
if _, ok := existing[key]; ok {
260260
delete(existing, key)
261261
} else {
262+
c.logger.Info("adding host to the cluster", zap.Stringer("host", host))
262263
c.sendEvent(&AddEvent{host})
263264
}
264265
}
265266

266267
for _, host := range existing {
268+
c.logger.Info("removing host from the cluster", zap.Stringer("host", host))
267269
c.sendEvent(&RemoveEvent{host})
268270
}
269271

proxycore/connpool.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,8 @@ func (p *connPool) stayConnected(idx int) {
183183
if conn == nil {
184184
if !pendingConnect {
185185
delay := reconnectPolicy.NextDelay()
186-
p.logger.Debug("pool connection attempting to reconnect after delay", zap.Duration("delay", delay))
186+
p.logger.Info("pool connection attempting to reconnect after delay",
187+
zap.Stringer("host", p.config.Endpoint), zap.Duration("delay", delay))
187188
connectTimer = time.NewTimer(reconnectPolicy.NextDelay())
188189
pendingConnect = true
189190
} else {
@@ -193,7 +194,8 @@ func (p *connPool) stayConnected(idx int) {
193194
case <-connectTimer.C:
194195
c, err := p.connect()
195196
if err != nil {
196-
p.logger.Error("pool failed to connect", zap.Stringer("host", p.config.Endpoint), zap.Error(err))
197+
p.logger.Error("pool connection failed to connect",
198+
zap.Stringer("host", p.config.Endpoint), zap.Error(err))
197199
} else {
198200
p.connsMu.Lock()
199201
conn, p.conns[idx] = c, c
@@ -209,6 +211,7 @@ func (p *connPool) stayConnected(idx int) {
209211
done = true
210212
_ = conn.Close()
211213
case <-conn.IsClosed():
214+
p.logger.Info("pool connection closed", zap.Stringer("host", p.config.Endpoint))
212215
p.connsMu.Lock()
213216
conn, p.conns[idx] = nil, nil
214217
p.connsMu.Unlock()

0 commit comments

Comments
 (0)