-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add cassandra driver, ci setup
- Loading branch information
1 parent
9196510
commit 2affc38
Showing
18 changed files
with
727 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
FROM godfish_test/client_base:latest | ||
LABEL driver=cassandra role=client | ||
|
||
WORKDIR /src | ||
RUN go build -v ./drivers/cassandra/godfish && \ | ||
go test -c . && go test -c ./drivers/cassandra | ||
|
||
# Alpine linux doesn't have a cassandra client. Build a golang binary to check | ||
# if server is ready and setup the test DB. Use it in the entrypoint. | ||
WORKDIR /src/.ci/cassandra | ||
RUN go build -v -o /client_setup_keyspace . | ||
COPY .ci/cassandra/client.sh / | ||
|
||
WORKDIR /src | ||
ENTRYPOINT /client.sh |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
#!/usr/bin/env sh | ||
|
||
set -eu | ||
|
||
dbhost="${1:?missing dbhost}" | ||
|
||
echo "building binary" | ||
make cassandra | ||
echo "testing godfish" | ||
make test ARGS='-v -count=1' | ||
|
||
# Wait for db server to be ready, with some limits. | ||
num_attempts=0 | ||
|
||
until /client_setup_keyspace "${dbhost}" godfish_test ; do | ||
num_attempts=$((num_attempts+1)) | ||
if [ $num_attempts -gt 12 ]; then | ||
>&2 echo "ERROR: max attempts exceeded" | ||
exit 1 | ||
fi | ||
|
||
>&2 echo "db is unavailable now, sleeping" | ||
sleep 5 | ||
done | ||
>&2 echo "db is up" | ||
|
||
echo "testing godfish against live db" | ||
make cassandra-test ARGS='-v -count=1' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// Alpine linux doesn't have a cassandra client. This command can be used by the | ||
// test environment to check if the server is ready, and when it is, prepare a | ||
// keyspace for the tests. | ||
package main | ||
|
||
import ( | ||
"log" | ||
"os" | ||
|
||
"github.com/gocql/gocql" | ||
) | ||
|
||
func init() { | ||
log.SetOutput(os.Stderr) | ||
} | ||
|
||
func main() { | ||
if len(os.Args) < 3 { | ||
log.Printf("requires 2 positional args; got %d; %#v\n", len(os.Args), os.Args) | ||
log.Fatalf("Usage: %s dbhost keyspace", os.Args[0]) | ||
} | ||
host, keyspace := os.Args[1], os.Args[2] | ||
|
||
err := setupKeyspace(host, keyspace) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
log.Println("ok") | ||
} | ||
|
||
func setupKeyspace(dbhost, keyspace string) error { | ||
cluster := gocql.NewCluster(dbhost) | ||
session, err := cluster.CreateSession() | ||
if err != nil { | ||
return err | ||
} | ||
defer session.Close() | ||
|
||
statement := `CREATE KEYSPACE IF NOT EXISTS ` + keyspace + ` WITH replication = {'class':'SimpleStrategy', 'replication_factor': 1}` | ||
return session.Query(statement).Exec() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
version: "3.9" | ||
|
||
services: | ||
client: | ||
image: godfish_test/cassandra/client:latest | ||
container_name: godfish_ci_cassandra_client | ||
depends_on: | ||
- server | ||
entrypoint: /client.sh server | ||
environment: | ||
CGO_ENABLED: 0 | ||
DB_DSN: "cassandra://server:9042/godfish_test?timeout_ms=2000&connect_timeout_ms=2000" | ||
tty: true | ||
server: | ||
image: godfish_test/cassandra/server:latest | ||
container_name: godfish_ci_cassandra_server | ||
expose: | ||
- "9042" | ||
volumes: | ||
- | ||
type: volume | ||
source: server | ||
target: /var/lib/cassandra | ||
volumes: | ||
server: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
FROM cassandra:latest | ||
LABEL driver=cassandra role=server | ||
|
||
# Tests run on a a single node, only need to expose the CQL listener port. | ||
EXPOSE 9042 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
name: cassandra | ||
on: [push, pull_request] | ||
jobs: | ||
all: | ||
runs-on: ubuntu-20.04 | ||
steps: | ||
- name: Checkout repo | ||
uses: actions/checkout@v1 | ||
- name: Build environment and run tests | ||
run: make -f Makefile.docker ci-cassandra-up | ||
- name: Teardown | ||
run: make -f Makefile.docker ci-cassandra-down |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# cassandra | ||
|
||
This `godfish.Driver` implementation has been tested against: | ||
|
||
- cassandra version 3.11.10 | ||
|
||
## Connecting | ||
|
||
Like other `godfish.Driver` implementations, you must specify an environment | ||
variable, `DB_DSN`, to connect to the DB. There does not seem to be a standard | ||
connection URI schema for cassandra, but nonetheless this library expects a | ||
`DB_DSN` value. The form is roughly: | ||
|
||
``` | ||
scheme://[userinfo@]host[,more,hosts]/keyspace[?query] | ||
``` | ||
|
||
It's parsed by `net/url.Parse` from the standard library and ends up making a | ||
`*gocql.ClusterConfig`. See the tests for working and non-working examples. | ||
|
||
### DSN Components | ||
|
||
- `scheme`: Required, the value can be something like `cassandra`, or really | ||
anything, followed by a `://`. If this is empty or malformed, then the parsing | ||
function gets confused, and may mix up the host and the keyspace. So, just to | ||
make all of this easier, it's best to put something here. | ||
- `userinfo`: Optionally specify username and password. | ||
- `host`: Required, IP address of DB server. | ||
- may also include a port, in the form: `ip_address:port` | ||
- optionally add `comma,delimited,hosts` | ||
- `keyspace`: Required, should be the first "path" in the DSN string. | ||
- `query`: Various options, represented as query string key value pairs. If any | ||
key is unspecified or the value is zero, then the corresponding field for | ||
`gocql.ClusterConfig` is set to its default. | ||
- `connect_timeout_ms`: Integer, milliseconds. Sets `gocql.ClusterConfig.ConnectTimeout`. | ||
- `protocol_version`: Integer. Sets `gocql.ClusterConfig.ProtoVersion`. | ||
- `timeout_ms`: Integer, milliseconds. Sets `gocql.ClusterConfig.Timeout`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
package cassandra | ||
|
||
import ( | ||
"regexp" | ||
"strings" | ||
|
||
"github.com/gocql/gocql" | ||
"github.com/rafaelespinoza/godfish" | ||
) | ||
|
||
// NewDriver creates a new cassandra driver. | ||
func NewDriver() godfish.Driver { return &driver{} } | ||
|
||
// driver implements the Driver interface for cassandra databases. | ||
type driver struct { | ||
connection *gocql.Session | ||
} | ||
|
||
func (d *driver) Name() string { return "cassandra" } | ||
func (d *driver) Connect(in string) (err error) { | ||
if d.connection != nil { | ||
return | ||
} | ||
|
||
cluster, err := newClusterConfig(in) | ||
if err != nil { | ||
return | ||
} | ||
conn, err := cluster.CreateSession() | ||
if err != nil { | ||
return | ||
} | ||
d.connection = conn | ||
return | ||
} | ||
|
||
func (d *driver) Close() (err error) { | ||
conn := d.connection | ||
if conn == nil { | ||
return | ||
} | ||
d.connection = nil | ||
conn.Close() | ||
return | ||
} | ||
|
||
var statementDelimiter = regexp.MustCompile(`;\s*\n`) | ||
|
||
func (d *driver) Execute(query string, args ...interface{}) (err error) { | ||
statements := statementDelimiter.Split(query, -1) | ||
if len(statements) < 1 { | ||
return | ||
} | ||
for _, q := range statements { | ||
if len(strings.TrimSpace(q)) < 1 { | ||
continue | ||
} | ||
err = d.connection.Query(q).Exec() | ||
if err != nil { | ||
return | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func (d *driver) CreateSchemaMigrationsTable() (err error) { | ||
err = d.connection.Query( | ||
`CREATE TABLE IF NOT EXISTS schema_migrations (migration_id TEXT PRIMARY KEY)`, | ||
).Exec() | ||
return | ||
} | ||
|
||
func (d *driver) AppliedVersions() (out godfish.AppliedVersions, err error) { | ||
query := d.connection.Query( | ||
`SELECT migration_id FROM schema_migrations`, | ||
) | ||
|
||
av := execAllAscending(query) | ||
|
||
ierr := av.err | ||
if ierr == nil { | ||
out = av | ||
return | ||
} | ||
|
||
// A cleaner approach may be to look for a specific error code. The most | ||
// specific error code from the gocql library I've encountered is 8704 (or | ||
// 0x2200 if using cqlsh). As far as I know, it just means "invalid". | ||
if strings.Contains(ierr.Error(), "unconfigured table") { | ||
err = godfish.ErrSchemaMigrationsDoesNotExist | ||
return | ||
} | ||
err = ierr | ||
return | ||
} | ||
|
||
func (d *driver) UpdateSchemaMigrations(dir godfish.Direction, version string) (err error) { | ||
conn := d.connection | ||
if dir == godfish.DirForward { | ||
err = conn.Query(` | ||
INSERT INTO schema_migrations (migration_id) | ||
VALUES (?)`, | ||
version, | ||
).Exec() | ||
} else { | ||
err = conn.Query(` | ||
DELETE FROM schema_migrations | ||
WHERE migration_id = ?`, | ||
version, | ||
).Exec() | ||
} | ||
return | ||
} |
Oops, something went wrong.