@@ -47,14 +47,19 @@ type tenantServer interface {
4747}
4848
4949// newServer creates a new cockroachDB server.
50- func newServer (t * testing.T , insecure bool ) testserver.TestServer {
50+ func newServer (t * testing.T , auth authMode ) testserver.TestServer {
5151 t .Helper ()
5252 var ts testserver.TestServer
5353 var err error
54- if insecure {
55- ts , err = testserver .NewTestServer ()
56- } else {
54+ switch auth {
55+ case authClientCert :
5756 ts , err = testserver .NewTestServer (testserver .SecureOpt ())
57+ case authPassword :
58+ ts , err = testserver .NewTestServer (testserver .SecureOpt (), testserver .RootPasswordOpt ("hunter2" ))
59+ case authInsecure :
60+ ts , err = testserver .NewTestServer ()
61+ default :
62+ err = fmt .Errorf ("unknown authMode %d" , auth )
5863 }
5964 if err != nil {
6065 t .Fatal (err )
@@ -64,9 +69,9 @@ func newServer(t *testing.T, insecure bool) testserver.TestServer {
6469
6570// newTenant creates a new SQL Tenant pointed at the given TestServer. See
6671// TestServer.NewTenantServer for more information.
67- func newTenant (t * testing.T , ts testserver.TestServer ) testserver.TestServer {
72+ func newTenant (t * testing.T , ts testserver.TestServer , proxy bool ) testserver.TestServer {
6873 t .Helper ()
69- tenant , err := ts .(tenantServer ).NewTenantServer (false /* proxy */ )
74+ tenant , err := ts .(tenantServer ).NewTenantServer (proxy )
7075 if err != nil {
7176 t .Fatal (err )
7277 }
@@ -193,17 +198,39 @@ var minRequiredVersionsByORMName = map[string]struct {
193198 },
194199}
195200
201+ type authMode byte
202+
203+ const (
204+ // Use client certs. When testing tenants, does not use the proxy (as the proxy does not support client certs).
205+ authClientCert authMode = iota
206+ // Use password auth. When testing tenants, tests through a proxy.
207+ authPassword
208+ // Use --insecure. When testing tenants, does not use the proxy (as the proxy does not support insecure connections).
209+ authInsecure
210+
211+ authModeSentinel // sentinel to iterate over all modes
212+ )
213+
214+ func (mode authMode ) String () string {
215+ switch mode {
216+ case authClientCert :
217+ return "client-cert"
218+ case authPassword :
219+ return "password"
220+ case authInsecure :
221+ return "insecure"
222+ default :
223+ return "unknown"
224+ }
225+ }
226+
196227type testInfo struct {
197228 language , orm string
198229 tableNames testTableNames // defaults to defaultTestTableNames
199230 columnNames testColumnNames // defaults to defaultTestColumnNames
200- // insecure is set if ORM does not handle secure servers (client certs).
201- // In that case, we start an insecure server (and don't test in tenant
202- // mode).
203- insecure bool
204231}
205232
206- func testORM (t * testing.T , info testInfo ) {
233+ func testORM (t * testing.T , info testInfo , auth authMode ) {
207234 if info .tableNames == (testTableNames {}) {
208235 info .tableNames = defaultTestTableNames
209236 }
@@ -222,7 +249,7 @@ func testORM(t *testing.T, info testInfo) {
222249 }
223250 var testCases []testCase
224251 {
225- ts := newServer (t , info . insecure )
252+ ts := newServer (t , auth )
226253 db , dbURL , stopDB := startServerWithApplication (t , ts , app )
227254 defer stopDB ()
228255
@@ -268,11 +295,19 @@ FROM
268295 t .Fatalf ("unable to read cluster version: %s" , err )
269296 }
270297 if tenantsSupported {
271- tenant := newTenant (t , ts )
298+ // Connect to the tenant through the SQL proxy, which is only supported
299+ // when using secure+password auth. (The proxy does not support client
300+ // certs or insecure connections).
301+ proxySupported := auth == authPassword
302+ name := "RegularTenant"
303+ if proxySupported {
304+ name += "ThroughProxy"
305+ }
306+ tenant := newTenant (t , ts , proxySupported )
272307 db , dbURL , stopDB := startServerWithApplication (t , tenant , app )
273308 defer stopDB ()
274309 testCases = append (testCases , testCase {
275- name : "RegularTenant" ,
310+ name : name ,
276311 db : db ,
277312 dbURL : dbURL ,
278313 })
@@ -368,62 +403,108 @@ FROM
368403 }
369404}
370405
406+ func testORMForAuthModesExcept (t * testing.T , info testInfo , skips map [authMode ]string /* mode -> reason */ ) {
407+ for auth := authMode (0 ); auth < authModeSentinel ; auth ++ {
408+ t .Run (fmt .Sprint (auth ), func (t * testing.T ) {
409+ if msg := skips [auth ]; msg != "" {
410+ t .Skip (msg )
411+ }
412+ testORM (t , info , auth )
413+ })
414+ }
415+ }
416+
417+ func nothingSkipped () map [authMode ]string { return nil }
418+
371419func TestGORM (t * testing.T ) {
372- testORM (t , testInfo {language : "go" , orm : "gorm" })
420+ testORMForAuthModesExcept (t , testInfo {language : "go" , orm : "gorm" }, nothingSkipped () )
373421}
374422
375423func TestGOPG (t * testing.T ) {
376- testORM (t , testInfo {
377- language : "go" ,
378- orm : "gopg" ,
379- // GoPG does not support client certs:
380- // https://github.com/go-pg/pg/blob/v10/options.go
381- // If we set up a secure deployment and went through the proxy, it would work (or should anyway), but only
382- // via the 'database' parameter; GoPG also does not support the 'options' parameter.
383- insecure : true ,
384- })
424+ testORMForAuthModesExcept (t ,
425+ testInfo {language : "go" , orm : "gopg" },
426+ map [authMode ]string {
427+ // https://github.com/go-pg/pg/blob/v10/options.go
428+ // If we set up a secure deployment and went through the proxy, it would work (or should anyway), but only
429+ // via the 'database' parameter; GoPG also does not support the 'options' parameter.
430+ //
431+ // pg: options other than 'sslmode', 'application_name' and 'connect_timeout' are not supported
432+ authClientCert : "GoPG does not support custom root cert" ,
433+ authPassword : "GoPG does not support custom root cert" ,
434+ })
385435}
386436
387437func TestHibernate (t * testing.T ) {
388- testORM (t , testInfo {
389- language : "java" ,
390- orm : "hibernate" ,
391- // Possibly does not unescape the path correctly:
392- // Caused by: java.io.FileNotFoundException:
393- // %2Ftmp%2Fcockroach-testserver913095208%2Fcerts%2Fca.crt (No such file or directory)
394- insecure : true ,
395- })
438+ testORMForAuthModesExcept (t ,
439+ testInfo {language : "java" , orm : "hibernate" },
440+ map [authMode ]string {
441+ // Driver does not unescape the path correctly:
442+ // Caused by: java.io.FileNotFoundException:
443+ // %2Ftmp%2Fcockroach-testserver913095208%2Fcerts%2Fca.crt (No such file or directory)
444+ //
445+ // Furthermore, if we preprocess the query string via
446+ //
447+ // tc.dbURL.RawQuery, err = url.QueryUnescape(tc.dbURL.RawQuery)
448+ //
449+ // then we run into
450+ // https://github.com/dbeaver/dbeaver/issues/1835
451+ // because hibernate expects the key in DER format, but it is PEM.
452+ authClientCert : "needs DER format and unescaped query string" ,
453+ // Doesn't seem to understand connection strings.
454+ // Caused by: java.net.UnknownHostException: root:hunter2@localhost
455+ authPassword : "needs custom setup for password support" ,
456+ },
457+ )
396458}
397459
398460func TestSequelize (t * testing.T ) {
399- testORM (t , testInfo {
400- language : "node" ,
401- orm : "sequelize" ,
402- // Requires bespoke code to actually use SSL, see:
403- // https://github.com/sequelize/sequelize/issues/10015
404- insecure : true ,
405- })
461+ testORMForAuthModesExcept (t ,
462+ testInfo {language : "node" , orm : "sequelize" },
463+ map [authMode ]string {
464+ // Requires bespoke code to actually use SSL, see:
465+ // https://github.com/sequelize/sequelize/issues/10015
466+ authClientCert : "needs custom SSL setup" ,
467+ authPassword : "needs custom SSL setup" ,
468+ },
469+ )
406470}
407471
408472func TestSQLAlchemy (t * testing.T ) {
409- testORM (t , testInfo {
410- language : "python" ,
411- orm : "sqlalchemy" ,
412- })
473+ testORMForAuthModesExcept (t , testInfo {language : "python" , orm : "sqlalchemy" }, nothingSkipped ())
413474}
414475
415476func TestDjango (t * testing.T ) {
416- testORM (t , testInfo {
477+ testORMForAuthModesExcept (t , testInfo {
417478 language : "python" ,
418479 orm : "django" ,
419480 tableNames : djangoTestTableNames ,
420481 columnNames : djangoTestColumnNames ,
421- // No support for client certs (at least not via the query string).
422- // psycopg2.OperationalError: fe_sendauth: no password supplied
423- insecure : true ,
424- })
482+ },
483+ map [authMode ]string {
484+ // No support for client certs (at least not via the query string).
485+ // psycopg2.OperationalError: fe_sendauth: no password supplied
486+ authClientCert : "client certs via query string unsupported" ,
487+ // Ditto,
488+ // psycopg2.OperationalError: fe_sendauth: no password supplied
489+ authPassword : "password via query string unsupported" ,
490+ },
491+ )
425492}
426493
494+ // TODO(rafiss): why can't I run the ActiveRecords tests manually
495+ // with this invocation?
496+ //
497+ // ./docker.sh make deps
498+ // ./docker.sh go test -v -run ActiveRecord ./testing
499+ //
500+ // It always fails opaquely like this (after some normal-looking output):
501+ //
502+ // => Run `rails server -h` for more startup options
503+ // Exiting
504+ // make: *** [Makefile:23: start] Error 1
505+ //
506+
427507func TestActiveRecord (t * testing.T ) {
428- testORM (t , testInfo {language : "ruby" , orm : "activerecord" })
508+ testORMForAuthModesExcept (t , testInfo {language : "ruby" , orm : "activerecord" }, nothingSkipped () )
429509}
510+
0 commit comments