Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/cockroachdb/examples-orms
go 1.13

require (
github.com/cockroachdb/cockroach-go/v2 v2.0.6
github.com/cockroachdb/cockroach-go/v2 v2.0.7
github.com/denisenkom/go-mssqldb v0.0.0-20200620013148-b91950f658ec // indirect
github.com/go-pg/pg/v9 v9.1.6
github.com/go-sql-driver/mysql v1.5.0 // indirect
Expand Down
7 changes: 5 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/cockroachdb/cockroach-go/v2 v2.0.6 h1:7JKIGouLq/hP+rxHIZ1emZ0J3AXPt8DE7yU/xw8JZPc=
github.com/cockroachdb/cockroach-go/v2 v2.0.6/go.mod h1:nkf7rUmgPdawp3EwRjXIumihI2AYg9usGNWbJ2hsJqI=
github.com/cockroachdb/cockroach-go/v2 v2.0.7-0.20200903114400-4bf206d91533 h1:FrTWuMCYMkwme38Us67F6VK5xSF1/iLlasSJR8EIhIg=
github.com/cockroachdb/cockroach-go/v2 v2.0.7-0.20200903114400-4bf206d91533/go.mod h1:nkf7rUmgPdawp3EwRjXIumihI2AYg9usGNWbJ2hsJqI=
github.com/cockroachdb/cockroach-go/v2 v2.0.7 h1:Xkv9PbnvBrZjxRdW7l6GLz76bxAE/xaro+SGTyLz6gQ=
github.com/cockroachdb/cockroach-go/v2 v2.0.7/go.mod h1:nkf7rUmgPdawp3EwRjXIumihI2AYg9usGNWbJ2hsJqI=
github.com/codemodus/kace v0.5.1 h1:4OCsBlE2c/rSJo375ggfnucv9eRzge/U5LrrOZd47HA=
github.com/codemodus/kace v0.5.1/go.mod h1:coddaHoX1ku1YFSe4Ip0mL9kQjJvKkzb9CfIdG1YR04=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
Expand Down Expand Up @@ -187,6 +189,7 @@ golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f h1:ESK9Jb5JOE+y4u+ozMQeXfMHwEHm6zVbaDQkeaj6wI4=
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down
180 changes: 130 additions & 50 deletions testing/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,19 @@ type tenantServer interface {
}

// newServer creates a new cockroachDB server.
func newServer(t *testing.T, insecure bool) testserver.TestServer {
func newServer(t *testing.T, auth authMode) testserver.TestServer {
t.Helper()
var ts testserver.TestServer
var err error
if insecure {
ts, err = testserver.NewTestServer()
} else {
switch auth {
case authClientCert:
ts, err = testserver.NewTestServer(testserver.SecureOpt())
case authPassword:
ts, err = testserver.NewTestServer(testserver.SecureOpt(), testserver.RootPasswordOpt("hunter2"))
case authInsecure:
ts, err = testserver.NewTestServer()
default:
err = fmt.Errorf("unknown authMode %d", auth)
}
if err != nil {
t.Fatal(err)
Expand All @@ -64,9 +69,9 @@ func newServer(t *testing.T, insecure bool) testserver.TestServer {

// newTenant creates a new SQL Tenant pointed at the given TestServer. See
// TestServer.NewTenantServer for more information.
func newTenant(t *testing.T, ts testserver.TestServer) testserver.TestServer {
func newTenant(t *testing.T, ts testserver.TestServer, proxy bool) testserver.TestServer {
t.Helper()
tenant, err := ts.(tenantServer).NewTenantServer(false /* proxy */)
tenant, err := ts.(tenantServer).NewTenantServer(proxy)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -193,17 +198,39 @@ var minRequiredVersionsByORMName = map[string]struct {
},
}

type authMode byte

const (
// Use client certs. When testing tenants, does not use the proxy (as the proxy does not support client certs).
authClientCert authMode = iota
// Use password auth. When testing tenants, tests through a proxy.
authPassword
// Use --insecure. When testing tenants, does not use the proxy (as the proxy does not support insecure connections).
authInsecure

authModeSentinel // sentinel to iterate over all modes
)

func (mode authMode) String() string {
switch mode {
case authClientCert:
return "client-cert"
case authPassword:
return "password"
case authInsecure:
return "insecure"
default:
return "unknown"
}
}

type testInfo struct {
language, orm string
tableNames testTableNames // defaults to defaultTestTableNames
columnNames testColumnNames // defaults to defaultTestColumnNames
// insecure is set if ORM does not handle secure servers (client certs).
// In that case, we start an insecure server (and don't test in tenant
// mode).
insecure bool
}

func testORM(t *testing.T, info testInfo) {
func testORM(t *testing.T, info testInfo, auth authMode) {
if info.tableNames == (testTableNames{}) {
info.tableNames = defaultTestTableNames
}
Expand All @@ -222,7 +249,7 @@ func testORM(t *testing.T, info testInfo) {
}
var testCases []testCase
{
ts := newServer(t, info.insecure)
ts := newServer(t, auth)
db, dbURL, stopDB := startServerWithApplication(t, ts, app)
defer stopDB()

Expand Down Expand Up @@ -268,11 +295,19 @@ FROM
t.Fatalf("unable to read cluster version: %s", err)
}
if tenantsSupported {
tenant := newTenant(t, ts)
// Connect to the tenant through the SQL proxy, which is only supported
// when using secure+password auth. (The proxy does not support client
// certs or insecure connections).
proxySupported := auth == authPassword
name := "RegularTenant"
if proxySupported {
name += "ThroughProxy"
}
tenant := newTenant(t, ts, proxySupported)
db, dbURL, stopDB := startServerWithApplication(t, tenant, app)
defer stopDB()
testCases = append(testCases, testCase{
name: "RegularTenant",
name: name,
db: db,
dbURL: dbURL,
})
Expand Down Expand Up @@ -368,66 +403,111 @@ FROM
}
}

func testORMForAuthModesExcept(t *testing.T, info testInfo, skips map[authMode]string /* mode -> reason */) {
for auth := authMode(0); auth < authModeSentinel; auth++ {
t.Run(fmt.Sprint(auth), func(t *testing.T) {
if msg := skips[auth]; msg != "" {
t.Skip(msg)
}
testORM(t, info, auth)
})
}
}

func nothingSkipped() map[authMode]string { return nil }

func TestGORM(t *testing.T) {
testORM(t, testInfo{language: "go", orm: "gorm"})
testORMForAuthModesExcept(t, testInfo{language: "go", orm: "gorm"}, nothingSkipped())
}

func TestGOPG(t *testing.T) {
testORM(t, testInfo{
language: "go",
orm: "gopg",
// GoPG does not support client certs:
// https://github.com/go-pg/pg/blob/v10/options.go
// If we set up a secure deployment and went through the proxy, it would work (or should anyway), but only
// via the 'database' parameter; GoPG also does not support the 'options' parameter.
insecure: true,
})
testORMForAuthModesExcept(t,
testInfo{language: "go", orm: "gopg"},
map[authMode]string{
// https://github.com/go-pg/pg/blob/v10/options.go
// If we set up a secure deployment and went through the proxy, it would work (or should anyway), but only
// via the 'database' parameter; GoPG also does not support the 'options' parameter.
//
// pg: options other than 'sslmode', 'application_name' and 'connect_timeout' are not supported
authClientCert: "GoPG does not support custom root cert",
authPassword: "GoPG does not support custom root cert",
})
}

func TestHibernate(t *testing.T) {
testORM(t, testInfo{
language: "java",
orm: "hibernate",
// Possibly does not unescape the path correctly:
// Caused by: java.io.FileNotFoundException:
// %2Ftmp%2Fcockroach-testserver913095208%2Fcerts%2Fca.crt (No such file or directory)
insecure: true,
})
testORMForAuthModesExcept(t,
testInfo{language: "java", orm: "hibernate"},
map[authMode]string{
// Driver does not unescape the path correctly:
// Caused by: java.io.FileNotFoundException:
// %2Ftmp%2Fcockroach-testserver913095208%2Fcerts%2Fca.crt (No such file or directory)
//
// Furthermore, if we preprocess the query string via
//
// tc.dbURL.RawQuery, err = url.QueryUnescape(tc.dbURL.RawQuery)
//
// then we run into
// https://github.com/dbeaver/dbeaver/issues/1835
// because hibernate expects the key in DER format, but it is PEM.
authClientCert: "needs DER format and unescaped query string",
// Doesn't seem to understand connection strings.
// Caused by: java.net.UnknownHostException: root:hunter2@localhost
authPassword: "needs custom setup for password support",
},
)
}

func TestSequelize(t *testing.T) {
testORM(t, testInfo{
language: "node",
orm: "sequelize",
// Requires bespoke code to actually use SSL, see:
// https://github.com/sequelize/sequelize/issues/10015
insecure: true,
})
testORMForAuthModesExcept(t,
testInfo{language: "node", orm: "sequelize"},
map[authMode]string{
// Requires bespoke code to actually use SSL, see:
// https://github.com/sequelize/sequelize/issues/10015
authClientCert: "needs custom SSL setup",
authPassword: "needs custom SSL setup",
},
)
}

func TestSQLAlchemy(t *testing.T) {
testORM(t, testInfo{
language: "python",
orm: "sqlalchemy",
})
testORMForAuthModesExcept(t, testInfo{language: "python", orm: "sqlalchemy"}, nothingSkipped())
}

func TestDjango(t *testing.T) {
testORM(t, testInfo{
testORMForAuthModesExcept(t, testInfo{
language: "python",
orm: "django",
tableNames: djangoTestTableNames,
columnNames: djangoTestColumnNames,
// No support for client certs (at least not via the query string).
// psycopg2.OperationalError: fe_sendauth: no password supplied
insecure: true,
})
},
map[authMode]string{
// No support for client certs (at least not via the query string).
// psycopg2.OperationalError: fe_sendauth: no password supplied
authClientCert: "client certs via query string unsupported",
// Ditto,
// psycopg2.OperationalError: fe_sendauth: no password supplied
authPassword: "password via query string unsupported",
},
)
}

// TODO(rafiss): why can't I run the ActiveRecords tests manually
// with this invocation?
//
// ./docker.sh make deps
// ./docker.sh go test -v -run ActiveRecord ./testing
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Weird.. mine also fails locally, but I get an error:

    main_test.go:296: Get "http://localhost:6543/ping/": dial tcp 127.0.0.1:6543: connect: connection refused
=== RUN   TestActiveRecord/SystemTenant/SecondRun
make: Entering directory '/home/rafiss/go/src/github.com/cockroachdb/examples-orms/ruby/activerecord'
panic: test timed out after 10m0s

rake aborted!
PG::ConnectionBad: could not connect to server: Connection refused
	Is the server running on host "localhost" (127.0.0.1) and accepting
	TCP/IP connections on port 34497?
could not connect to server: Cannot assign requested address
	Is the server running on host "localhost" (::1) and accepting
	TCP/IP connections on port 34497?

//
// It always fails opaquely like this (after some normal-looking output):
//
// => Run `rails server -h` for more startup options
// Exiting
// make: *** [Makefile:23: start] Error 1
//

func TestActiveRecord(t *testing.T) {
testORM(t, testInfo{language: "ruby", orm: "activerecord"})
testORMForAuthModesExcept(t, testInfo{language: "ruby", orm: "activerecord"}, nothingSkipped())
}

func TestActiveRecord4(t *testing.T) {
testORM(t, testInfo{language: "ruby", orm: "ar4"})
testORMForAuthModesExcept(t, testInfo{language: "ruby", orm: "ar4"}, nothingSkipped())
}