diff --git a/go.mod b/go.mod index 583f7bedd98..865a2b88d03 100644 --- a/go.mod +++ b/go.mod @@ -53,18 +53,13 @@ require ( github.com/cockroachdb/tools v0.0.0-20211112185054-642e51449b40 github.com/cockroachdb/ttycolor v0.0.0-20210902133924-c7d7dcdde4e8 github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd - github.com/containerd/containerd v1.5.4 github.com/dave/dst v0.24.0 - github.com/docker/distribution v2.7.1+incompatible - github.com/docker/docker v20.10.8+incompatible - github.com/docker/go-connections v0.4.0 github.com/dustin/go-humanize v1.0.0 github.com/edsrzf/mmap-go v1.0.0 github.com/elastic/gosigar v0.14.1 github.com/emicklei/dot v0.15.0 github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a github.com/fraugster/parquet-go v0.10.0 - github.com/fsnotify/fsnotify v1.5.1 github.com/getsentry/sentry-go v0.12.0 github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9 github.com/go-sql-driver/mysql v1.6.0 @@ -117,7 +112,6 @@ require ( github.com/mmatczuk/go_generics v0.0.0-20181212143635-0aaa050f9bab github.com/montanaflynn/stats v0.6.3 github.com/olekukonko/tablewriter v0.0.5-0.20200416053754-163badb3bac6 - github.com/opencontainers/image-spec v1.0.1 github.com/petermattis/goid v0.0.0-20211229010228-4d14c490ee36 github.com/pierrre/geohash v1.0.0 github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 @@ -173,7 +167,6 @@ require ( cloud.google.com/go/compute v1.2.0 // indirect cloud.google.com/go/iam v0.1.1 // indirect github.com/Azure/azure-pipeline-go v0.2.3 // indirect - github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest/adal v0.9.15 // indirect github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect @@ -183,7 +176,6 @@ require ( github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver v1.5.0 // indirect github.com/Masterminds/sprig v2.22.0+incompatible // indirect - github.com/Microsoft/go-winio v0.4.17 // indirect github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/abbot/go-http-auth v0.4.1-0.20181019201920-860ed7f246ff // indirect @@ -204,9 +196,9 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/dimchansky/utfbom v1.1.1 // indirect github.com/djherbis/atime v1.1.0 // indirect - github.com/docker/go-units v0.4.0 // indirect github.com/fatih/structs v1.1.0 // indirect github.com/felixge/httpsnoop v1.0.1 // indirect + github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-kit/log v0.1.0 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect @@ -272,14 +264,11 @@ require ( github.com/mitchellh/copystructure v1.0.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.4.1 // indirect - github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/morikuni/aec v1.0.0 // indirect github.com/mostynb/go-grpc-compression v1.1.12 // indirect github.com/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007 // indirect github.com/oklog/ulid v1.3.1 // indirect - github.com/opencontainers/go-digest v1.0.0 // indirect github.com/openzipkin/zipkin-go v0.2.5 // indirect github.com/pelletier/go-toml v1.9.3 // indirect github.com/pkg/errors v0.9.1 // indirect diff --git a/go.sum b/go.sum index 866507c7553..b16dc79578b 100644 --- a/go.sum +++ b/go.sum @@ -78,7 +78,6 @@ github.com/Azure/azure-sdk-for-go v57.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9mo github.com/Azure/azure-storage-blob-go v0.8.0/go.mod h1:lPI3aLPpuLTeUwh1sViKXFxwl2B6teiRqI0deQUvsw0= github.com/Azure/azure-storage-blob-go v0.14.0 h1:1BCg74AmVdYwO3dlKwtFU1V0wU2PZdREkXvAmZJRUlM= github.com/Azure/azure-storage-blob-go v0.14.0/go.mod h1:SMqIBi+SuiQH32bvyjngEewEeXoPfKMgWlBDaYf6fck= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= @@ -186,7 +185,6 @@ github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.4.17 h1:iT12IBVClFevaf8PuVyi3UmZOVh4OqnaLxDTW2O6j3w= github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= @@ -486,7 +484,6 @@ github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= -github.com/containerd/containerd v1.5.4 h1:uPF0og3ByFzDnaStfiQj3fVGTEtaSNyU+bW7GR/nqGA= github.com/containerd/containerd v1.5.4/go.mod h1:sx18RgvW6ABJ4iYUw7Q5x7bgFOAB9B6G7+yO0XBc4zw= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= @@ -566,7 +563,6 @@ github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKY github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/crossdock/crossdock-go v0.0.0-20160816171116-049aabb0122b/go.mod h1:v9FBN7gdVTpiD/+LZ7Po0UKvROyT87uLVxTHVky/dlQ= github.com/cyberdelia/go-metrics-graphite v0.0.0-20161219230853-39f87cc3b432/go.mod h1:xwIwAxMvYnVrGJPe2FKx5prTrnAjGOD8zvDOnxnrrkM= @@ -616,16 +612,13 @@ github.com/djherbis/atime v1.1.0/go.mod h1:28OF6Y8s3NQWwacXc5eZTsEsiMzp7LF8MbXE+ github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= @@ -1593,14 +1586,12 @@ github.com/mmatczuk/go_generics v0.0.0-20181212143635-0aaa050f9bab/go.mod h1:Fs8 github.com/mmcloughlin/geohash v0.9.0 h1:FihR004p/aE1Sju6gcVq5OLDqGcMnpBY+8moBqIsVOs= github.com/mmcloughlin/geohash v0.9.0/go.mod h1:oNZxQo5yWJh0eMQEP/8hwQuVx9Z9tjwFUqcTB1SmG0c= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= -github.com/moby/moby v20.10.6+incompatible h1:3wn5wW3KwjAv8Z36VHdbvaqvY273JiWUDFuudH0z5Vs= github.com/moby/moby v20.10.6+incompatible/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= -github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk= github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= @@ -1613,7 +1604,6 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/montanaflynn/stats v0.6.3 h1:F8446DrvIF5V5smZfZ8K9nrmmix0AFgevPdLruGOmzk= github.com/montanaflynn/stats v0.6.3/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= -github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mostynb/go-grpc-compression v1.1.4/go.mod h1:rUYnd4NcUyZqHgjjISqjBMa6qTxMFjFu5+fHLI3k3Ho= github.com/mostynb/go-grpc-compression v1.1.12 h1:kx3qBtR7GqfI92TJ06DYMCyWCeOkZo2JZyaK6LTRojQ= @@ -1676,10 +1666,8 @@ github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1 github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= @@ -2968,10 +2956,8 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C gorm.io/driver/postgres v1.0.8/go.mod h1:4eOzrI1MUfm6ObJU/UcmbXyiHSs8jSwH95G5P5dxcAg= gorm.io/gorm v1.20.12/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= gorm.io/gorm v1.21.4/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= -gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= -gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/pkg/acceptance/.gitignore b/pkg/acceptance/.gitignore deleted file mode 100644 index a5e7e9d4c3d..00000000000 --- a/pkg/acceptance/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -# Do not add environment-specific entries here (see the top-level .gitignore -# for reasoning and alternatives). -.localcluster* -*.backup -*.tfstate -# TODO(tschottdorf): looks unused. Removable? -.cluster.certs.* diff --git a/pkg/acceptance/adapter_test.go b/pkg/acceptance/adapter_test.go deleted file mode 100644 index de3722439b1..00000000000 --- a/pkg/acceptance/adapter_test.go +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2017 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -package acceptance - -import ( - "context" - "strings" - "testing" - - "github.com/cockroachdb/cockroach/pkg/testutils/skip" - "github.com/cockroachdb/cockroach/pkg/util/log" -) - -func TestDockerC(t *testing.T) { - s := log.Scope(t) - defer s.Close(t) - - ctx := context.Background() - t.Run("Success", func(t *testing.T) { - testDockerSuccess(ctx, t, "c", []string{ - "sh", "-c", - `cd /mnt/data/c && make test && ./test 'SELECT $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12'`, - }) - }) - t.Run("Fail", func(t *testing.T) { - testDockerFail(ctx, t, "c", []string{ - "sh", "-c", - `cd /mnt/data/c && make test && ./test 'SELECT 1'`, - }) - }) -} - -func TestDockerCSharp(t *testing.T) { - skip.WithIssue(t, 58218, "flaky test") - s := log.Scope(t) - defer s.Close(t) - - ctx := context.Background() - testDockerSuccess(ctx, t, "csharp", []string{"sh", "-c", "cd /mnt/data/csharp && dotnet run"}) - testDockerFail(ctx, t, "csharp", []string{"sh", "-c", "cd /mnt/data/csharp && dotnet notacommand"}) -} - -func TestDockerJava(t *testing.T) { - s := log.Scope(t) - defer s.Close(t) - - ctx := context.Background() - testDockerSuccess(ctx, t, "java", []string{"sh", "-c", "cd /mnt/data/java && mvn -o test"}) - testDockerFail(ctx, t, "java", []string{"sh", "-c", "cd /mnt/data/java && mvn -o foobar"}) -} - -func TestDockerElixir(t *testing.T) { - skip.IgnoreLint(t, "Elixir requires network to run, which can flake. When attempting to update this (#52341), the new Elixir version does not work with CRDB/TLS.") - - s := log.Scope(t) - defer s.Close(t) - - ctx := context.Background() - testDockerSuccess(ctx, t, "elixir", []string{"sh", "-c", "cd /mnt/data/elixir/test_crdb && mix local.hex --force && mix deps.get && psql -c 'CREATE DATABASE IF NOT EXISTS testdb' && mix test"}) - testDockerFail(ctx, t, "elixir", []string{"sh", "-c", "cd /mnt/data/elixir/test_crdb && mix local.hex --force && mix deps.get && mix thisshouldfail"}) -} - -func TestDockerNodeJS(t *testing.T) { - s := log.Scope(t) - defer s.Close(t) - - const nodeJS = ` - set -e - cd /mnt/data/node - - export SHOULD_FAIL=%v - # Get access to globally installed node modules. - export NODE_PATH=$NODE_PATH:/usr/lib/node - /usr/lib/node/.bin/mocha . - ` - - ctx := context.Background() - testDockerSuccess(ctx, t, "node.js", []string{"/bin/sh", "-c", strings.Replace(nodeJS, "%v", "", 1)}) - testDockerFail(ctx, t, "node.js", []string{"/bin/sh", "-c", strings.Replace(nodeJS, "%v", "fail", 1)}) -} - -func TestDockerPHP(t *testing.T) { - s := log.Scope(t) - defer s.Close(t) - - ctx := context.Background() - testDockerSuccess(ctx, t, "php", []string{"sh", "-c", "cd /mnt/data/php && php test.php 3"}) - testDockerFail(ctx, t, "php", []string{"sh", "-c", "cd /mnt/data/php && php test.php 1"}) -} - -func TestDockerPSQL(t *testing.T) { - s := log.Scope(t) - defer s.Close(t) - - ctx := context.Background() - testDockerSuccess(ctx, t, "psql", []string{"/mnt/data/psql/test-psql.sh"}) -} - -func TestDockerPython(t *testing.T) { - s := log.Scope(t) - defer s.Close(t) - - ctx := context.Background() - testDockerSuccess(ctx, t, "python", []string{"sh", "-c", "cd /mnt/data/python && python test.py 3"}) - testDockerFail(ctx, t, "python", []string{"sh", "-c", "cd /mnt/data/python && python test.py 2"}) -} - -func TestDockerRuby(t *testing.T) { - s := log.Scope(t) - defer s.Close(t) - - ctx := context.Background() - testDockerSuccess(ctx, t, "ruby", []string{"sh", "-c", "cd /mnt/data/ruby && ruby test.rb 3"}) - testDockerFail(ctx, t, "ruby", []string{"sh", "-c", "cd /mnt/data/ruby && ruby test.rb 1"}) -} diff --git a/pkg/acceptance/cli_test.go b/pkg/acceptance/cli_test.go deleted file mode 100644 index 788b5377d87..00000000000 --- a/pkg/acceptance/cli_test.go +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2016 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -package acceptance - -import ( - "context" - "fmt" - "path/filepath" - "strings" - "testing" - - "github.com/cockroachdb/cockroach/pkg/acceptance/cluster" - "github.com/cockroachdb/cockroach/pkg/build/bazel" - "github.com/cockroachdb/cockroach/pkg/security" - "github.com/cockroachdb/cockroach/pkg/testutils/skip" - "github.com/cockroachdb/cockroach/pkg/util/log" -) - -var cmdBase = []string{ - "/usr/bin/env", - "COCKROACH_SKIP_UPDATE_CHECK=1", - "COCKROACH_CRASH_REPORTS=", - "/bin/bash", - "-c", -} - -func TestDockerCLI(t *testing.T) { - s := log.Scope(t) - defer s.Close(t) - - containerConfig := defaultContainerConfig() - containerConfig.Cmd = []string{"stat", cluster.CockroachBinaryInContainer} - containerConfig.Env = []string{fmt.Sprintf("PGUSER=%s", security.RootUser)} - ctx := context.Background() - if err := testDockerOneShot(ctx, t, "cli_test", containerConfig); err != nil { - skip.IgnoreLintf(t, `TODO(dt): No binary in one-shot container, see #6086: %s`, err) - } - - testGlob := "../cli/interactive_tests/test*.tcl" - paths, err := filepath.Glob(testGlob) - if err != nil { - t.Fatal(err) - } - if len(paths) == 0 { - t.Fatalf("no testfiles found (%v)", testGlob) - } - - containerPath := "/go/src/github.com/cockroachdb/cockroach/cli/interactive_tests" - if bazel.BuiltWithBazel() { - containerPath = "/mnt/interactive_tests" - } - for _, p := range paths { - testFile := filepath.Base(p) - testPath := filepath.Join(containerPath, testFile) - if strings.Contains(testPath, "disabled") { - t.Logf("Skipping explicitly disabled test %s", testFile) - continue - } - t.Run(testFile, func(t *testing.T) { - log.Infof(ctx, "-- starting tests from: %s", testFile) - - // Symlink the logs directory to /logs, which is visible outside of the - // container and preserved if the test fails. (They don't write to /logs - // directly because they are often run manually outside of Docker, where - // /logs is unlikely to exist.) - cmd := "ln -s /logs logs" - - // We run the expect command using `bash -c "(expect ...)"`. - // - // We cannot run `expect` directly, nor `bash -c "expect ..."`, - // because both cause Expect to become the PID 1 process inside - // the container. On Unix, orphan processes need to be wait()ed - // upon by the PID 1 process when they terminate, lest they - // remain forever in the zombie state. Unfortunately, Expect - // does not contain code to do this. Bash does. - cmd += "; (expect" - if log.V(2) { - cmd = cmd + " -d" - } - cmd = cmd + " -f " + testPath + " " + cluster.CockroachBinaryInContainer + ")" - containerConfig.Cmd = append(cmdBase, cmd) - - if err := testDockerOneShot(ctx, t, "cli_test", containerConfig); err != nil { - t.Error(err) - } - }) - } -} - -// TestDockerUnixSocket verifies that CockroachDB initializes a unix -// socket useable by 'psql', even when the server runs insecurely. -// TODO(knz): Replace this with a roachtest when roachtest/roachprod -// know how to start secure clusters. -func TestDockerUnixSocket(t *testing.T) { - s := log.Scope(t) - defer s.Close(t) - - containerConfig := defaultContainerConfig() - containerConfig.Cmd = []string{"stat", cluster.CockroachBinaryInContainer} - ctx := context.Background() - - if err := testDockerOneShot(ctx, t, "cli_test", containerConfig); err != nil { - skip.IgnoreLintf(t, `TODO(dt): No binary in one-shot container, see #6086: %s`, err) - } - - containerConfig.Env = []string{fmt.Sprintf("PGUSER=%s", security.RootUser)} - containerConfig.Cmd = append(cmdBase, - "/mnt/data/psql/test-psql-unix.sh "+cluster.CockroachBinaryInContainer) - if err := testDockerOneShot(ctx, t, "unix_socket_test", containerConfig); err != nil { - t.Error(err) - } -} - -// TestSQLWithoutTLS verifies that CockroachDB can accept clients -// without a TLS handshake in secure mode. -// TODO(knz): Replace this with a roachtest when roachtest/roachprod -// know how to start secure clusters. -func TestSQLWithoutTLS(t *testing.T) { - s := log.Scope(t) - defer s.Close(t) - - containerConfig := defaultContainerConfig() - containerConfig.Cmd = []string{"stat", cluster.CockroachBinaryInContainer} - ctx := context.Background() - - if err := testDockerOneShot(ctx, t, "cli_test", containerConfig); err != nil { - skip.IgnoreLintf(t, `TODO(dt): No binary in one-shot container, see #6086: %s`, err) - } - - containerConfig.Env = []string{fmt.Sprintf("PGUSER=%s", security.RootUser)} - containerConfig.Cmd = append(cmdBase, - "/mnt/data/psql/test-psql-notls.sh "+cluster.CockroachBinaryInContainer) - if err := testDockerOneShot(ctx, t, "notls_secure_test", containerConfig); err != nil { - t.Error(err) - } -} diff --git a/pkg/acceptance/cluster/certs.go b/pkg/acceptance/cluster/certs.go deleted file mode 100644 index b0fca23dc16..00000000000 --- a/pkg/acceptance/cluster/certs.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2018 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -package cluster - -import ( - "context" - "fmt" - "os" - "os/exec" - "path/filepath" - "time" - - "github.com/cockroachdb/cockroach/pkg/roachpb" - "github.com/cockroachdb/cockroach/pkg/security" -) - -const certsDir = ".localcluster.certs" - -var absCertsDir string - -// keyLen is the length (in bits) of the generated TLS certs. -// -// This needs to be at least 2048 since the newer versions of openssl -// (used by some tests) produce an error 'ee key too small' for -// smaller values. -const keyLen = 2048 - -// AbsCertsDir returns the absolute path to the certificate directory. -func AbsCertsDir() string { - return absCertsDir -} - -// GenerateCerts generates CA and client certificates and private keys to be -// used with a cluster. It returns a function that will clean up the generated -// files. -func GenerateCerts(ctx context.Context) func() { - var err error - // docker-compose tests change their working directory, - // so they need to know the absolute path to the certificate directory. - absCertsDir, err = filepath.Abs(certsDir) - if err != nil { - panic(err) - } - maybePanic(os.RemoveAll(certsDir)) - - maybePanic(security.CreateCAPair( - certsDir, filepath.Join(certsDir, security.EmbeddedCAKey), - keyLen, 96*time.Hour, false, false)) - - // Root user. - // Scope root user to system tenant and tenant ID 5 which is what we use by default for acceptance - // tests. - userScopes := []roachpb.TenantID{roachpb.SystemTenantID, roachpb.MakeTenantID(5)} - maybePanic(security.CreateClientPair( - certsDir, filepath.Join(certsDir, security.EmbeddedCAKey), - keyLen, 48*time.Hour, false, security.RootUserName(), userScopes, true /* generate pk8 key */)) - - // Test user. - // Scope test user to system tenant and tenant ID 5 which is what we use by default for acceptance - // tests. - maybePanic(security.CreateClientPair( - certsDir, filepath.Join(certsDir, security.EmbeddedCAKey), - keyLen, 48*time.Hour, false, security.TestUserName(), userScopes, true /* generate pk8 key */)) - - // Certs for starting a cockroach server. Key size is from cli/cert.go:defaultKeySize. - maybePanic(security.CreateNodePair( - certsDir, filepath.Join(certsDir, security.EmbeddedCAKey), - keyLen, 48*time.Hour, false, []string{"localhost", "cockroach"})) - - // Store a copy of the client certificate and private key in a PKCS#12 - // bundle, which is the only format understood by Npgsql (.NET). - { - execCmd("openssl", "pkcs12", "-export", "-password", "pass:", - "-in", filepath.Join(certsDir, "client.root.crt"), - "-inkey", filepath.Join(certsDir, "client.root.key"), - "-out", filepath.Join(certsDir, "client.root.pk12")) - } - - return func() { _ = os.RemoveAll(certsDir) } -} - -// GenerateCerts is only called in a file protected by a build tag. Suppress the -// unused linter's warning. -var _ = GenerateCerts - -func execCmd(args ...string) { - cmd := exec.Command(args[0], args[1:]...) - if out, err := cmd.CombinedOutput(); err != nil { - panic(fmt.Sprintf("error: %s: %s\nout: %s\n", args, err, out)) - } -} diff --git a/pkg/acceptance/cluster/cluster.go b/pkg/acceptance/cluster/cluster.go deleted file mode 100644 index cbf828ce5cb..00000000000 --- a/pkg/acceptance/cluster/cluster.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2015 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -package cluster - -import ( - "context" - gosql "database/sql" - "net" - "testing" - - "github.com/cockroachdb/errors" -) - -// A Cluster is an abstraction away from a concrete cluster deployment (i.e. -// a local docker cluster, or an AWS-provisioned one). It exposes a shared -// set of methods for test-related manipulation. -type Cluster interface { - // NumNodes returns the number of nodes in the cluster, running or not. - NumNodes() int - // NewDB returns a sql.DB client for the given node. - NewDB(context.Context, int) (*gosql.DB, error) - // PGUrl returns a URL string for the given node postgres server. - PGUrl(context.Context, int) string - // InternalIP returns the address used for inter-node communication. - InternalIP(ctx context.Context, i int) net.IP - // Assert verifies that the cluster state is as expected (i.e. no unexpected - // restarts or node deaths occurred). Tests can call this periodically to - // ascertain cluster health. - Assert(context.Context, testing.TB) - // AssertAndStop performs the same test as Assert but then proceeds to - // dismantle the cluster. - AssertAndStop(context.Context, testing.TB) - // ExecCLI runs `./cockroach `, while filling in required flags such as - // --insecure, --certs-dir, --host. - // - // Returns stdout, stderr, and an error. - ExecCLI(ctx context.Context, i int, args []string) (string, string, error) - // Kill terminates the cockroach process running on the given node number. - // The given integer must be in the range [0,NumNodes()-1]. - Kill(context.Context, int) error - // Restart terminates the cockroach process running on the given node - // number, unless it is already stopped, and restarts it. - // The given integer must be in the range [0,NumNodes()-1]. - Restart(context.Context, int) error - // URL returns the HTTP(s) endpoint. - URL(context.Context, int) string - // Addr returns the host and port from the node in the format HOST:PORT. - Addr(ctx context.Context, i int, port string) string - // Hostname returns a node's hostname. - Hostname(i int) string -} - -// Consistent performs a replication consistency check on all the ranges -// in the cluster. It depends on a majority of the nodes being up, and does -// the check against the node at index i. -func Consistent(ctx context.Context, c Cluster, i int) error { - return errors.Errorf("Consistency checking is unimplmented and should be re-implemented using SQL") -} diff --git a/pkg/acceptance/cluster/docker.go b/pkg/acceptance/cluster/docker.go deleted file mode 100644 index 33abb281c51..00000000000 --- a/pkg/acceptance/cluster/docker.go +++ /dev/null @@ -1,453 +0,0 @@ -// Copyright 2015 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -package cluster - -import ( - "context" - "encoding/binary" - "fmt" - "io" - "math" - "net" - "net/url" - "os" - "os/user" - "path/filepath" - "strconv" - "strings" - "time" - - "github.com/cockroachdb/cockroach/pkg/util/contextutil" - "github.com/cockroachdb/cockroach/pkg/util/log" - "github.com/cockroachdb/cockroach/pkg/util/log/severity" - "github.com/cockroachdb/errors" - "github.com/cockroachdb/errors/oserror" - "github.com/docker/distribution/reference" - "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/filters" - "github.com/docker/docker/api/types/network" - "github.com/docker/docker/client" - "github.com/docker/docker/pkg/jsonmessage" - "github.com/docker/go-connections/nat" - isatty "github.com/mattn/go-isatty" - specs "github.com/opencontainers/image-spec/specs-go/v1" -) - -// Retrieve the IP address of docker itself. -func dockerIP() net.IP { - host := os.Getenv("DOCKER_HOST") - if host == "" { - host = client.DefaultDockerHost - } - u, err := url.Parse(host) - if err != nil { - panic(err) - } - if u.Scheme == "unix" { - return net.IPv4(127, 0, 0, 1) - } - h, _, err := net.SplitHostPort(u.Host) - if err != nil { - panic(err) - } - return net.ParseIP(h) -} - -// Container provides the programmatic interface for a single docker -// container. -type Container struct { - id string - name string - cluster *DockerCluster -} - -// Name returns the container's name. -func (c Container) Name() string { - return c.name -} - -func hasImage(ctx context.Context, l *DockerCluster, ref string) error { - distributionRef, err := reference.ParseNamed(ref) - if err != nil { - return err - } - path := reference.Path(distributionRef) - // Correct for random docker stupidity: - // - // https://github.com/moby/moby/blob/7248742/registry/service.go#L207:L215 - path = strings.TrimPrefix(path, "library/") - - images, err := l.client.ImageList(ctx, types.ImageListOptions{ - All: true, - Filters: filters.NewArgs( - filters.Arg("reference", path), - ), - }) - if err != nil { - return err - } - - tagged, ok := distributionRef.(reference.Tagged) - if !ok { - return errors.Errorf("untagged reference %s not permitted", ref) - } - - wanted := fmt.Sprintf("%s:%s", path, tagged.Tag()) - for _, image := range images { - for _, repoTag := range image.RepoTags { - // The Image.RepoTags field contains strings of the form :. - if repoTag == wanted { - return nil - } - } - } - var imageList []string - for _, image := range images { - for _, tag := range image.RepoTags { - imageList = append(imageList, "%s %s", tag, image.ID) - } - } - return errors.Errorf("%s not found in:\n%s", wanted, strings.Join(imageList, "\n")) -} - -func pullImage( - ctx context.Context, l *DockerCluster, ref string, options types.ImagePullOptions, -) error { - // HACK: on CircleCI, docker pulls the image on the first access from an - // acceptance test even though that image is already present. So we first - // check to see if our image is present in order to avoid this slowness. - if hasImage(ctx, l, ref) == nil { - log.Infof(ctx, "ImagePull %s already exists", ref) - return nil - } - - log.Infof(ctx, "ImagePull %s starting", ref) - defer log.Infof(ctx, "ImagePull %s complete", ref) - - rc, err := l.client.ImagePull(ctx, ref, options) - if err != nil { - return err - } - defer rc.Close() - out := os.Stderr - outFd := out.Fd() - isTerminal := isatty.IsTerminal(outFd) - - if err := jsonmessage.DisplayJSONMessagesStream(rc, out, outFd, isTerminal, nil); err != nil { - return err - } - if err := hasImage(ctx, l, ref); err != nil { - return errors.Wrapf(err, "pulled image %s but still don't have it", ref) - } - return nil -} - -// splitBindSpec splits a Docker bind specification into its host and container -// paths. -func splitBindSpec(bind string) (hostPath string, containerPath string) { - s := strings.SplitN(bind, ":", 2) - return s[0], s[1] -} - -// getNonRootContainerUser determines a non-root UID and GID to use in the -// container to minimize file ownership problems in bind mounts. It returns a -// UID:GID string suitable for use as the User field container.Config. -func getNonRootContainerUser() (string, error) { - // This number is Debian-specific, but for now all of our acceptance test - // containers are based on Debian. - // See: https://www.debian.org/doc/debian-policy/#uid-and-gid-classes - const minUnreservedID = 101 - user, err := user.Current() - if err != nil { - return "", err - } - uid, err := strconv.Atoi(user.Uid) - if err != nil { - return "", errors.Wrap(err, "looking up host UID") - } - if uid < minUnreservedID { - return "", fmt.Errorf("host UID %d in container's reserved UID space", uid) - } - gid, err := strconv.Atoi(user.Gid) - if err != nil { - return "", errors.Wrap(err, "looking up host GID") - } - if gid < minUnreservedID { - // If the GID is in the reserved space, silently upconvert to the known-good - // UID. We don't want to return an error because users on a macOS host - // typically have a GID in the reserved space, and this upconversion has - // been empirically verified to not cause ownership issues. - gid = uid - } - return fmt.Sprintf("%d:%d", uid, gid), nil -} - -// createContainer creates a new container using the specified -// options. Per the docker API, the created container is not running -// and must be started explicitly. Note that the passed-in hostConfig -// will be augmented with the necessary settings to use the network -// defined by l.createNetwork(). -func createContainer( - ctx context.Context, - l *DockerCluster, - containerConfig container.Config, - hostConfig container.HostConfig, - platformSpec specs.Platform, - containerName string, -) (*Container, error) { - hostConfig.NetworkMode = container.NetworkMode(l.networkID) - // Disable DNS search under the host machine's domain. This can - // catch upstream wildcard DNS matching and result in odd behavior. - hostConfig.DNSSearch = []string{"."} - - // Run the container as the current user to avoid creating root-owned files - // and directories from within the container. - user, err := getNonRootContainerUser() - if err != nil { - return nil, err - } - containerConfig.User = user - - // Additionally ensure that the host side of every bind exists. Otherwise, the - // Docker daemon will create the host directory as root before running the - // container. - for _, bind := range hostConfig.Binds { - hostPath, _ := splitBindSpec(bind) - if _, err := os.Stat(hostPath); oserror.IsNotExist(err) { - maybePanic(os.MkdirAll(hostPath, 0755)) - } else { - maybePanic(err) - } - } - - resp, err := l.client.ContainerCreate(ctx, &containerConfig, &hostConfig, nil, &platformSpec, containerName) - if err != nil { - return nil, err - } - return &Container{ - id: resp.ID, - name: containerName, - cluster: l, - }, nil -} - -func maybePanic(err error) { - if err != nil { - panic(err) - } -} - -// Remove removes the container from docker. It is an error to remove a running -// container. -func (c *Container) Remove(ctx context.Context) error { - return c.cluster.client.ContainerRemove(ctx, c.id, types.ContainerRemoveOptions{ - RemoveVolumes: true, - Force: true, - }) -} - -// Kill stops a running container, without removing it. -func (c *Container) Kill(ctx context.Context) error { - if err := c.cluster.client.ContainerKill(ctx, c.id, "9"); err != nil && !strings.Contains(err.Error(), "is not running") { - return err - } - c.cluster.expectEvent(c, eventDie) - return nil -} - -// Start starts a non-running container. -// -// TODO(pmattis): Generalize the setting of parameters here. -func (c *Container) Start(ctx context.Context) error { - return c.cluster.client.ContainerStart(ctx, c.id, types.ContainerStartOptions{}) -} - -// Restart restarts a running container. -// Container will be killed after 'timeout' seconds if it fails to stop. -func (c *Container) Restart(ctx context.Context, timeout *time.Duration) error { - var exp []string - if ci, err := c.Inspect(ctx); err != nil { - return err - } else if ci.State.Running { - exp = append(exp, eventDie) - } - if err := c.cluster.client.ContainerRestart(ctx, c.id, timeout); err != nil { - return err - } - c.cluster.expectEvent(c, append(exp, eventRestart)...) - return nil -} - -// Wait waits for a running container to exit. -func (c *Container) Wait(ctx context.Context, condition container.WaitCondition) error { - waitOKBodyCh, errCh := c.cluster.client.ContainerWait(ctx, c.id, condition) - select { - case err := <-errCh: - return err - case waitOKBody := <-waitOKBodyCh: - outputLog := filepath.Join(c.cluster.volumesDir, "logs", "console-output.log") - cmdLog, err := os.Create(outputLog) - if err != nil { - return err - } - defer cmdLog.Close() - - out := io.MultiWriter(cmdLog, os.Stderr) - if err := c.Logs(ctx, out); err != nil { - log.Warningf(ctx, "%v", err) - } - - if exitCode := waitOKBody.StatusCode; exitCode != 0 { - err = errors.Errorf("non-zero exit code: %d", exitCode) - fmt.Fprintln(out, err.Error()) - log.Shoutf(ctx, severity.INFO, "command left-over files in %s", c.cluster.volumesDir) - } - - return err - } -} - -// Logs outputs the containers logs to the given io.Writer. -func (c *Container) Logs(ctx context.Context, w io.Writer) error { - rc, err := c.cluster.client.ContainerLogs(ctx, c.id, types.ContainerLogsOptions{ - ShowStdout: true, - ShowStderr: true, - }) - if err != nil { - return err - } - defer rc.Close() - // The docker log output is not quite plaintext: each line has a - // prefix consisting of one byte file descriptor (stdout vs stderr), - // three bytes padding, four byte length. We could use this to - // disentangle stdout and stderr if we wanted to output them into - // separate streams, but we don't really care. - for { - var header uint64 - if err := binary.Read(rc, binary.BigEndian, &header); err == io.EOF { - break - } else if err != nil { - return err - } - size := header & math.MaxUint32 - if _, err := io.CopyN(w, rc, int64(size)); err != nil { - return err - } - } - return nil -} - -// Inspect retrieves detailed info about a container. -func (c *Container) Inspect(ctx context.Context) (types.ContainerJSON, error) { - return c.cluster.client.ContainerInspect(ctx, c.id) -} - -// Addr returns the TCP address to connect to. -func (c *Container) Addr(ctx context.Context, port nat.Port) *net.TCPAddr { - containerInfo, err := c.Inspect(ctx) - if err != nil { - log.Errorf(ctx, "%v", err) - return nil - } - bindings, ok := containerInfo.NetworkSettings.Ports[port] - if !ok || len(bindings) == 0 { - return nil - } - portNum, err := strconv.Atoi(bindings[0].HostPort) - if err != nil { - log.Errorf(ctx, "%v", err) - return nil - } - return &net.TCPAddr{ - IP: dockerIP(), - Port: portNum, - } -} - -// resilientDockerClient handles certain recoverable Docker usage errors. -// -// For example, `ContainerCreate` will fail if a container with the requested -// name already exists. resilientDockerClient will catch this, delete the -// existing container and try again. -type resilientDockerClient struct { - client.APIClient -} - -func (cli resilientDockerClient) ContainerStart( - clientCtx context.Context, id string, opts types.ContainerStartOptions, -) error { - for { - err := contextutil.RunWithTimeout(clientCtx, "start container", 20*time.Second, func(ctx context.Context) error { - return cli.APIClient.ContainerStart(ctx, id, opts) - }) - - // Keep going if ContainerStart timed out, but client's context is not - // expired. - if errors.Is(err, context.DeadlineExceeded) && clientCtx.Err() == nil { - log.Warningf(clientCtx, "ContainerStart timed out, retrying") - continue - } - return err - } -} - -func (cli resilientDockerClient) ContainerCreate( - ctx context.Context, - config *container.Config, - hostConfig *container.HostConfig, - networkingConfig *network.NetworkingConfig, - platformSpec *specs.Platform, - containerName string, -) (container.ContainerCreateCreatedBody, error) { - response, err := cli.APIClient.ContainerCreate( - ctx, config, hostConfig, networkingConfig, platformSpec, containerName, - ) - if err != nil && strings.Contains(err.Error(), "already in use") { - log.Infof(ctx, "unable to create container %s: %v", containerName, err) - containers, cerr := cli.ContainerList(ctx, types.ContainerListOptions{ - All: true, - Limit: -1, // no limit, see docker/docker/client/container_list.go - }) - if cerr != nil { - log.Infof(ctx, "unable to list containers: %v", cerr) - return container.ContainerCreateCreatedBody{}, err - } - for _, c := range containers { - for _, n := range c.Names { - // The container names begin with a "/". - n = strings.TrimPrefix(n, "/") - if n != containerName { - continue - } - log.Infof(ctx, "trying to remove %s", c.ID) - options := types.ContainerRemoveOptions{ - RemoveVolumes: true, - Force: true, - } - if rerr := cli.ContainerRemove(ctx, c.ID, options); rerr != nil { - log.Infof(ctx, "unable to remove container: %v", rerr) - return container.ContainerCreateCreatedBody{}, err - } - return cli.ContainerCreate(ctx, config, hostConfig, networkingConfig, platformSpec, containerName) - } - } - log.Warningf(ctx, "error indicated existing container %s, "+ - "but none found:\nerror: %s\ncontainers: %+v", - containerName, err, containers) - // We likely raced with a previous (late) removal of the container. - // Return a timeout so a higher level can retry and hopefully - // succeed (or get stuck in an infinite loop, at which point at - // least we'll have gathered an additional bit of information). - return response, context.DeadlineExceeded - } - return response, err -} diff --git a/pkg/acceptance/cluster/dockercluster.go b/pkg/acceptance/cluster/dockercluster.go deleted file mode 100644 index 36d51504899..00000000000 --- a/pkg/acceptance/cluster/dockercluster.go +++ /dev/null @@ -1,885 +0,0 @@ -// Copyright 2015 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -package cluster - -import ( - "bytes" - "context" - gosql "database/sql" - "encoding/json" - "flag" - "fmt" - "io" - "io/ioutil" - "net" - "net/url" - "os" - "path/filepath" - "strings" - "sync" - "testing" - "time" - - "github.com/cockroachdb/cockroach/pkg/base" - "github.com/cockroachdb/cockroach/pkg/config/zonepb" - "github.com/cockroachdb/cockroach/pkg/security" - "github.com/cockroachdb/cockroach/pkg/util/log" - "github.com/cockroachdb/cockroach/pkg/util/log/logflags" - "github.com/cockroachdb/cockroach/pkg/util/stop" - "github.com/cockroachdb/cockroach/pkg/util/syncutil" - "github.com/cockroachdb/cockroach/pkg/util/timeutil" - "github.com/cockroachdb/cockroach/pkg/util/uuid" - "github.com/cockroachdb/errors" - "github.com/cockroachdb/errors/oserror" - "github.com/containerd/containerd/platforms" - "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/events" - "github.com/docker/docker/api/types/filters" - "github.com/docker/docker/client" - "github.com/docker/docker/pkg/stdcopy" - "github.com/docker/go-connections/nat" - specs "github.com/opencontainers/image-spec/specs-go/v1" -) - -const ( - defaultImage = "docker.io/library/ubuntu:focal-20210119" - networkPrefix = "cockroachdb_acceptance" -) - -// DefaultTCP is the default SQL/RPC port specification. -const DefaultTCP nat.Port = base.DefaultPort + "/tcp" -const defaultHTTP nat.Port = base.DefaultHTTPPort + "/tcp" - -// CockroachBinaryInContainer is the container-side path to the CockroachDB -// binary. -const CockroachBinaryInContainer = "/cockroach/cockroach" - -var cockroachImage = flag.String("i", defaultImage, "the docker image to run") -var cockroachEntry = flag.String("e", "", "the entry point for the image") -var waitOnStop = flag.Bool("w", false, "wait for the user to interrupt before tearing down the cluster") -var maxRangeBytes = *zonepb.DefaultZoneConfig().RangeMaxBytes - -// CockroachBinary is the path to the host-side binary to use. -var CockroachBinary = flag.String("b", "", "the host-side binary to run") - -func exists(path string) bool { - if _, err := os.Stat(path); oserror.IsNotExist(err) { - return false - } - return true -} - -func nodeStr(l *DockerCluster, i int) string { - return fmt.Sprintf("roach-%s-%d", l.clusterID, i) -} - -func storeStr(node, store int) string { - return fmt.Sprintf("data%d.%d", node, store) -} - -// The various event types. -const ( - eventDie = "die" - eventRestart = "restart" -) - -// Event for a node containing a node index and the type of event. -type Event struct { - NodeIndex int - Status string -} - -type testStore struct { - index int - dir string - config StoreConfig -} - -type testNode struct { - *Container - index int - nodeStr string - config NodeConfig - stores []testStore -} - -// DockerCluster manages a local cockroach cluster running on docker. The -// cluster is composed of a "volumes" container which manages the -// persistent volumes used for certs and node data and N cockroach nodes. -type DockerCluster struct { - client client.APIClient - mu syncutil.Mutex // Protects the fields below - vols *Container - config TestConfig - Nodes []*testNode - events chan Event - expectedEvents chan Event - oneshot *Container - stopper *stop.Stopper - monitorCtx context.Context - monitorCtxCancelFunc func() - clusterID string - networkID string - networkName string - // Careful! volumesDir will be emptied during cluster cleanup. - volumesDir string -} - -// CreateDocker creates a Docker-based cockroach cluster. The stopper is used to -// gracefully shutdown the channel (e.g. when a signal arrives). The cluster -// must be started before being used and keeps container volumes in the -// specified volumesDir, including logs and cockroach stores. If volumesDir is -// empty, a temporary directory is created. -func CreateDocker( - ctx context.Context, cfg TestConfig, volumesDir string, stopper *stop.Stopper, -) *DockerCluster { - select { - case <-stopper.ShouldQuiesce(): - // The stopper was already closed, exit early. - os.Exit(1) - default: - } - - if *cockroachImage == defaultImage && !exists(*CockroachBinary) { - log.Fatalf(ctx, "\"%s\": does not exist", *CockroachBinary) - } - - cli, err := client.NewClientWithOpts(client.FromEnv) - maybePanic(err) - - cli.NegotiateAPIVersion(ctx) - - clusterID := uuid.MakeV4() - clusterIDS := clusterID.Short() - - if volumesDir == "" { - volumesDir, err = ioutil.TempDir("", fmt.Sprintf("cockroach-acceptance-%s", clusterIDS)) - maybePanic(err) - } else { - volumesDir = filepath.Join(volumesDir, clusterIDS) - } - if !filepath.IsAbs(volumesDir) { - pwd, err := os.Getwd() - maybePanic(err) - volumesDir = filepath.Join(pwd, volumesDir) - } - maybePanic(os.MkdirAll(volumesDir, 0755)) - log.Infof(ctx, "cluster volume directory: %s", volumesDir) - - return &DockerCluster{ - clusterID: clusterIDS, - client: resilientDockerClient{APIClient: cli}, - config: cfg, - stopper: stopper, - // TODO(tschottdorf): deadlocks will occur if these channels fill up. - events: make(chan Event, 1000), - expectedEvents: make(chan Event, 1000), - volumesDir: volumesDir, - } -} - -func (l *DockerCluster) expectEvent(c *Container, msgs ...string) { - for index, ctr := range l.Nodes { - if c.id != ctr.id { - continue - } - for _, status := range msgs { - select { - case l.expectedEvents <- Event{NodeIndex: index, Status: status}: - default: - panic("expectedEvents channel filled up") - } - } - break - } -} - -// OneShot runs a container, expecting it to successfully run to completion -// and die, after which it is removed. Not goroutine safe: only one OneShot -// can be running at once. -// Adds the same binds as the cluster containers (certs, binary, etc). -func (l *DockerCluster) OneShot( - ctx context.Context, - ref string, - ipo types.ImagePullOptions, - containerConfig container.Config, - hostConfig container.HostConfig, - platformSpec specs.Platform, - name string, -) error { - if err := pullImage(ctx, l, ref, ipo); err != nil { - return err - } - hostConfig.VolumesFrom = []string{l.vols.id} - c, err := createContainer(ctx, l, containerConfig, hostConfig, platformSpec, name) - if err != nil { - return err - } - l.oneshot = c - defer func() { - if err := l.oneshot.Remove(ctx); err != nil { - log.Errorf(ctx, "ContainerRemove: %s", err) - } - l.oneshot = nil - }() - - if err := l.oneshot.Start(ctx); err != nil { - return err - } - return l.oneshot.Wait(ctx, container.WaitConditionNotRunning) -} - -// stopOnPanic is invoked as a deferred function in Start in order to attempt -// to tear down the cluster if a panic occurs while starting it. If the panic -// was initiated by the stopper being closed (which panicOnStop notices) then -// the process is exited with a failure code. -func (l *DockerCluster) stopOnPanic(ctx context.Context) { - if r := recover(); r != nil { - l.stop(ctx) - if r != l { - panic(r) - } - os.Exit(1) - } -} - -// panicOnStop tests whether the stopper has been closed and panics if -// it has. This allows polling for whether to stop and avoids nasty locking -// complications with trying to call Stop at arbitrary points such as in the -// middle of creating a container. -func (l *DockerCluster) panicOnStop() { - if l.stopper == nil { - panic(l) - } - - select { - case <-l.stopper.IsStopped(): - l.stopper = nil - panic(l) - default: - } -} - -func (l *DockerCluster) createNetwork(ctx context.Context) { - l.panicOnStop() - - l.networkName = fmt.Sprintf("%s-%s", networkPrefix, l.clusterID) - log.Infof(ctx, "creating docker network with name: %s", l.networkName) - net, err := l.client.NetworkInspect(ctx, l.networkName, types.NetworkInspectOptions{}) - if err == nil { - // We need to destroy the network and any running containers inside of it. - for containerID := range net.Containers { - // This call could fail if the container terminated on its own after we call - // NetworkInspect, but the likelihood of this seems low. If this line creates - // a lot of panics we should do more careful error checking. - maybePanic(l.client.ContainerKill(ctx, containerID, "9")) - } - maybePanic(l.client.NetworkRemove(ctx, l.networkName)) - } else if !client.IsErrNotFound(err) { - panic(err) - } - - resp, err := l.client.NetworkCreate(ctx, l.networkName, types.NetworkCreate{ - Driver: "bridge", - // Docker gets very confused if two networks have the same name. - CheckDuplicate: true, - }) - maybePanic(err) - if resp.Warning != "" { - log.Warningf(ctx, "creating network: %s", resp.Warning) - } - l.networkID = resp.ID -} - -// create the volumes container that keeps all of the volumes used by -// the cluster. -func (l *DockerCluster) initCluster(ctx context.Context) { - configJSON, err := json.Marshal(l.config) - maybePanic(err) - log.Infof(ctx, "Initializing Cluster %s:\n%s", l.config.Name, configJSON) - l.panicOnStop() - - pwd, err := os.Getwd() - maybePanic(err) - - // Boot2docker's handling of binding local directories into the container is - // very confusing. If the directory being bound has a parent directory that - // exists in the boot2docker VM then that directory is bound into the - // container. In particular, that means that binds of /tmp and /var will be - // problematic. - binds := []string{ - filepath.Join(pwd, certsDir) + ":/certs", - filepath.Join(pwd, "..") + ":/go/src/github.com/cockroachdb/cockroach", - filepath.Join(l.volumesDir, "logs") + ":/logs", - } - - if *cockroachImage == defaultImage { - path, err := filepath.Abs(*CockroachBinary) - maybePanic(err) - binds = append(binds, path+":"+CockroachBinaryInContainer) - } - - l.Nodes = []*testNode{} - // Expand the cluster configuration into nodes and stores per node. - for i, nc := range l.config.Nodes { - newTestNode := &testNode{ - config: nc, - index: i, - nodeStr: nodeStr(l, i), - } - for j, sc := range nc.Stores { - hostDir := filepath.Join(l.volumesDir, storeStr(i, j)) - containerDir := "/" + storeStr(i, j) - binds = append(binds, hostDir+":"+containerDir) - newTestNode.stores = append(newTestNode.stores, - testStore{ - config: sc, - index: j, - dir: containerDir, - }) - } - l.Nodes = append(l.Nodes, newTestNode) - } - - if *cockroachImage == defaultImage { - maybePanic(pullImage(ctx, l, defaultImage, types.ImagePullOptions{})) - } - c, err := createContainer( - ctx, - l, - container.Config{ - Image: *cockroachImage, - Entrypoint: []string{"/bin/true"}, - }, container.HostConfig{ - Binds: binds, - PublishAllPorts: true, - }, - platforms.DefaultSpec(), - fmt.Sprintf("volumes-%s", l.clusterID), - ) - maybePanic(err) - // Make sure this assignment to l.vols is before the calls to Start and Wait. - // Otherwise, if they trigger maybePanic, this container won't get cleaned up - // and it'll get in the way of future runs. - l.vols = c - maybePanic(c.Start(ctx)) - maybePanic(c.Wait(ctx, container.WaitConditionNotRunning)) -} - -// cockroachEntryPoint returns the value to be used as -// container.Config.Entrypoint for a container running the cockroach -// binary under test. -// TODO(bdarnell): refactor this to minimize globals -func cockroachEntrypoint() []string { - var entrypoint []string - if *cockroachImage == defaultImage { - entrypoint = append(entrypoint, CockroachBinaryInContainer) - } else if *cockroachEntry != "" { - entrypoint = append(entrypoint, *cockroachEntry) - } - return entrypoint -} - -// createRoach creates the docker container for a testNode. It may be called in -// parallel to start many nodes at once, and thus should remain threadsafe. -func (l *DockerCluster) createRoach( - ctx context.Context, node *testNode, vols *Container, env []string, cmd ...string, -) { - l.panicOnStop() - - hostConfig := container.HostConfig{ - PublishAllPorts: true, - NetworkMode: container.NetworkMode(l.networkID), - } - - if vols != nil { - hostConfig.VolumesFrom = append(hostConfig.VolumesFrom, vols.id) - } - - var hostname string - if node.index >= 0 { - hostname = fmt.Sprintf("roach-%s-%d", l.clusterID, node.index) - } - log.Infof(ctx, "creating docker container with name: %s", hostname) - var err error - node.Container, err = createContainer( - ctx, - l, - container.Config{ - Hostname: hostname, - Image: *cockroachImage, - ExposedPorts: map[nat.Port]struct{}{ - DefaultTCP: {}, - defaultHTTP: {}, - }, - Entrypoint: cockroachEntrypoint(), - Env: env, - Cmd: cmd, - Labels: map[string]string{ - // Allow for `docker ps --filter label=Hostname=roach--0` or `--filter label=Roach`. - "Hostname": hostname, - "Roach": "", - "Acceptance-cluster-id": l.clusterID, - }, - }, - hostConfig, - platforms.DefaultSpec(), - node.nodeStr, - ) - maybePanic(err) -} - -func (l *DockerCluster) createNodeCerts() { - // TODO(mjibson): Including "cockroach" here to mimic - // GenerateCerts. Currently when this function is called it overwrites - // the node.crt and node.key files generated by that function. Until - // this is correctly fixed, make the certs generated here at least the - // same as those. - nodes := []string{"localhost", "cockroach", dockerIP().String()} - for _, node := range l.Nodes { - nodes = append(nodes, node.nodeStr) - } - maybePanic(security.CreateNodePair( - certsDir, - filepath.Join(certsDir, security.EmbeddedCAKey), - keyLen, 48*time.Hour, true /* overwrite */, nodes)) -} - -// startNode starts a Docker container to run testNode. It may be called in -// parallel to start many nodes at once, and thus should remain threadsafe. -func (l *DockerCluster) startNode(ctx context.Context, node *testNode) { - cmd := []string{ - "start", - "--certs-dir=/certs/", - "--listen-addr=" + node.nodeStr, - "--vmodule=*=1", - } - - // Forward the vmodule flag to the nodes. - vmoduleFlag := flag.Lookup(logflags.VModuleName) - if vmoduleFlag.Value.String() != "" { - cmd = append(cmd, fmt.Sprintf("--%s=%s", vmoduleFlag.Name, vmoduleFlag.Value.String())) - } - - for _, store := range node.stores { - storeSpec := base.StoreSpec{ - Path: store.dir, - Size: base.SizeSpec{InBytes: int64(store.config.MaxRanges) * maxRangeBytes}, - } - cmd = append(cmd, fmt.Sprintf("--store=%s", storeSpec)) - } - // Append --join flag for all nodes. - firstNodeAddr := l.Nodes[0].nodeStr - cmd = append(cmd, "--join="+net.JoinHostPort(firstNodeAddr, base.DefaultPort)) - - dockerLogDir := "/logs/" + node.nodeStr - localLogDir := filepath.Join(l.volumesDir, "logs", node.nodeStr) - cmd = append( - cmd, - "--logtostderr=ERROR", - "--log-dir="+dockerLogDir) - env := []string{ - "COCKROACH_SCAN_MAX_IDLE_TIME=200ms", - "COCKROACH_SKIP_UPDATE_CHECK=1", - "COCKROACH_CRASH_REPORTS=", - } - l.createRoach(ctx, node, l.vols, env, cmd...) - maybePanic(node.Start(ctx)) - httpAddr := node.Addr(ctx, defaultHTTP) - - log.Infof(ctx, `*** started %[1]s *** - ui: %[2]s - trace: %[2]s/debug/requests - logs: %[3]s/cockroach.INFO - pprof: docker exec -it %[4]s pprof https+insecure://$(hostname):%[5]s/debug/pprof/heap - cockroach: %[6]s - - cli-env: COCKROACH_INSECURE=false COCKROACH_CERTS_DIR=%[7]s COCKROACH_HOST=%s:%d`, - node.Name(), "https://"+httpAddr.String(), localLogDir, node.Container.id[:5], - base.DefaultHTTPPort, cmd, certsDir, httpAddr.IP, httpAddr.Port) -} - -// RunInitCommand runs the `cockroach init` command. Normally called -// automatically, but exposed for tests that use INIT_NONE. nodeIdx -// may designate any node in the cluster as the target of the command. -func (l *DockerCluster) RunInitCommand(ctx context.Context, nodeIdx int) { - containerConfig := container.Config{ - Image: *cockroachImage, - Entrypoint: cockroachEntrypoint(), - Cmd: []string{ - "init", - "--certs-dir=/certs/", - "--host=" + l.Nodes[nodeIdx].nodeStr, - "--log-dir=/logs/init-command", - "--logtostderr=NONE", - }, - } - - log.Infof(ctx, "trying to initialize via %v", containerConfig.Cmd) - maybePanic(l.OneShot(ctx, defaultImage, types.ImagePullOptions{}, - containerConfig, container.HostConfig{}, platforms.DefaultSpec(), "init-command")) - log.Info(ctx, "cluster successfully initialized") -} - -// returns false is the event -func (l *DockerCluster) processEvent(ctx context.Context, event events.Message) bool { - l.mu.Lock() - defer l.mu.Unlock() - - // Logging everything we get from Docker in service of finding the root - // cause of #58955. - log.Infof(ctx, "processing event from Docker: %+v", event) - - // If there's currently a oneshot container, ignore any die messages from - // it because those are expected. - if l.oneshot != nil && event.ID == l.oneshot.id && event.Status == eventDie { - return true - } - - for i, n := range l.Nodes { - if n != nil && n.id == event.ID { - if log.V(1) { - log.Errorf(ctx, "node=%d status=%s", i, event.Status) - } - select { - case l.events <- Event{NodeIndex: i, Status: event.Status}: - default: - panic("events channel filled up") - } - return true - } - } - - log.Infof(ctx, "received docker event for unrecognized container: %+v", - event) - - // An event on any other container is unexpected. Die. - select { - case <-l.stopper.ShouldQuiesce(): - case <-l.monitorCtx.Done(): - default: - // There is a very tiny race here: the signal handler might be closing the - // stopper simultaneously. - log.Errorf(ctx, "stopping due to unexpected event: %+v", event) - if rc, err := l.client.ContainerLogs(context.Background(), event.Actor.ID, types.ContainerLogsOptions{ - ShowStdout: true, - ShowStderr: true, - }); err == nil { - defer rc.Close() - if _, err := io.Copy(os.Stderr, rc); err != nil { - log.Infof(ctx, "error listing logs: %s", err) - } - } - } - return false -} - -func (l *DockerCluster) monitor(ctx context.Context) { - if log.V(1) { - log.Infof(ctx, "events monitor starts") - defer log.Infof(ctx, "events monitor exits") - } - longPoll := func() bool { - // If our context was canceled, it's time to go home. - if l.monitorCtx.Err() != nil { - return false - } - - eventq, errq := l.client.Events(l.monitorCtx, types.EventsOptions{ - Filters: filters.NewArgs( - filters.Arg("label", "Acceptance-cluster-id="+l.clusterID), - ), - }) - for { - select { - case err := <-errq: - log.Infof(ctx, "event stream done, resetting...: %s", err) - // Sometimes we get a random string-wrapped EOF error back. - // Hard to assert on, so we just let this goroutine spin. - return true - case event := <-eventq: - // Currently, the only events generated (and asserted against) are "die" - // and "restart", to maximize compatibility across different versions of - // Docker. - switch event.Status { - case eventDie, eventRestart: - if !l.processEvent(ctx, event) { - return false - } - } - } - } - } - - for longPoll() { - } -} - -// Start starts the cluster. -func (l *DockerCluster) Start(ctx context.Context) { - defer l.stopOnPanic(ctx) - - l.mu.Lock() - defer l.mu.Unlock() - - l.createNetwork(ctx) - l.initCluster(ctx) - log.Infof(ctx, "creating node certs (%dbit) in: %s", keyLen, certsDir) - l.createNodeCerts() - - log.Infof(ctx, "starting %d nodes", len(l.Nodes)) - l.monitorCtx, l.monitorCtxCancelFunc = context.WithCancel(context.Background()) - go l.monitor(ctx) - var wg sync.WaitGroup - wg.Add(len(l.Nodes)) - for _, node := range l.Nodes { - go func(node *testNode) { - defer wg.Done() - l.startNode(ctx, node) - }(node) - } - wg.Wait() - - if l.config.InitMode == INIT_COMMAND && len(l.Nodes) > 0 { - l.RunInitCommand(ctx, 0) - } -} - -// Assert drains the Events channel and compares the actual events with those -// expected to have been generated by the operations performed on the nodes in -// the cluster (restart, kill, ...). In the event of a mismatch, the passed -// Tester receives a fatal error. -func (l *DockerCluster) Assert(ctx context.Context, t testing.TB) { - const almostZero = 50 * time.Millisecond - filter := func(ch chan Event, wait time.Duration) *Event { - select { - case act := <-ch: - return &act - case <-time.After(wait): - } - return nil - } - - var events []Event - for { - exp := filter(l.expectedEvents, almostZero) - if exp == nil { - break - } - act := filter(l.events, 15*time.Second) - if act == nil || *exp != *act { - t.Fatalf("expected event %v, got %v (after %v)", exp, act, events) - } - events = append(events, *exp) - } - if cur := filter(l.events, almostZero); cur != nil { - t.Fatalf("unexpected extra event %v (after %v)", cur, events) - } - if log.V(2) { - log.Infof(ctx, "asserted %v", events) - } -} - -// AssertAndStop calls Assert and then stops the cluster. It is safe to stop -// the cluster multiple times. -func (l *DockerCluster) AssertAndStop(ctx context.Context, t testing.TB) { - defer l.stop(ctx) - l.Assert(ctx, t) -} - -// stop stops the cluster. -func (l *DockerCluster) stop(ctx context.Context) { - if *waitOnStop { - log.Infof(ctx, "waiting for interrupt") - <-l.stopper.ShouldQuiesce() - } - - log.Infof(ctx, "stopping") - - l.mu.Lock() - defer l.mu.Unlock() - - if l.monitorCtxCancelFunc != nil { - l.monitorCtxCancelFunc() - l.monitorCtxCancelFunc = nil - } - - if l.vols != nil { - maybePanic(l.vols.Kill(ctx)) - maybePanic(l.vols.Remove(ctx)) - l.vols = nil - } - for i, n := range l.Nodes { - if n.Container == nil { - continue - } - ci, err := n.Inspect(ctx) - crashed := err != nil || (!ci.State.Running && ci.State.ExitCode != 0) - maybePanic(n.Kill(ctx)) - // TODO(bdarnell): make these filenames more consistent with structured - // logs? - file := filepath.Join(l.volumesDir, "logs", nodeStr(l, i), - fmt.Sprintf("stderr.%s.log", strings.Replace( - timeutil.Now().Format(time.RFC3339), ":", "_", -1))) - maybePanic(os.MkdirAll(filepath.Dir(file), 0755)) - w, err := os.Create(file) - maybePanic(err) - defer w.Close() - maybePanic(n.Logs(ctx, w)) - log.Infof(ctx, "node %d: stderr at %s", i, file) - if crashed { - log.Infof(ctx, "~~~ node %d CRASHED ~~~~", i) - } - maybePanic(n.Remove(ctx)) - } - l.Nodes = nil - - if l.networkID != "" { - maybePanic( - l.client.NetworkRemove(ctx, l.networkID)) - l.networkID = "" - l.networkName = "" - } -} - -// NewDB implements the Cluster interface. -func (l *DockerCluster) NewDB(ctx context.Context, i int) (*gosql.DB, error) { - return gosql.Open("postgres", l.PGUrl(ctx, i)) -} - -// InternalIP returns the IP address used for inter-node communication. -func (l *DockerCluster) InternalIP(ctx context.Context, i int) net.IP { - c := l.Nodes[i] - containerInfo, err := c.Inspect(ctx) - if err != nil { - return nil - } - return net.ParseIP(containerInfo.NetworkSettings.Networks[l.networkName].IPAddress) -} - -// PGUrl returns a URL string for the given node postgres server. -func (l *DockerCluster) PGUrl(ctx context.Context, i int) string { - certUser := security.RootUser - options := url.Values{} - options.Add("sslmode", "verify-full") - options.Add("sslcert", filepath.Join(certsDir, security.EmbeddedRootCert)) - options.Add("sslkey", filepath.Join(certsDir, security.EmbeddedRootKey)) - options.Add("sslrootcert", filepath.Join(certsDir, security.EmbeddedCACert)) - pgURL := url.URL{ - Scheme: "postgres", - User: url.User(certUser), - Host: l.Nodes[i].Addr(ctx, DefaultTCP).String(), - RawQuery: options.Encode(), - } - return pgURL.String() -} - -// NumNodes returns the number of nodes in the cluster. -func (l *DockerCluster) NumNodes() int { - return len(l.Nodes) -} - -// Kill kills the i-th node. -func (l *DockerCluster) Kill(ctx context.Context, i int) error { - if err := l.Nodes[i].Kill(ctx); err != nil { - return errors.Wrapf(err, "failed to kill node %d", i) - } - return nil -} - -// Restart restarts the given node. If the node isn't running, this starts it. -func (l *DockerCluster) Restart(ctx context.Context, i int) error { - // The default timeout is 10 seconds. - if err := l.Nodes[i].Restart(ctx, nil); err != nil { - return errors.Wrapf(err, "failed to restart node %d", i) - } - return nil -} - -// URL returns the base url. -func (l *DockerCluster) URL(ctx context.Context, i int) string { - return "https://" + l.Nodes[i].Addr(ctx, defaultHTTP).String() -} - -// Addr returns the host and port from the node in the format HOST:PORT. -func (l *DockerCluster) Addr(ctx context.Context, i int, port string) string { - return l.Nodes[i].Addr(ctx, nat.Port(port+"/tcp")).String() -} - -// Hostname implements the Cluster interface. -func (l *DockerCluster) Hostname(i int) string { - return l.Nodes[i].nodeStr -} - -// ExecCLI runs ./cockroach with sane defaults. -func (l *DockerCluster) ExecCLI(ctx context.Context, i int, cmd []string) (string, string, error) { - cmd = append([]string{CockroachBinaryInContainer}, cmd...) - cmd = append(cmd, "--host", l.Hostname(i), "--certs-dir=/certs") - cfg := types.ExecConfig{ - Cmd: cmd, - AttachStderr: true, - AttachStdout: true, - } - createResp, err := l.client.ContainerExecCreate(ctx, l.Nodes[i].Container.id, cfg) - if err != nil { - return "", "", err - } - var outputStream, errorStream bytes.Buffer - { - resp, err := l.client.ContainerExecAttach(ctx, createResp.ID, types.ExecStartCheck{}) - if err != nil { - return "", "", err - } - defer resp.Close() - ch := make(chan error) - go func() { - _, err := stdcopy.StdCopy(&outputStream, &errorStream, resp.Reader) - ch <- err - }() - if err := <-ch; err != nil { - return "", "", err - } - } - { - resp, err := l.client.ContainerExecInspect(ctx, createResp.ID) - if err != nil { - return "", "", err - } - if resp.Running { - return "", "", errors.Errorf("command still running") - } - if resp.ExitCode != 0 { - o, e := outputStream.String(), errorStream.String() - return o, e, fmt.Errorf("error executing %s:\n%s\n%s", - cmd, o, e) - } - } - return outputStream.String(), errorStream.String(), nil -} - -// Cleanup removes the cluster's volumes directory, optionally preserving the -// logs directory. -func (l *DockerCluster) Cleanup(ctx context.Context, preserveLogs bool) { - volumes, err := os.ReadDir(l.volumesDir) - if err != nil { - log.Warningf(ctx, "%v", err) - return - } - for _, v := range volumes { - if preserveLogs && v.Name() == "logs" { - continue - } - if err := os.RemoveAll(filepath.Join(l.volumesDir, v.Name())); err != nil { - log.Warningf(ctx, "%v", err) - } - } -} diff --git a/pkg/acceptance/cluster/http.go b/pkg/acceptance/cluster/http.go deleted file mode 100644 index a55e72c066e..00000000000 --- a/pkg/acceptance/cluster/http.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2015 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -package cluster - -import ( - "crypto/tls" - "net/http" - - "github.com/cockroachdb/cockroach/pkg/base" -) - -// HTTPClient is an http.Client configured for querying a cluster. We need to -// run with "InsecureSkipVerify" (at least on Docker) due to the fact that we -// cannot use a fixed hostname to reach the cluster. This in turn means that we -// do not have a verified server name in the certs. -var HTTPClient = http.Client{ - Timeout: base.NetworkTimeout, - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: true, - }, - }, -} diff --git a/pkg/acceptance/cluster/testconfig.proto b/pkg/acceptance/cluster/testconfig.proto deleted file mode 100644 index cc09659e660..00000000000 --- a/pkg/acceptance/cluster/testconfig.proto +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2016 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -syntax = "proto2"; -package cockroach.acceptance.cluster; -option go_package = "cluster"; - -import "gogoproto/gogo.proto"; - -// InitMode specifies different ways to initialize the cluster. -enum InitMode { - option (gogoproto.goproto_enum_prefix) = false; - - // INIT_COMMAND starts every node with a join flag and issues the - // init command. - INIT_COMMAND = 0; - - reserved 1; - - // INIT_NONE starts every node with a join flag and leaves the - // cluster uninitialized. - INIT_NONE = 2; -} - -// StoreConfig holds the configuration of a collection of similar stores. -message StoreConfig { - optional int32 max_ranges = 2 [(gogoproto.nullable) = false]; -} - -// NodeConfig holds the configuration of a collection of similar nodes. -message NodeConfig { - optional string version = 1 [(gogoproto.nullable) = false]; - repeated StoreConfig stores = 2 [(gogoproto.nullable) = false]; -} - -message TestConfig { - optional string name = 1 [(gogoproto.nullable) = false]; - repeated NodeConfig nodes = 2 [(gogoproto.nullable) = false]; - // Duration is the total time that the test should run for. Important for - // tests such as TestPut that will run indefinitely. - optional int64 duration = 3 [(gogoproto.nullable) = false, (gogoproto.casttype) = "time.Duration"]; - optional InitMode init_mode = 4 [(gogoproto.nullable) = false]; - // When set, the cluster is started as quickly as possible, without waiting - // for ranges to replicate, or even ports to be opened. - optional bool no_wait = 5 [(gogoproto.nullable) = false]; -} diff --git a/pkg/acceptance/compose/flyway/docker-compose.yml b/pkg/acceptance/compose/flyway/docker-compose.yml deleted file mode 100644 index 0b1470ea9a6..00000000000 --- a/pkg/acceptance/compose/flyway/docker-compose.yml +++ /dev/null @@ -1,14 +0,0 @@ -version: '3' -services: - cockroach: - image: ubuntu:xenial-20170214 - command: /cockroach/cockroach start-single-node --insecure --listen-addr cockroach - volumes: - - ${COCKROACH_BINARY:-../../../../cockroach-linux-2.6.32-gnu-amd64}:/cockroach/cockroach - flyway: - depends_on: - - cockroach - image: flyway/flyway:6 - volumes: - - ./sql:/sql - command: migrate -user=root -url=jdbc:postgresql://cockroach:26257/defaultdb -locations=filesystem:/sql diff --git a/pkg/acceptance/compose/flyway/sql/V1__init.sql b/pkg/acceptance/compose/flyway/sql/V1__init.sql deleted file mode 100644 index 523d49491b7..00000000000 --- a/pkg/acceptance/compose/flyway/sql/V1__init.sql +++ /dev/null @@ -1,11 +0,0 @@ -CREATE TABLE test ( - i INT8, - s STRING, - f FLOAT8, - b BYTES, - PRIMARY KEY (s, i), - FAMILY f1 (i, s, f), - FAMILY f2 (b) -); - -INSERT INTO test VALUES (1, 'blah', 2.3e4, 'some bytes'); diff --git a/pkg/acceptance/compose/flyway/sql/V2__make_index.sql b/pkg/acceptance/compose/flyway/sql/V2__make_index.sql deleted file mode 100644 index cffa900cb8b..00000000000 --- a/pkg/acceptance/compose/flyway/sql/V2__make_index.sql +++ /dev/null @@ -1,3 +0,0 @@ -CREATE INDEX test_fb ON test (f, b); - -CREATE UNIQUE INDEX test_f_unique ON test (f); diff --git a/pkg/acceptance/compose/flyway/sql/V3__make_fk.sql b/pkg/acceptance/compose/flyway/sql/V3__make_fk.sql deleted file mode 100644 index 0124868b1c4..00000000000 --- a/pkg/acceptance/compose/flyway/sql/V3__make_fk.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE test ADD CONSTRAINT test_f_fk FOREIGN KEY (f) REFERENCES test (f) ON DELETE CASCADE; diff --git a/pkg/acceptance/compose/gss/docker-compose-python.yml b/pkg/acceptance/compose/gss/docker-compose-python.yml deleted file mode 100644 index bd103562fe8..00000000000 --- a/pkg/acceptance/compose/gss/docker-compose-python.yml +++ /dev/null @@ -1,33 +0,0 @@ -version: '3' -services: - kdc: - build: ./kdc - volumes: - - ./kdc/start.sh:/start.sh - - keytab:/keytab - cockroach: - image: ubuntu:xenial-20170214 - depends_on: - - kdc - command: /cockroach/cockroach --certs-dir=/certs start-single-node --listen-addr cockroach - environment: - - KRB5_KTNAME=/keytab/crdb.keytab - volumes: - - ${CERTS_DIR:-../../.localcluster.certs}:/certs - - keytab:/keytab - - ${COCKROACH_BINARY:-../../../../cockroach-linux-2.6.32-gnu-amd64}:/cockroach/cockroach - python: - build: ./python - user: "${UID}:${GID}" - depends_on: - - cockroach - environment: - - PGHOST=cockroach - - PGPORT=26257 - - PGSSLMODE=require - volumes: - - ./kdc/krb5.conf:/etc/krb5.conf - - ./python/start.sh:/start.sh - - ${CERTS_DIR:-../../.localcluster.certs}:/certs -volumes: - keytab: diff --git a/pkg/acceptance/compose/gss/docker-compose.yml b/pkg/acceptance/compose/gss/docker-compose.yml deleted file mode 100644 index e0fcfba127c..00000000000 --- a/pkg/acceptance/compose/gss/docker-compose.yml +++ /dev/null @@ -1,34 +0,0 @@ -version: '3' -services: - kdc: - build: ./kdc - volumes: - - ./kdc/start.sh:/start.sh - - keytab:/keytab - cockroach: - image: ubuntu:xenial-20170214 - depends_on: - - kdc - command: /cockroach/cockroach --certs-dir=/certs start-single-node --listen-addr cockroach - environment: - - KRB5_KTNAME=/keytab/crdb.keytab - volumes: - - ${CERTS_DIR:-../../.localcluster.certs}:/certs - - keytab:/keytab - - ${COCKROACH_BINARY:-../../../../cockroach-linux-2.6.32-gnu-amd64}:/cockroach/cockroach - psql: - build: ./psql - user: "${UID}:${GID}" - depends_on: - - cockroach - environment: - - PGHOST=cockroach - - PGPORT=26257 - volumes: - - ./kdc/krb5.conf:/etc/krb5.conf - - ./psql/gss_test.go:/test/gss_test.go - - ./psql/start.sh:/start.sh - - ${CERTS_DIR:-../../.localcluster.certs}:/certs - - ${COCKROACH_BINARY:-../../../../cockroach-linux-2.6.32-gnu-amd64}:/cockroach/cockroach -volumes: - keytab: diff --git a/pkg/acceptance/compose/gss/kdc/Dockerfile b/pkg/acceptance/compose/gss/kdc/Dockerfile deleted file mode 100644 index dcbe10d6775..00000000000 --- a/pkg/acceptance/compose/gss/kdc/Dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -FROM alpine:3.14 - -RUN apk add --no-cache \ - krb5-server \ - && rm -rf /var/cache/apk/* - -COPY krb5.conf /etc/krb5.conf - -RUN kdb5_util create -s -P kpass \ - && kadmin.local -q "addprinc -pw psql tester@MY.EX" \ - && kadmin.local -q "addprinc -randkey postgres/gss_cockroach_1.gss_default@MY.EX" - -CMD ["/start.sh"] diff --git a/pkg/acceptance/compose/gss/kdc/krb5.conf b/pkg/acceptance/compose/gss/kdc/krb5.conf deleted file mode 100644 index 8d22cf4c44e..00000000000 --- a/pkg/acceptance/compose/gss/kdc/krb5.conf +++ /dev/null @@ -1,32 +0,0 @@ -[logging] - default = FILE:/var/log/krb5libs.log - kdc = FILE:/var/log/krb5kdc.log - admin_server = FILE:/var/log/kadmind.log - -[libdefaults] - default_realm = MY.EX - dns_lookup_realm = false - dns_lookup_kdc = false - ticket_lifetime = 24h - renew_lifetime = 7d - forwardable = yes - -[realms] - MY.EX = { - kdc = kdc:88 - admin_server = kdc:74 - default_domain = my.ex - } - -[domain_realm] - .my.ex = MY.EX - my.ex = MY.EX - -[appdefaults] - pam = { - debug = false - ticket_lifetime = 36000 - renew_lifetime = 36000 - forwardable = true - krb4_convert = false - } diff --git a/pkg/acceptance/compose/gss/kdc/start.sh b/pkg/acceptance/compose/gss/kdc/start.sh deleted file mode 100755 index 9e451e776d5..00000000000 --- a/pkg/acceptance/compose/gss/kdc/start.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -set -e - -# The /keytab directory is volume mounted on both kdc and cockroach. kdc -# can create the keytab with kadmin.local here and it is then useable -# by cockroach. -kadmin.local -q "ktadd -k /keytab/crdb.keytab postgres/gss_cockroach_1.gss_default@MY.EX" - -krb5kdc -n diff --git a/pkg/acceptance/compose/gss/psql/Dockerfile b/pkg/acceptance/compose/gss/psql/Dockerfile deleted file mode 100644 index 7af31ed5e2d..00000000000 --- a/pkg/acceptance/compose/gss/psql/Dockerfile +++ /dev/null @@ -1,23 +0,0 @@ -# Build the test binary in a multistage build. -FROM golang:1.17 AS builder -WORKDIR /workspace -COPY . . -RUN go test -v -c -tags gss_compose -o gss.test - -# Copy the test binary to an image with psql and krb installed. -FROM postgres:15 - -RUN apt-get update && \ - DEBIAN_FRONTEND=noninteractive apt-get install --yes --no-install-recommends \ - ca-certificates \ - curl \ - krb5-user - -COPY --from=builder /workspace/gss.test . - -RUN curl -fsSL "https://github.com/benesch/autouseradd/releases/download/1.3.0/autouseradd-1.3.0-amd64.tar.gz" -o autouseradd.tar.gz \ - && echo "442dae58b727a79f81368127fac141d7f95501ffa05f8c48943d27c4e807deb7 autouseradd.tar.gz" | sha256sum -c - \ - && tar xzf autouseradd.tar.gz --strip-components 1 \ - && rm autouseradd.tar.gz - -ENTRYPOINT ["autouseradd", "--user", "roach", "--no-create-home", "/start.sh"] diff --git a/pkg/acceptance/compose/gss/psql/empty.go b/pkg/acceptance/compose/gss/psql/empty.go deleted file mode 100644 index 3e88cfd7505..00000000000 --- a/pkg/acceptance/compose/gss/psql/empty.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2019 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -package gss - -// This file is here so go test always finds at least one file. diff --git a/pkg/acceptance/compose/gss/psql/go.mod b/pkg/acceptance/compose/gss/psql/go.mod deleted file mode 100644 index 287c8e1f91d..00000000000 --- a/pkg/acceptance/compose/gss/psql/go.mod +++ /dev/null @@ -1,31 +0,0 @@ -module github.com/cockroachdb/cockroach/pkg/acceptance/compose/gss/psql - -go 1.18 - -require ( - github.com/cockroachdb/errors v1.9.0 - github.com/lib/pq v1.10.6 - github.com/lib/pq/auth/kerberos v0.0.0-20220516182748-8c6de565f76f -) - -require ( - github.com/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5 // indirect - github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f // indirect - github.com/cockroachdb/redact v1.1.3 // indirect - github.com/getsentry/sentry-go v0.12.0 // indirect - github.com/gogo/protobuf v1.3.2 // indirect - github.com/hashicorp/go-uuid v1.0.2 // indirect - github.com/jcmturner/aescts/v2 v2.0.0 // indirect - github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect - github.com/jcmturner/gofork v1.0.0 // indirect - github.com/jcmturner/goidentity/v6 v6.0.1 // indirect - github.com/jcmturner/gokrb5/v8 v8.2.0 // indirect - github.com/jcmturner/rpc/v2 v2.0.2 // indirect - github.com/kr/pretty v0.3.0 // indirect - github.com/kr/text v0.2.0 // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/rogpeppe/go-internal v1.8.1 // indirect - golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect - golang.org/x/net v0.0.0-20211008194852-3b03d305991f // indirect - golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect -) diff --git a/pkg/acceptance/compose/gss/psql/go.sum b/pkg/acceptance/compose/gss/psql/go.sum deleted file mode 100644 index 49f9aa586f9..00000000000 --- a/pkg/acceptance/compose/gss/psql/go.sum +++ /dev/null @@ -1,408 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a/go.mod h1:EFZQ978U7x8IRnstaskI3IysnWY5Ao3QgZUKOXlsAdw= -github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= -github.com/CloudyKit/jet v2.1.3-0.20180809161101-62edd43e4f88+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w= -github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo= -github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= -github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM= -github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= -github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= -github.com/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5 h1:P5U+E4x5OkVEKQDklVPmzs71WM56RTTRqV4OrDC//Y4= -github.com/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5/go.mod h1:976q2ETgjT2snVCf2ZaBnyBbVoPERGjUz+0sofzEfro= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cockroachdb/datadriven v1.0.1-0.20211007161720-b558070c3be0/go.mod h1:5Ib8Meh+jk1RlHIXej6Pzevx/NLlNvQB9pmSBZErGA4= -github.com/cockroachdb/datadriven v1.0.1-0.20220214170620-9913f5bc19b7/go.mod h1:hi0MtSY3AYDQNDi83kDkMH5/yqM/CsIrsOITkSoH7KI= -github.com/cockroachdb/errors v1.6.1/go.mod h1:tm6FTP5G81vwJ5lC0SizQo374JNCOPrHyXGitRJoDqM= -github.com/cockroachdb/errors v1.8.8/go.mod h1:z6VnEL3hZ/2ONZEvG7S5Ym0bU2AqPcEKnIiA1wbsSu0= -github.com/cockroachdb/errors v1.9.0 h1:B48dYem5SlAY7iU8AKsgedb4gH6mo+bDkbtLIvM/a88= -github.com/cockroachdb/errors v1.9.0/go.mod h1:vaNcEYYqbIqB5JhKBhFV9CneUqeuEbB2OYJBK4GBNYQ= -github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= -github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f h1:6jduT9Hfc0njg5jJ1DdKCFPdMBrp/mdZfCpa5h+WM74= -github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= -github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= -github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= -github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ= -github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= -github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= -github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= -github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= -github.com/getsentry/sentry-go v0.12.0 h1:era7g0re5iY13bHSdN/xMkyV+5zZppjRVQhZrXCaEIk= -github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c= -github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= -github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= -github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= -github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= -github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= -github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= -github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= -github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= -github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= -github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= -github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= -github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= -github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= -github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= -github.com/gorilla/sessions v1.2.0 h1:S7P+1Hm5V/AT9cjEcUD5uDaQSX0OE577aCXgoaKpYbQ= -github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= -github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/hydrogen18/memlistener v0.0.0-20141126152155-54553eb933fb/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= -github.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= -github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= -github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= -github.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0/go.mod h1:pMCz62A0xJL6I+umB2YTlFRwWXaDFA0jy+5HzGiJjqI= -github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk= -github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g= -github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= -github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= -github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= -github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo= -github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= -github.com/jcmturner/gofork v1.0.0 h1:J7uCkflzTEhUZ64xqKnkDxq3kzc96ajM1Gli5ktUem8= -github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= -github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o= -github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= -github.com/jcmturner/gokrb5/v8 v8.2.0 h1:lzPl/30ZLkTveYsYZPKMcgXc8MbnE6RsTd4F9KgiLtk= -github.com/jcmturner/gokrb5/v8 v8.2.0/go.mod h1:T1hnNppQsBtxW0tCHMHTkAt8n/sABdzZgZdoFrZaZNM= -github.com/jcmturner/rpc/v2 v2.0.2 h1:gMB4IwRXYsWw4Bc6o/az2HJgFUA1ffSh90i26ZJ6Xl0= -github.com/jcmturner/rpc/v2 v2.0.2/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= -github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= -github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA= -github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= -github.com/kataras/golog v0.0.9/go.mod h1:12HJgwBIZFNGL0EJnMRhmvGA0PQGx8VFwrZtM4CqbAk= -github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8= -github.com/kataras/iris/v12 v12.0.1/go.mod h1:udK4vLQKkdDqMGJJVd/msuMtN6hpYJhg/lSzuxjhO+U= -github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE= -github.com/kataras/neffos v0.0.10/go.mod h1:ZYmJC07hQPW67eKuzlfY7SO3bC0mw83A3j6im82hfqw= -github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE= -github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0= -github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro= -github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= -github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y= -github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= -github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs= -github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq/auth/kerberos v0.0.0-20220516182748-8c6de565f76f h1:jXqVYMCT82otkhESiM5dEy290r+lGxwwbg4+VawKEco= -github.com/lib/pq/auth/kerberos v0.0.0-20220516182748-8c6de565f76f/go.mod h1:jydegJvs5JvVcuFD/YAT8JRmRVeOoRhtnGEgRnAoPpE= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= -github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg= -github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ= -github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= -github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= -github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= -github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM= -github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= -github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4= -github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= -github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= -github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= -github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= -github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= -github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= -github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= -github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= -github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20211008194852-3b03d305991f h1:1scJEYZBaF48BaG6tYbtxmLcXqwYGSfGcMoStTqkkIw= -golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158 h1:rm+CHSpPEEW2IsXUib1ThaHIjuBVZjxNgSKmBLFfD4c= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= -gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= -gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/pkg/acceptance/compose/gss/psql/gss_test.go b/pkg/acceptance/compose/gss/psql/gss_test.go deleted file mode 100644 index 0c8c5d1a2cf..00000000000 --- a/pkg/acceptance/compose/gss/psql/gss_test.go +++ /dev/null @@ -1,253 +0,0 @@ -// Copyright 2019 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -// "make test" would normally test this file, but it should only be tested -// within docker compose. We also can't use just "gss" here because that -// tag is reserved for the toplevel Makefile's linux-gnu build. - -//go:build gss_compose -// +build gss_compose - -package gss - -import ( - gosql "database/sql" - "fmt" - "os/exec" - "regexp" - "strings" - "testing" - "time" - "unsafe" - - "github.com/cockroachdb/errors" - "github.com/lib/pq" - "github.com/lib/pq/auth/kerberos" -) - -func init() { - pq.RegisterGSSProvider(func() (pq.GSS, error) { return kerberos.NewGSS() }) -} - -func TestGSS(t *testing.T) { - connector, err := pq.NewConnector("user=root password=rootpw sslmode=require") - if err != nil { - t.Fatal(err) - } - db := gosql.OpenDB(connector) - defer db.Close() - - tests := []struct { - // The hba.conf file/setting. - conf string - user string - // Error message of hba conf - hbaErr string - // Error message of gss login. - gssErr string - // Optionally inject an HBA identity map. - identMap string - }{ - { - conf: `host all all all gss include_realm=0 nope=1`, - hbaErr: `unsupported option`, - }, - { - conf: `host all all all gss include_realm=1`, - hbaErr: `include_realm must be set to 0`, - }, - { - conf: `host all all all gss map=ignored include_realm=1`, - hbaErr: `include_realm must be set to 0`, - }, - { - conf: `host all all all gss`, - hbaErr: `at least one of "include_realm=0" or "map" options required`, - }, - { - conf: `host all all all gss include_realm=0`, - user: "tester", - gssErr: `GSS authentication requires an enterprise license`, - }, - { - conf: `host all tester all gss include_realm=0`, - user: "tester", - gssErr: `GSS authentication requires an enterprise license`, - }, - { - conf: `host all nope all gss include_realm=0`, - user: "tester", - gssErr: "no server.host_based_authentication.configuration entry", - }, - { - conf: `host all all all gss include_realm=0 krb_realm=MY.EX`, - user: "tester", - gssErr: `GSS authentication requires an enterprise license`, - }, - { - conf: `host all all all gss include_realm=0 krb_realm=NOPE.EX`, - user: "tester", - gssErr: `GSSAPI realm \(MY.EX\) didn't match any configured realm`, - }, - { - conf: `host all all all gss include_realm=0 krb_realm=NOPE.EX krb_realm=MY.EX`, - user: "tester", - gssErr: `GSS authentication requires an enterprise license`, - }, - // Validate that we can use the "map" option to strip the realm - // data. Note that the system-identity value will have been - // normalized into a lower-case value. - { - conf: `host all all all gss map=demo`, - identMap: `demo /^(.*)@my.ex$ \1`, - user: "tester", - gssErr: `GSS authentication requires an enterprise license`, - }, - // Verify case-sensitivity. - { - conf: `host all all all gss map=demo`, - identMap: `demo /^(.*)@MY.EX$ \1`, - user: "tester", - gssErr: `system identity "tester@my.ex" did not map to a database role`, - }, - // Validating the use of "map" as a filter. - { - conf: `host all all all gss map=demo`, - identMap: `demo /^(.*)@NOPE.EX$ \1`, - user: "tester", - gssErr: `system identity "tester@my.ex" did not map to a database role`, - }, - // Check map+include_realm=0 case. - { - conf: `host all all all gss include_realm=0 map=demo`, - identMap: `demo tester remapped`, - user: "remapped", - gssErr: `GSS authentication requires an enterprise license`, - }, - } - for i, tc := range tests { - t.Run(fmt.Sprint(i), func(t *testing.T) { - if _, err := db.Exec(`SET CLUSTER SETTING server.host_based_authentication.configuration = $1`, tc.conf); !IsError(err, tc.hbaErr) { - t.Fatalf("expected err %v, got %v", tc.hbaErr, err) - } - if tc.hbaErr != "" { - return - } - if tc.identMap != "" { - if _, err := db.Exec(`SET CLUSTER SETTING server.identity_map.configuration = $1`, tc.identMap); err != nil { - t.Fatalf("bad identity_map: %v", err) - } - } - if _, err := db.Exec(fmt.Sprintf(`CREATE USER IF NOT EXISTS %s`, tc.user)); err != nil { - t.Fatal(err) - } - t.Run("libpq", func(t *testing.T) { - userConnector, err := pq.NewConnector(fmt.Sprintf("user=%s sslmode=require krbspn=postgres/gss_cockroach_1.gss_default", tc.user)) - if err != nil { - t.Fatal(err) - } - userDB := gosql.OpenDB(userConnector) - defer userDB.Close() - _, err = userDB.Exec("SELECT 1") - if !IsError(err, tc.gssErr) { - t.Errorf("expected err %v, got %v", tc.gssErr, err) - } - }) - t.Run("psql", func(t *testing.T) { - out, err := exec.Command("psql", "-c", "SELECT 1", "-U", tc.user).CombinedOutput() - err = errors.Wrap(err, strings.TrimSpace(string(out))) - if !IsError(err, tc.gssErr) { - t.Errorf("expected err %v, got %v", tc.gssErr, err) - } - }) - t.Run("cockroach", func(t *testing.T) { - out, err := exec.Command("/cockroach/cockroach", "sql", - "-e", "SELECT 1", - "--certs-dir", "/certs", - // TODO(mjibson): Teach the CLI to not ask for passwords during kerberos. - // See #51588. - "--url", fmt.Sprintf("postgresql://%s:nopassword@cockroach:26257/?sslmode=require&krbspn=postgres/gss_cockroach_1.gss_default", tc.user), - ).CombinedOutput() - err = errors.Wrap(err, strings.TrimSpace(string(out))) - if !IsError(err, tc.gssErr) { - t.Errorf("expected err %v, got %v", tc.gssErr, err) - } - }) - }) - } -} - -func TestGSSFileDescriptorCount(t *testing.T) { - // When the docker-compose.yml added a ulimit for the cockroach - // container the open file count would just stop there, it wouldn't - // cause cockroach to panic or error like I had hoped since it would - // allow a test to assert that multiple gss connections didn't leak - // file descriptors. Another possibility would be to have something - // track the open file count in the cockroach container, but that seems - // brittle and probably not worth the effort. However this test is - // useful when doing manual tracking of file descriptor count. - t.Skip("#51791") - - rootConnector, err := pq.NewConnector("user=root sslmode=require") - if err != nil { - t.Fatal(err) - } - rootDB := gosql.OpenDB(rootConnector) - defer rootDB.Close() - - if _, err := rootDB.Exec(`SET CLUSTER SETTING server.host_based_authentication.configuration = $1`, "host all all all gss include_realm=0"); err != nil { - t.Fatal(err) - } - const user = "tester" - if _, err := rootDB.Exec(fmt.Sprintf(`CREATE USER IF NOT EXISTS %s`, user)); err != nil { - t.Fatal(err) - } - - start := now() - for i := 0; i < 1000; i++ { - fmt.Println(i, time.Since(start)) - out, err := exec.Command("psql", "-c", "SELECT 1", "-U", user).CombinedOutput() - if IsError(err, "GSS authentication requires an enterprise license") { - t.Log(string(out)) - t.Fatal(err) - } - } -} - -func IsError(err error, re string) bool { - if err == nil && re == "" { - return true - } - if err == nil || re == "" { - return false - } - matched, merr := regexp.MatchString(re, err.Error()) - if merr != nil { - return false - } - return matched -} - -// This is copied from pkg/util/timeutil. -// "A little copying is better than a little dependency". -// See pkg/util/timeutil/time.go for an explanation of the hacky -// implementation here. -type timeLayout struct { - wall uint64 - ext int64 - loc *time.Location -} - -func now() time.Time { - t := time.Now() - x := (*timeLayout)(unsafe.Pointer(&t)) - x.loc = nil // nil means UTC - return t -} diff --git a/pkg/acceptance/compose/gss/psql/start.sh b/pkg/acceptance/compose/gss/psql/start.sh deleted file mode 100755 index 2f003bc5cfd..00000000000 --- a/pkg/acceptance/compose/gss/psql/start.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh - -set -e - -echo "Available certs:" -ls -l /certs - -echo "Environment:" -env - -echo "Creating a k5s token..." -echo psql | kinit tester@MY.EX - -echo "Preparing SQL user ahead of test" -env \ - PGSSLKEY=/certs/client.root.key \ - PGSSLCERT=/certs/client.root.crt \ - psql -U root -c "ALTER USER root WITH PASSWORD rootpw" - -echo "Running test" -./gss.test diff --git a/pkg/acceptance/compose/gss/python/Dockerfile b/pkg/acceptance/compose/gss/python/Dockerfile deleted file mode 100644 index 8beb09da23e..00000000000 --- a/pkg/acceptance/compose/gss/python/Dockerfile +++ /dev/null @@ -1,23 +0,0 @@ -FROM python:3.10 -ENV PYTHONUNBUFFERED 1 - -RUN wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - && \ - echo "deb http://apt.postgresql.org/pub/repos/apt/ bullseye-pgdg main" | tee /etc/apt/sources.list.d/pgdg.list && \ - apt-get update && \ - DEBIAN_FRONTEND=noninteractive apt-get install --yes --no-install-recommends \ - curl \ - krb5-user \ - postgresql-client-11 - -RUN curl -fsSL "https://github.com/benesch/autouseradd/releases/download/1.3.0/autouseradd-1.3.0-amd64.tar.gz" -o autouseradd.tar.gz \ - && echo "442dae58b727a79f81368127fac141d7f95501ffa05f8c48943d27c4e807deb7 autouseradd.tar.gz" | sha256sum -c - \ - && tar xzf autouseradd.tar.gz --strip-components 1 \ - && rm autouseradd.tar.gz - -RUN mkdir /code -WORKDIR /code -COPY requirements.txt /code/ -RUN pip install -r requirements.txt -COPY . /code/ - -ENTRYPOINT ["autouseradd", "--user", "roach", "--no-create-home", "/start.sh"] diff --git a/pkg/acceptance/compose/gss/python/manage.py b/pkg/acceptance/compose/gss/python/manage.py deleted file mode 100755 index 852d2e5cbde..00000000000 --- a/pkg/acceptance/compose/gss/python/manage.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python -"""Django's command-line utility for administrative tasks.""" -import os -import sys - - -def main(): - os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'python.settings') - try: - from django.core.management import execute_from_command_line - except ImportError as exc: - raise ImportError( - "Couldn't import Django. Are you sure it's installed and " - "available on your PYTHONPATH environment variable? Did you " - "forget to activate a virtual environment?" - ) from exc - execute_from_command_line(sys.argv) - - -if __name__ == '__main__': - main() diff --git a/pkg/acceptance/compose/gss/python/python/__init__.py b/pkg/acceptance/compose/gss/python/python/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/pkg/acceptance/compose/gss/python/python/settings.py b/pkg/acceptance/compose/gss/python/python/settings.py deleted file mode 100644 index 2d48e784b95..00000000000 --- a/pkg/acceptance/compose/gss/python/python/settings.py +++ /dev/null @@ -1,121 +0,0 @@ -""" -Django settings for python project. - -Generated by 'django-admin startproject' using Django 2.2.13. - -For more information on this file, see -https://docs.djangoproject.com/en/2.2/topics/settings/ - -For the full list of settings and their values, see -https://docs.djangoproject.com/en/2.2/ref/settings/ -""" - -import os - -# Build paths inside the project like this: os.path.join(BASE_DIR, ...) -BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - - -# Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/ - -# SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'am+pc&0q^sf6)q7%qcw)*m^&@+hgr@l03p*-n07bhj#yylq)1e' - -# SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True - -ALLOWED_HOSTS = ['*'] - - -# Application definition - -INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', -] - -MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', -] - -ROOT_URLCONF = 'python.urls' - -TEMPLATES = [ - { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - ], - }, - }, -] - -WSGI_APPLICATION = 'python.wsgi.application' - - -# Database -# https://docs.djangoproject.com/en/2.2/ref/settings/#databases - -DATABASES = { - 'default': { - 'ENGINE': 'django_cockroachdb', - 'NAME': 'defaultdb', - 'USER': 'tester', - } -} - - -# Password validation -# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators - -AUTH_PASSWORD_VALIDATORS = [ - { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', - }, -] - - -# Internationalization -# https://docs.djangoproject.com/en/2.2/topics/i18n/ - -LANGUAGE_CODE = 'en-us' - -TIME_ZONE = 'UTC' - -USE_I18N = True - -USE_L10N = True - -USE_TZ = True - - -# Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/2.2/howto/static-files/ - -STATIC_URL = '/static/' diff --git a/pkg/acceptance/compose/gss/python/python/urls.py b/pkg/acceptance/compose/gss/python/python/urls.py deleted file mode 100644 index eefe6fb8eb5..00000000000 --- a/pkg/acceptance/compose/gss/python/python/urls.py +++ /dev/null @@ -1,21 +0,0 @@ -"""python URL Configuration - -The `urlpatterns` list routes URLs to views. For more information please see: - https://docs.djangoproject.com/en/2.2/topics/http/urls/ -Examples: -Function views - 1. Add an import: from my_app import views - 2. Add a URL to urlpatterns: path('', views.home, name='home') -Class-based views - 1. Add an import: from other_app.views import Home - 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') -Including another URLconf - 1. Import the include() function: from django.urls import include, path - 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) -""" -from django.contrib import admin -from django.urls import path - -urlpatterns = [ - path('admin/', admin.site.urls), -] diff --git a/pkg/acceptance/compose/gss/python/python/wsgi.py b/pkg/acceptance/compose/gss/python/python/wsgi.py deleted file mode 100644 index 52ad9beed9b..00000000000 --- a/pkg/acceptance/compose/gss/python/python/wsgi.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -WSGI config for python project. - -It exposes the WSGI callable as a module-level variable named ``application``. - -For more information on this file, see -https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/ -""" - -import os - -from django.core.wsgi import get_wsgi_application - -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'python.settings') - -application = get_wsgi_application() diff --git a/pkg/acceptance/compose/gss/python/requirements.txt b/pkg/acceptance/compose/gss/python/requirements.txt deleted file mode 100644 index 78a9a45121f..00000000000 --- a/pkg/acceptance/compose/gss/python/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -Django==3.0.* -django-cockroachdb==3.0.* -psycopg2==2.8.* diff --git a/pkg/acceptance/compose/gss/python/start.sh b/pkg/acceptance/compose/gss/python/start.sh deleted file mode 100755 index e0e53227e6e..00000000000 --- a/pkg/acceptance/compose/gss/python/start.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash - -set -e - -echo "Available certs:" -ls -l /certs - -echo "Environment:" -env - -echo "Creating a k5s token..." -echo psql | kinit tester@MY.EX - -export PGSSLKEY=/certs/client.root.key -export PGSSLCERT=/certs/client.root.crt -export PGUSER=root - -echo "Creating test user" -psql -c "CREATE USER tester" -echo "Configuring the HBA rule prior to running the test..." -psql -c "SET CLUSTER SETTING server.host_based_authentication.configuration = 'host all all all gss include_realm=0'" - -echo "Testing the django connection..." - -unset PGSSLKEY -unset PGSSLCERT -export PGUSER=tester - -# Exit with error unless we find the expected error message. -python manage.py inspectdb 2>&1 | grep 'use of GSS authentication requires an enterprise license' diff --git a/pkg/acceptance/compose_test.go b/pkg/acceptance/compose_test.go deleted file mode 100644 index 0fee6b2a38c..00000000000 --- a/pkg/acceptance/compose_test.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2019 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -package acceptance - -import ( - "bytes" - "io" - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "strconv" - "testing" - - "github.com/cockroachdb/cockroach/pkg/acceptance/cluster" - "github.com/cockroachdb/cockroach/pkg/build/bazel" -) - -const composeDir = "compose" - -func TestComposeGSS(t *testing.T) { - testCompose(t, filepath.Join("gss", "docker-compose.yml"), "psql") -} - -func TestComposeGSSPython(t *testing.T) { - testCompose(t, filepath.Join("gss", "docker-compose-python.yml"), "python") -} - -func TestComposeFlyway(t *testing.T) { - testCompose(t, filepath.Join("flyway", "docker-compose.yml"), "flyway") -} - -func testCompose(t *testing.T, path string, exitCodeFrom string) { - if bazel.BuiltWithBazel() { - // Copy runfiles symlink content to a temporary directory to avoid broken symlinks in docker. - tmpComposeDir, err := ioutil.TempDir("", "") - if err != nil { - t.Fatal(err.Error()) - } - err = copyRunfiles(composeDir, tmpComposeDir) - if err != nil { - t.Fatal(err.Error()) - } - defer func() { - _ = os.RemoveAll(tmpComposeDir) - }() - path = filepath.Join(tmpComposeDir, path) - // If running under Bazel, export 2 environment variables that will be interpolated in docker-compose.yml files. - cockroachBinary, err := filepath.Abs(*cluster.CockroachBinary) - if err != nil { - t.Fatal(err.Error()) - } - err = os.Setenv("COCKROACH_BINARY", cockroachBinary) - if err != nil { - t.Fatal(err.Error()) - } - err = os.Setenv("CERTS_DIR", cluster.AbsCertsDir()) - if err != nil { - t.Fatal(err.Error()) - } - } else { - path = filepath.Join(composeDir, path) - } - uid := os.Getuid() - err := os.Setenv("UID", strconv.Itoa(uid)) - if err != nil { - t.Fatal(err.Error()) - } - gid := os.Getgid() - err = os.Setenv("GID", strconv.Itoa(gid)) - if err != nil { - t.Fatal(err.Error()) - } - cmd := exec.Command( - "docker-compose", - "--no-ansi", - "-f", path, - "up", - "--force-recreate", - "--build", - "--exit-code-from", exitCodeFrom, - ) - var buf bytes.Buffer - if testing.Verbose() { - cmd.Stdout = io.MultiWriter(&buf, os.Stdout) - cmd.Stderr = io.MultiWriter(&buf, os.Stderr) - } else { - cmd.Stdout = &buf - cmd.Stderr = &buf - } - if err := cmd.Run(); err != nil { - t.Log(buf.String()) - t.Fatal(err) - } -} diff --git a/pkg/acceptance/debug_remote_test.go b/pkg/acceptance/debug_remote_test.go deleted file mode 100644 index 44d5d802f4b..00000000000 --- a/pkg/acceptance/debug_remote_test.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2017 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -package acceptance - -import ( - "context" - gosql "database/sql" - "net/http" - "strings" - "testing" - - "github.com/cockroachdb/cockroach/pkg/acceptance/cluster" - "github.com/cockroachdb/cockroach/pkg/util/log" -) - -func TestDebugRemote(t *testing.T) { - s := log.Scope(t) - defer s.Close(t) - // TODO(knz): This test can probably move to a unit test inside the - // server package - RunDocker(t, testDebugRemote) -} - -func testDebugRemote(t *testing.T) { - cfg := cluster.TestConfig{ - Name: "TestDebugRemote", - Duration: *flagDuration, - Nodes: []cluster.NodeConfig{{Stores: []cluster.StoreConfig{{}}}}, - } - ctx := context.Background() - l := StartCluster(ctx, t, cfg).(*cluster.DockerCluster) - defer l.AssertAndStop(ctx, t) - - db, err := gosql.Open("postgres", l.PGUrl(ctx, 0)) - if err != nil { - t.Fatal(err) - } - defer db.Close() - - stdout, stderr, err := l.ExecCLI(ctx, 0, []string{"auth-session", "login", "root", "--only-cookie"}) - if err != nil { - t.Fatalf("auth-session failed: %s\nstdout: %s\nstderr: %s\n", err, stdout, stderr) - } - cookie := strings.Trim(stdout, "\n") - - for i, url := range []string{ - "/debug/", - "/debug/pprof", - "/debug/requests", - "/debug/range?id=1", - "/debug/certificates", - "/debug/logspy?duration=1ns", - } { - t.Run(url, func(t *testing.T) { - req, err := http.NewRequest("GET", l.URL(ctx, 0)+url, nil) - if err != nil { - t.Fatal(err) - } - req.Header.Set("Cookie", cookie) - resp, err := cluster.HTTPClient.Do(req) - if err != nil { - t.Fatalf("%d: %v", i, err) - } - resp.Body.Close() - - if http.StatusOK != resp.StatusCode { - t.Fatalf("%d: expected %d, but got %d", i, http.StatusOK, resp.StatusCode) - } - }) - } -} diff --git a/pkg/acceptance/flags.go b/pkg/acceptance/flags.go deleted file mode 100644 index 0d1fd93dad2..00000000000 --- a/pkg/acceptance/flags.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2015 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -package acceptance - -import ( - "flag" - "time" -) - -var flagDuration = flag.Duration("d", 5*time.Second, "for duration-limited tests, how long to run them for") -var flagLogDir = flag.String("l", "", "the directory to store log files, relative to the test source") diff --git a/pkg/acceptance/localcluster/cluster.go b/pkg/acceptance/localcluster/cluster.go deleted file mode 100644 index 9e4490ab44e..00000000000 --- a/pkg/acceptance/localcluster/cluster.go +++ /dev/null @@ -1,827 +0,0 @@ -// Copyright 2016 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -package localcluster - -import ( - "bytes" - "context" - gosql "database/sql" - "fmt" - "go/build" - "io" - "io/ioutil" - "math/rand" - "net" - "net/url" - "os" - "os/exec" - "path/filepath" - "sort" - "strings" - "sync/atomic" - "text/tabwriter" - "time" - - "github.com/cockroachdb/cockroach/pkg/base" - "github.com/cockroachdb/cockroach/pkg/config/zonepb" - "github.com/cockroachdb/cockroach/pkg/roachpb" - "github.com/cockroachdb/cockroach/pkg/rpc" - "github.com/cockroachdb/cockroach/pkg/security" - "github.com/cockroachdb/cockroach/pkg/server/serverpb" - "github.com/cockroachdb/cockroach/pkg/settings/cluster" - "github.com/cockroachdb/cockroach/pkg/testutils" - "github.com/cockroachdb/cockroach/pkg/util/hlc" - "github.com/cockroachdb/cockroach/pkg/util/log" - "github.com/cockroachdb/cockroach/pkg/util/protoutil" - "github.com/cockroachdb/cockroach/pkg/util/retry" - "github.com/cockroachdb/cockroach/pkg/util/stop" - "github.com/cockroachdb/cockroach/pkg/util/syncutil" - "github.com/cockroachdb/cockroach/pkg/util/timeutil" - "github.com/cockroachdb/errors" - "github.com/cockroachdb/errors/oserror" - "github.com/gogo/protobuf/proto" - // Import postgres driver. - _ "github.com/lib/pq" -) - -func repoRoot() string { - root, err := build.Import("github.com/cockroachdb/cockroach", "", build.FindOnly) - if err != nil { - panic(fmt.Sprintf("must run from within the cockroach repository: %s", err)) - } - return root.Dir -} - -// SourceBinary returns the path of the cockroach binary that was built with the -// local source. -func SourceBinary() string { - return filepath.Join(repoRoot(), "cockroach") -} - -const listeningURLFile = "cockroachdb-url" - -// IsUnavailableError returns true iff the error corresponds to a GRPC -// connection unavailable error. -func IsUnavailableError(err error) bool { - return strings.Contains(err.Error(), "grpc: the connection is unavailable") -} - -// A ClusterConfig holds the configuration for a Cluster. -type ClusterConfig struct { - Ephemeral bool // when true, wipe DataDir on Close() - Binary string // path to cockroach, defaults go /cockroach - AllNodeArgs []string // args to pass to ./cockroach on all nodes - NumNodes int // number of nodes in the cluster - DataDir string // node i will use storage DataDir/ - LogDir string // when empty, node i defaults to DataDir//logs - PerNodeCfg map[int]NodeConfig // optional map of nodeIndex -> configuration - DB string // database to configure DB connection for - NumWorkers int // SetMaxOpenConns to use for DB connection - NoWait bool // if set, return from Start before cluster ready -} - -// NodeConfig is a configuration for a node in a Cluster. Options with the zero -// value are typically populated from the corresponding Cluster's ClusterConfig. -type NodeConfig struct { - Binary string // when specified, overrides the node's binary - DataDir string // when specified, overrides the node's data dir - LogDir string // when specified, overrides the node's log dir - Addr string // listening host, defaults to 127.0.0.1 - ExtraArgs []string // extra arguments for ./cockroach start - ExtraEnv []string // environment variables in format key=value - RPCPort, HTTPPort int // zero for auto-assign - DB string // see ClusterConfig - NumWorkers int // see ClusterConfig -} - -// MakePerNodeFixedPortsCfg makes a PerNodeCfg map of the given number of nodes -// with odd ports starting at 26257 for the RPC endpoint, and even points for -// the ui. -func MakePerNodeFixedPortsCfg(numNodes int) map[int]NodeConfig { - perNodeCfg := make(map[int]NodeConfig) - - for i := 0; i < numNodes; i++ { - perNodeCfg[i] = NodeConfig{ - RPCPort: 26257 + 2*i, - HTTPPort: 26258 + 2*i, - } - } - - return perNodeCfg -} - -// Cluster holds the state for a local cluster, providing methods for common -// operations, access to the underlying nodes and per-node KV and SQL clients. -type Cluster struct { - Cfg ClusterConfig - seq *seqGen - Nodes []*Node - stopper *stop.Stopper - started time.Time -} - -type seqGen int32 - -func (s *seqGen) Next() int32 { - return atomic.AddInt32((*int32)(s), 1) -} - -// New creates a Cluster with the given configuration. -func New(cfg ClusterConfig) *Cluster { - if cfg.Binary == "" { - cfg.Binary = SourceBinary() - } - return &Cluster{ - Cfg: cfg, - seq: new(seqGen), - stopper: stop.NewStopper(), - } -} - -// Start starts a cluster. The numWorkers parameter controls the SQL connection -// settings to avoid unnecessary connection creation. The allNodeArgs parameter -// can be used to pass extra arguments to every node. The perNodeArgs parameter -// can be used to pass extra arguments to an individual node. If not nil, its -// size must equal the number of nodes. -func (c *Cluster) Start(ctx context.Context) { - c.started = timeutil.Now() - - chs := make([]<-chan error, c.Cfg.NumNodes) - for i := 0; i < c.Cfg.NumNodes; i++ { - cfg := c.Cfg.PerNodeCfg[i] // zero value is ok - if cfg.Binary == "" { - cfg.Binary = c.Cfg.Binary - } - if cfg.DataDir == "" { - cfg.DataDir = filepath.Join(c.Cfg.DataDir, fmt.Sprintf("%d", i+1)) - } - if cfg.LogDir == "" && c.Cfg.LogDir != "" { - cfg.LogDir = filepath.Join(c.Cfg.LogDir, fmt.Sprintf("%d", i+1)) - } - if cfg.Addr == "" { - cfg.Addr = "127.0.0.1" - } - if cfg.DB == "" { - cfg.DB = c.Cfg.DB - } - if cfg.NumWorkers == 0 { - cfg.NumWorkers = c.Cfg.NumWorkers - } - cfg.ExtraArgs = append(append([]string(nil), c.Cfg.AllNodeArgs...), cfg.ExtraArgs...) - var node *Node - node, chs[i] = c.makeNode(ctx, i, cfg) - c.Nodes = append(c.Nodes, node) - if i == 0 && cfg.RPCPort == 0 && c.Cfg.NumNodes > 1 { - // The first node must know its RPCPort or we can't possibly tell - // the other nodes the correct one to go to. - // - // Note: we can't set up a cluster first and clone it for each test, - // because all ports change so the cluster won't come together. - // Luckily, it takes only ~2 seconds from zero to a replicated 4 - // node cluster. - if err := <-chs[0]; err != nil { - log.Fatalf(ctx, "while starting first node: %s", err) - } - ch := make(chan error) - close(ch) - chs[0] = ch - } - } - - if !c.Cfg.NoWait { - for i := range chs { - if err := <-chs[i]; err != nil { - log.Fatalf(ctx, "node %d: %s", i+1, err) - } - } - } - - log.Infof(context.Background(), "started %.3fs", timeutil.Since(c.started).Seconds()) - - if c.Cfg.NumNodes > 1 || !c.Cfg.NoWait { - c.waitForFullReplication() - } else { - // NB: This is useful for TestRapidRestarts. - log.Infof(ctx, "not waiting for initial replication") - } -} - -// Close stops the cluster, killing all of the nodes. -func (c *Cluster) Close() { - for _, n := range c.Nodes { - n.Kill() - } - c.stopper.Stop(context.Background()) - if c.Cfg.Ephemeral { - _ = os.RemoveAll(c.Cfg.DataDir) - } -} - -func (c *Cluster) joins() []string { - type addrAndSeq struct { - addr string - seq int32 - } - - var joins []addrAndSeq - for _, node := range c.Nodes { - advertAddr := node.AdvertiseAddr() - if advertAddr != "" { - joins = append(joins, addrAndSeq{ - addr: advertAddr, - seq: atomic.LoadInt32(&node.startSeq), - }) - } - } - sort.Slice(joins, func(i, j int) bool { - return joins[i].seq < joins[j].seq - }) - - if len(joins) == 0 { - return nil - } - - // Return the node with the smallest startSeq, i.e. the node that was - // started first. This is the node that might have no --join flag set, and - // we must point the other nodes at it, and *only* at it (or the other nodes - // may connect sufficiently and never bother to talk to this node). - return []string{joins[0].addr} -} - -// IPAddr returns the IP address of the specified node. -func (c *Cluster) IPAddr(nodeIdx int) string { - return c.Nodes[nodeIdx].IPAddr() -} - -// RPCPort returns the RPC port of the specified node. Returns zero if unknown. -func (c *Cluster) RPCPort(nodeIdx int) string { - return c.Nodes[nodeIdx].RPCPort() -} - -func (c *Cluster) makeNode(ctx context.Context, nodeIdx int, cfg NodeConfig) (*Node, <-chan error) { - baseCtx := &base.Config{ - User: security.NodeUserName(), - Insecure: true, - } - rpcCtx := rpc.NewContext(ctx, rpc.ContextOptions{ - TenantID: roachpb.SystemTenantID, - Config: baseCtx, - Clock: hlc.NewClock(hlc.UnixNano, 0), - Stopper: c.stopper, - Settings: cluster.MakeTestingClusterSettings(), - }) - - n := &Node{ - Cfg: cfg, - rpcCtx: rpcCtx, - seq: c.seq, - } - - args := []string{ - cfg.Binary, - "start", - "--insecure", - // Although --host/--port are deprecated, we cannot yet replace - // this here by --listen-addr/--listen-port, because - // TestVersionUpgrade will also try old binaries. - fmt.Sprintf("--host=%s", n.IPAddr()), - fmt.Sprintf("--port=%d", cfg.RPCPort), - fmt.Sprintf("--http-port=%d", cfg.HTTPPort), - fmt.Sprintf("--store=%s", cfg.DataDir), - fmt.Sprintf("--listening-url-file=%s", n.listeningURLFile()), - "--cache=256MiB", - } - - if n.Cfg.LogDir != "" { - args = append(args, fmt.Sprintf("--log-dir=%s", n.Cfg.LogDir)) - } - - n.Cfg.ExtraArgs = append(args, cfg.ExtraArgs...) - - if err := os.MkdirAll(n.logDir(), 0755); err != nil { - log.Fatalf(context.Background(), "%v", err) - } - - joins := c.joins() - if nodeIdx > 0 && len(joins) == 0 { - ch := make(chan error, 1) - ch <- errors.Errorf("node %d started without join flags", nodeIdx+1) - return nil, ch - } - ch := n.StartAsync(ctx, joins...) - return n, ch -} - -// waitForFullReplication waits for the cluster to be fully replicated. -func (c *Cluster) waitForFullReplication() { - for i := 1; true; i++ { - done, detail := c.isReplicated() - if (done && i >= 50) || (i%50) == 0 { - fmt.Print(detail) - log.Infof(context.Background(), "waiting for replication") - } - if done { - break - } - time.Sleep(100 * time.Millisecond) - } - - log.Infof(context.Background(), "replicated %.3fs", timeutil.Since(c.started).Seconds()) -} - -func (c *Cluster) isReplicated() (bool, string) { - db := c.Nodes[0].DB() - rows, err := db.Query(`SELECT range_id, start_key, end_key, array_length(replicas, 1) FROM crdb_internal.ranges`) - if err != nil { - // Versions <= 1.1 do not contain the crdb_internal table, which is what's used - // to determine whether a cluster has up-replicated. This is relevant for the - // version upgrade acceptance test. Just skip the replication check for this case. - if testutils.IsError(err, "(table|relation) \"crdb_internal.ranges\" does not exist") { - return true, "" - } - log.Fatalf(context.Background(), "%v", err) - } - defer rows.Close() - - var buf bytes.Buffer - tw := tabwriter.NewWriter(&buf, 2, 1, 2, ' ', 0) - done := true - for rows.Next() { - var rangeID int64 - var startKey, endKey roachpb.Key - var numReplicas int - if err := rows.Scan(&rangeID, &startKey, &endKey, &numReplicas); err != nil { - log.Fatalf(context.Background(), "unable to scan range replicas: %s", err) - } - fmt.Fprintf(tw, "\t%s\t%s\t[%d]\t%d\n", startKey, endKey, rangeID, numReplicas) - // This check is coarse since it doesn't know the real configuration. - // Assume all is well when there are 3+ replicas, or if there are as - // many replicas as there are nodes. - if numReplicas < 3 && numReplicas != len(c.Nodes) { - done = false - } - } - _ = tw.Flush() - return done, buf.String() -} - -// UpdateZoneConfig updates the default zone config for the cluster. -func (c *Cluster) UpdateZoneConfig(rangeMinBytes, rangeMaxBytes int64) { - zone := zonepb.DefaultZoneConfig() - zone.RangeMinBytes = proto.Int64(rangeMinBytes) - zone.RangeMaxBytes = proto.Int64(rangeMaxBytes) - - buf, err := protoutil.Marshal(&zone) - if err != nil { - log.Fatalf(context.Background(), "%v", err) - } - _, err = c.Nodes[0].DB().Exec(`UPSERT INTO system.zones (id, config) VALUES (0, $1)`, buf) - if err != nil { - log.Fatalf(context.Background(), "%v", err) - } -} - -// Split splits the range containing the split key at the specified split key. -func (c *Cluster) Split(nodeIdx int, splitKey roachpb.Key) error { - return errors.Errorf("Split is unimplemented and should be re-implemented using SQL") -} - -// TransferLease transfers the lease for the range containing key to a random -// alive node in the range. -func (c *Cluster) TransferLease(nodeIdx int, r *rand.Rand, key roachpb.Key) (bool, error) { - return false, errors.Errorf("TransferLease is unimplemented and should be re-implemented using SQL") -} - -// RandNode returns the index of a random alive node. -func (c *Cluster) RandNode(f func(int) int) int { - for { - i := f(len(c.Nodes)) - if c.Nodes[i].Alive() { - return i - } - } -} - -// Node holds the state for a single node in a local cluster and provides -// methods for starting, pausing, resuming and stopping the node. -type Node struct { - Cfg NodeConfig - rpcCtx *rpc.Context - seq *seqGen - - startSeq int32 // updated atomically on start, nonzero while running - waitErr atomic.Value // last `error`` returned from cmd.Wait() - - syncutil.Mutex - notRunning chan struct{} - cmd *exec.Cmd - rpcPort, pgURL string // legacy: remove once 1.0.x is no longer tested - db *gosql.DB - statusClient serverpb.StatusClient -} - -// RPCPort returns the RPC + Postgres port. -func (n *Node) RPCPort() string { - if s := func() string { - // Legacy case. To be removed. - n.Lock() - defer n.Unlock() - if n.rpcPort != "" && n.rpcPort != "0" { - return n.rpcPort - } - return "" - }(); s != "" { - return s - } - - advAddr := readFileOrEmpty(n.advertiseAddrFile()) - if advAddr == "" { - return "" - } - _, p, _ := net.SplitHostPort(advAddr) - return p -} - -// RPCAddr returns the RPC + Postgres address, or an empty string if it is not known -// (for instance since the node is down). -func (n *Node) RPCAddr() string { - port := n.RPCPort() - if port == "" || port == "0" { - return "" - } - return net.JoinHostPort(n.IPAddr(), port) -} - -// HTTPAddr returns the HTTP address (once known). -func (n *Node) HTTPAddr() string { - return readFileOrEmpty(n.httpAddrFile()) -} - -// PGUrl returns the postgres connection string (may be empty until known). -func (n *Node) PGUrl() string { - n.Lock() - defer n.Unlock() - return n.pgURL -} - -// Alive returns true if the node is alive (i.e. not stopped). Note that a -// paused node is considered alive. -func (n *Node) Alive() bool { - n.Lock() - defer n.Unlock() - return n.cmd != nil -} - -// StatusClient returns a StatusClient set up to talk to this node. -func (n *Node) StatusClient() serverpb.StatusClient { - n.Lock() - existingClient := n.statusClient - n.Unlock() - - if existingClient != nil { - return existingClient - } - - conn, _, err := n.rpcCtx.GRPCDialRaw(n.RPCAddr()) - if err != nil { - log.Fatalf(context.Background(), "failed to initialize status client: %s", err) - } - return serverpb.NewStatusClient(conn) -} - -func (n *Node) logDir() string { - if n.Cfg.LogDir == "" { - return filepath.Join(n.Cfg.DataDir, "logs") - } - return n.Cfg.LogDir -} - -func (n *Node) listeningURLFile() string { - return filepath.Join(n.Cfg.DataDir, listeningURLFile) -} - -// Start starts a node. -func (n *Node) Start(ctx context.Context, joins ...string) { - if err := <-n.StartAsync(ctx, joins...); err != nil { - log.Fatalf(ctx, "%v", err) - } -} - -func (n *Node) setNotRunningLocked(waitErr *exec.ExitError) { - _ = os.Remove(n.listeningURLFile()) - _ = os.Remove(n.advertiseAddrFile()) - _ = os.Remove(n.httpAddrFile()) - if n.notRunning != nil { - close(n.notRunning) - } - n.notRunning = make(chan struct{}) - n.db = nil - n.statusClient = nil - n.cmd = nil - n.rpcPort = "" - n.waitErr.Store(waitErr) - atomic.StoreInt32(&n.startSeq, 0) -} - -func (n *Node) startAsyncInnerLocked(ctx context.Context, joins ...string) error { - n.setNotRunningLocked(nil) - - args := append([]string(nil), n.Cfg.ExtraArgs[1:]...) - for _, join := range joins { - args = append(args, "--join", join) - } - n.cmd = exec.Command(n.Cfg.ExtraArgs[0], args...) - n.cmd.Env = os.Environ() - n.cmd.Env = append(n.cmd.Env, "COCKROACH_SCAN_MAX_IDLE_TIME=5ms") // speed up rebalancing - n.cmd.Env = append(n.cmd.Env, n.Cfg.ExtraEnv...) - - atomic.StoreInt32(&n.startSeq, n.seq.Next()) - - _ = os.MkdirAll(n.logDir(), 0755) - - stdoutPath := filepath.Join(n.logDir(), "stdout") - stdout, err := os.OpenFile(stdoutPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) - if err != nil { - return errors.Wrapf(err, "unable to open file %s", stdoutPath) - } - // This causes the "node startup header" to be printed to stdout, which is - // helpful and not too noisy. - n.cmd.Stdout = io.MultiWriter(stdout, os.Stdout) - - stderrPath := filepath.Join(n.logDir(), "stderr") - stderr, err := os.OpenFile(stderrPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) - if err != nil { - return errors.Wrapf(err, "unable to open file %s", stderrPath) - } - n.cmd.Stderr = stderr - - if n.Cfg.RPCPort > 0 { - n.rpcPort = fmt.Sprintf("%d", n.Cfg.RPCPort) - } - - if err := n.cmd.Start(); err != nil { - if err := stdout.Close(); err != nil { - log.Warningf(ctx, "%v", err) - } - if err := stderr.Close(); err != nil { - log.Warningf(ctx, "%v", err) - } - return errors.Wrapf(err, "running %s %v", n.cmd.Path, n.cmd.Args) - } - - log.Infof(ctx, "process %d starting: %s", n.cmd.Process.Pid, n.cmd.Args) - - go func(cmd *exec.Cmd) { - waitErr := cmd.Wait() - if waitErr != nil { - log.Warningf(ctx, "%v", waitErr) - } - if err := stdout.Close(); err != nil { - log.Warningf(ctx, "%v", err) - } - if err := stderr.Close(); err != nil { - log.Warningf(ctx, "%v", err) - } - - log.Infof(ctx, "process %d: %s", cmd.Process.Pid, cmd.ProcessState) - - var execErr *exec.ExitError - _ = errors.As(waitErr, &execErr) - n.Lock() - n.setNotRunningLocked(execErr) - n.Unlock() - }(n.cmd) - - return nil -} - -// StartAsync starts a node asynchronously. It returns a buffered channel that -// receives either an error, or, once the node has started up and is fully -// functional, `nil`. -// -// StartAsync is a no-op if the node is already running. -func (n *Node) StartAsync(ctx context.Context, joins ...string) <-chan error { - ch := make(chan error, 1) - - if err := func() error { - n.Lock() - defer n.Unlock() - if n.cmd != nil { - return errors.New("server is already running") - } - return n.startAsyncInnerLocked(ctx, joins...) - }(); err != nil { - ch <- err - return ch - } - - go func() { - // If the node does not become live within a minute, something is wrong and - // it's better not to hang indefinitely. - ch <- n.waitUntilLive(time.Minute) - }() - - return ch -} - -func portFromURL(rawURL string) (string, *url.URL, error) { - u, err := url.Parse(rawURL) - if err != nil { - return "", nil, err - } - - _, port, err := net.SplitHostPort(u.Host) - return port, u, err -} - -func makeDB(url string, numWorkers int, dbName string) *gosql.DB { - conn, err := gosql.Open("postgres", url) - if err != nil { - log.Fatalf(context.Background(), "%v", err) - } - if numWorkers == 0 { - numWorkers = 1 - } - conn.SetMaxOpenConns(numWorkers) - conn.SetMaxIdleConns(numWorkers) - return conn -} - -func (n *Node) advertiseAddrFile() string { - return filepath.Join(n.Cfg.DataDir, "cockroach.advertise-addr") -} - -func (n *Node) httpAddrFile() string { - return filepath.Join(n.Cfg.DataDir, "cockroach.http-addr") -} - -func readFileOrEmpty(f string) string { - c, err := ioutil.ReadFile(f) - if err != nil { - if !oserror.IsNotExist(err) { - panic(err) - } - return "" - } - return string(c) -} - -// AdvertiseAddr returns the Node's AdvertiseAddr or empty if none is available. -func (n *Node) AdvertiseAddr() (s string) { - addr := readFileOrEmpty(n.advertiseAddrFile()) - if addr != "" { - return addr - } - // The below is part of the workaround for nodes at v1.0 which don't - // write the file above, explained in more detail in StartAsync(). - if port := n.RPCPort(); port != "" { - return net.JoinHostPort(n.IPAddr(), n.RPCPort()) - } - return addr -} - -func (n *Node) waitUntilLive(dur time.Duration) error { - ctx := context.Background() - closer := make(chan struct{}) - defer time.AfterFunc(dur, func() { close(closer) }).Stop() - opts := retry.Options{ - InitialBackoff: time.Millisecond, - MaxBackoff: 500 * time.Millisecond, - Multiplier: 2, - Closer: closer, - } - for r := retry.Start(opts); r.Next(); { - var pid int - n.Lock() - if n.cmd != nil { - pid = n.cmd.Process.Pid - } - n.Unlock() - if pid == 0 { - log.Info(ctx, "process already quit") - return nil - } - - urlBytes, err := ioutil.ReadFile(n.listeningURLFile()) - if err != nil { - log.Infof(ctx, "%v", err) - continue - } - - var pgURL *url.URL - _, pgURL, err = portFromURL(string(urlBytes)) - if err != nil { - log.Infof(ctx, "%v", err) - continue - } - - if n.Cfg.RPCPort == 0 { - n.Lock() - n.rpcPort = pgURL.Port() - n.Unlock() - } - - pgURL.Path = n.Cfg.DB - n.Lock() - n.pgURL = pgURL.String() - n.Unlock() - - var uiURL *url.URL - - defer func() { - log.Infof(ctx, "process %d started (db: %s ui: %s)", pid, pgURL, uiURL) - }() - - // We're basically running, but (at least) the decommissioning test sometimes starts - // up servers that can already be draining when they get here. For that reason, leave - // the admin port undefined if we don't manage to get it. - // - // This can be improved by making the below code run opportunistically whenever the - // http port is required but isn't initialized yet. - n.Lock() - n.db = makeDB(n.pgURL, n.Cfg.NumWorkers, n.Cfg.DB) - n.Unlock() - - { - var uiStr string - if err := n.db.QueryRow( - `SELECT value FROM crdb_internal.node_runtime_info WHERE component='UI' AND field = 'URL'`, - ).Scan(&uiStr); err != nil { - log.Infof(ctx, "%v", err) - return nil - } - - _, uiURL, err = portFromURL(uiStr) - if err != nil { - log.Infof(ctx, "%v", err) - // TODO(tschottdorf): see above. - } - } - return nil - } - return errors.Errorf("node %+v was unable to join cluster within %s", n.Cfg, dur) -} - -// Kill stops a node abruptly by sending it SIGKILL. -func (n *Node) Kill() { - n.Signal(os.Kill) - // Wait for the process to have been cleaned up (or a call to Start() could - // turn into an unintended no-op). - for ok := false; !ok; { - n.Lock() - ok = n.cmd == nil - n.Unlock() - } -} - -// IPAddr returns the node's listening address (for ui, inter-node, cli, and -// Postgres alike). -func (n *Node) IPAddr() string { - return n.Cfg.Addr -} - -// DB returns a Postgres connection set up to talk to the node. -func (n *Node) DB() *gosql.DB { - n.Lock() - defer n.Unlock() - return n.db -} - -// Signal sends the given signal to the process. It is a no-op if the process is -// not running. -func (n *Node) Signal(s os.Signal) { - n.Lock() - defer n.Unlock() - if n.cmd == nil || n.cmd.Process == nil { - return - } - if err := n.cmd.Process.Signal(s); err != nil { - log.Warningf(context.Background(), "%v", err) - } -} - -// Wait waits for the process to terminate and returns its process' Wait(). This -// is nil if the process terminated with a zero exit code. -func (n *Node) Wait() *exec.ExitError { - n.Lock() - ch := n.notRunning - n.Unlock() - if ch == nil { - log.Warning(context.Background(), "(*Node).Wait called when node was not running") - return nil - } - <-ch - ee, _ := n.waitErr.Load().(*exec.ExitError) - return ee -} - -// Silence unused warning. -var _ = (*Node)(nil).Wait diff --git a/pkg/acceptance/localcluster/localcluster.go b/pkg/acceptance/localcluster/localcluster.go deleted file mode 100644 index d49e5f3dccb..00000000000 --- a/pkg/acceptance/localcluster/localcluster.go +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2015 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -package localcluster - -import ( - "bytes" - "context" - gosql "database/sql" - "net" - "os/exec" - "testing" - "time" - - "github.com/cockroachdb/cockroach/pkg/acceptance/cluster" - "github.com/cockroachdb/errors" -) - -// LocalCluster implements cluster.Cluster. -type LocalCluster struct { - *Cluster -} - -var _ cluster.Cluster = &LocalCluster{} - -// Port implements cluster.Cluster. -func (b *LocalCluster) Port(ctx context.Context, i int) string { - return b.RPCPort(i) -} - -// NumNodes implements cluster.Cluster. -func (b *LocalCluster) NumNodes() int { - return len(b.Nodes) -} - -// NewDB implements the Cluster interface. -func (b *LocalCluster) NewDB(ctx context.Context, i int) (*gosql.DB, error) { - return gosql.Open("postgres", b.PGUrl(ctx, i)) -} - -// PGUrl implements cluster.Cluster. -func (b *LocalCluster) PGUrl(ctx context.Context, i int) string { - return b.Nodes[i].PGUrl() -} - -// InternalIP implements cluster.Cluster. -func (b *LocalCluster) InternalIP(ctx context.Context, i int) net.IP { - ips, err := net.LookupIP(b.IPAddr(i)) - if err != nil { - panic(err) - } - return ips[0] -} - -// Assert implements cluster.Cluster. -func (b *LocalCluster) Assert(ctx context.Context, t testing.TB) { - // TODO(tschottdorf): actually implement this. -} - -// AssertAndStop implements cluster.Cluster. -func (b *LocalCluster) AssertAndStop(ctx context.Context, t testing.TB) { - b.Assert(ctx, t) - b.Close() -} - -// ExecCLI implements cluster.Cluster. -func (b *LocalCluster) ExecCLI(ctx context.Context, i int, cmd []string) (string, string, error) { - cmd = append([]string{b.Cfg.Binary}, cmd...) - cmd = append(cmd, "--insecure", "--host", ":"+b.Port(ctx, i)) - c := exec.CommandContext(ctx, cmd[0], cmd[1:]...) - var o, e bytes.Buffer - c.Stdout, c.Stderr = &o, &e - err := c.Run() - if err != nil { - err = errors.Wrapf(err, "cmd: %v\nstderr:\n %s\nstdout:\n %s", cmd, o.String(), e.String()) - } - return o.String(), e.String(), err -} - -// Kill implements cluster.Cluster. -func (b *LocalCluster) Kill(ctx context.Context, i int) error { - b.Nodes[i].Kill() - return nil -} - -// RestartAsync restarts the node. The returned channel receives an error or, -// once the node is successfully connected to the cluster and serving, nil. -func (b *LocalCluster) RestartAsync(ctx context.Context, i int) <-chan error { - b.Nodes[i].Kill() - joins := b.joins() - ch := b.Nodes[i].StartAsync(ctx, joins...) - if len(joins) == 0 && len(b.Nodes) > 1 { - // This blocking loop in is counter-intuitive but is essential in allowing - // restarts of whole clusters. Roughly the following happens: - // - // 1. The whole cluster gets killed. - // 2. A node restarts. - // 3. It will *block* here until it has written down the file which contains - // enough information to link other nodes. - // 4. When restarting other nodes, and `.joins()` is passed in, these nodes - // can connect (at least) to the first node. - // 5. the cluster can become healthy after restart. - // - // If we didn't block here, we'd start all nodes up with join addresses that - // don't make any sense, and the cluster would likely not become connected. - // - // An additional difficulty is that older versions (pre 1.1) don't write - // this file. That's why we let *every* node do this (you could try to make - // only the first one wait, but if that one is 1.0, bad luck). - // Short-circuiting the wait in the case that the listening URL file is - // written makes restarts work with 1.0 servers for the most part. - for { - if gossipAddr := b.Nodes[i].AdvertiseAddr(); gossipAddr != "" { - return ch - } - time.Sleep(10 * time.Millisecond) - } - } - return ch -} - -// Restart implements cluster.Cluster. -func (b *LocalCluster) Restart(ctx context.Context, i int) error { - return <-b.RestartAsync(ctx, i) -} - -// URL implements cluster.Cluster. -func (b *LocalCluster) URL(ctx context.Context, i int) string { - rest := b.Nodes[i].HTTPAddr() - if rest == "" { - return "" - } - return "http://" + rest -} - -// Addr implements cluster.Cluster. -func (b *LocalCluster) Addr(ctx context.Context, i int, port string) string { - return net.JoinHostPort(b.Nodes[i].AdvertiseAddr(), port) -} - -// Hostname implements cluster.Cluster. -func (b *LocalCluster) Hostname(i int) string { - return b.IPAddr(i) -} diff --git a/pkg/acceptance/localcluster/tc/tc.go b/pkg/acceptance/localcluster/tc/tc.go deleted file mode 100644 index 4a05899fe04..00000000000 --- a/pkg/acceptance/localcluster/tc/tc.go +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2017 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -// Package tc contains utility methods for using the Linux tc (traffic control) -// command to mess with the network links between cockroach nodes running on -// the local machine. -// -// Requires passwordless sudo in order to run tc. -// -// Does not work on OS X due to the lack of the tc command (and even an -// alternative wouldn't work for the current use case of this code, which also -// requires being able to bind to multiple localhost addresses). -package tc - -import ( - "fmt" - "os/exec" - "strings" - "time" - - "github.com/cockroachdb/errors" -) - -const ( - rootHandle = 1 - defaultClass = 1 -) - -// Controller provides a way to add artificial latency to local traffic. -type Controller struct { - interfaces []string - nextClass int -} - -// NewController creates and returns a controller that will modify the traffic -// routing for the provided interfaces. -func NewController(interfaces ...string) *Controller { - return &Controller{ - interfaces: interfaces, - nextClass: defaultClass + 1, - } -} - -// Init prepares the local network interfaces so that we can later add per-node -// traffic shaping rules. -func (c *Controller) Init() error { - for _, ifce := range c.interfaces { - _, _ = exec.Command("sudo", strings.Split(fmt.Sprintf("tc qdisc del dev %s root", ifce), " ")...).Output() - out, err := exec.Command("sudo", strings.Split(fmt.Sprintf("tc qdisc add dev %s root handle %d: htb default %d", - ifce, rootHandle, defaultClass), " ")...).Output() - if err != nil { - return errors.Wrapf(err, "failed to create root tc qdisc for %q: %s", ifce, out) - } - // The 100mbit limitation is because classes of type htb (hierarchy token - // bucket) need a bandwidth limit, and we want an arbitrarily high one. Feel - // free to bump it up here and below if you think it's limiting you. - out, err = exec.Command("sudo", strings.Split(fmt.Sprintf("tc class add dev %s parent %d: classid %d:%d htb rate 100mbit", - ifce, rootHandle, rootHandle, defaultClass), " ")...).Output() - if err != nil { - return errors.Wrapf(err, "failed to create root tc class for %q: %s", ifce, out) - } - } - return nil -} - -// AddLatency adds artificial latency between the specified source and dest -// addresses. -func (c *Controller) AddLatency(srcIP, dstIP string, latency time.Duration) error { - class := c.nextClass - handle := class * 10 - c.nextClass++ - for _, ifce := range c.interfaces { - out, err := exec.Command("sudo", strings.Split(fmt.Sprintf("tc class add dev %s parent %d: classid %d:%d htb rate 100mbit", - ifce, rootHandle, rootHandle, class), " ")...).Output() - if err != nil { - return errors.Wrapf(err, "failed to add tc class %d: %s", class, out) - } - out, err = exec.Command("sudo", strings.Split(fmt.Sprintf("tc qdisc add dev %s parent %d:%d handle %d: netem delay %v", - ifce, rootHandle, class, handle, latency), " ")...).Output() - if err != nil { - return errors.Wrapf(err, "failed to add tc netem delay of %v: %s", latency, out) - } - out, err = exec.Command("sudo", strings.Split(fmt.Sprintf("tc filter add dev %s parent %d: protocol ip u32 match ip src %s/32 match ip dst %s/32 flowid %d:%d", - ifce, rootHandle, srcIP, dstIP, rootHandle, class), " ")...).Output() - if err != nil { - return errors.Wrapf(err, "failed to add tc filter rule between %s and %s: %s", srcIP, dstIP, out) - } - } - return nil -} - -// CleanUp resets all interfaces back to their default tc policies. -func (c *Controller) CleanUp() error { - var err error - for _, ifce := range c.interfaces { - out, thisErr := exec.Command("sudo", strings.Split(fmt.Sprintf("tc qdisc del dev %s root", ifce), " ")...).Output() - if err != nil { - err = errors.CombineErrors(err, errors.Wrapf( - thisErr, "failed to remove tc rules for %q -- you may have to remove them manually: %s", ifce, out)) - } - } - return err -} diff --git a/pkg/acceptance/main_test.go b/pkg/acceptance/main_test.go deleted file mode 100644 index 4ab2c28175e..00000000000 --- a/pkg/acceptance/main_test.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2015 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -package acceptance - -import "testing" - -func TestMain(m *testing.M) { - MainTest(m) -} diff --git a/pkg/acceptance/prepare.sh b/pkg/acceptance/prepare.sh deleted file mode 100755 index 60b40bedaaa..00000000000 --- a/pkg/acceptance/prepare.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash - -set -euxo pipefail - -# Ensure that no stale binary remains. -rm -f cockroach-linux-2.6.32-gnu-amd64 pkg/acceptance/acceptance.test - -# We must make a release build here because the binary needs to work in both -# the builder image and the postgres-test image, which have different libstdc++ -# versions. -build/builder.sh mkrelease linux-gnu diff --git a/pkg/acceptance/run.sh b/pkg/acceptance/run.sh deleted file mode 100755 index 8377ca093b9..00000000000 --- a/pkg/acceptance/run.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash - -set -euxo pipefail - -"$(dirname "${0}")"/prepare.sh - -# The log files that should be created by -l below can only -# be created if the parent directory already exists. Ensure -# that it exists before running the test. -mkdir -p artifacts/acceptance -export TMPDIR=$PWD/artifacts/acceptance - -# For the acceptance tests that run without Docker. -make build -make test PKG=./pkg/acceptance TESTTIMEOUT="${TESTTIMEOUT-30m}" TAGS=acceptance TESTFLAGS="${TESTFLAGS--v} -b $PWD/cockroach-linux-2.6.32-gnu-amd64 -l $TMPDIR" diff --git a/pkg/acceptance/test_acceptance.go b/pkg/acceptance/test_acceptance.go deleted file mode 100644 index 7a5682ce5b2..00000000000 --- a/pkg/acceptance/test_acceptance.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2015 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -//go:build acceptance -// +build acceptance - -// Acceptance tests are comparatively slow to run, so we use the above build -// tag to separate invocations of `go test` which are intended to run the -// acceptance tests from those which are not. The corollary file to this one -// is test_main.go - -package acceptance - -import ( - "context" - "os" - "os/signal" - "testing" - - "github.com/cockroachdb/cockroach/pkg/acceptance/cluster" - "github.com/cockroachdb/cockroach/pkg/security" - "github.com/cockroachdb/cockroach/pkg/security/securitytest" - "github.com/cockroachdb/cockroach/pkg/server" - "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" - "github.com/cockroachdb/cockroach/pkg/testutils/testcluster" - "github.com/cockroachdb/cockroach/pkg/util/randutil" -) - -func MainTest(m *testing.M) { - security.SetAssetLoader(securitytest.EmbeddedAssets) - serverutils.InitTestServerFactory(server.TestServerFactory) - serverutils.InitTestClusterFactory(testcluster.TestClusterFactory) - os.Exit(RunTests(m)) -} - -// RunTests runs the tests in a package while gracefully handling interrupts. -func RunTests(m *testing.M) int { - randutil.SeedForTests() - - ctx := context.Background() - defer cluster.GenerateCerts(ctx)() - - go func() { - // Shut down tests when interrupted (for example CTRL+C). - sig := make(chan os.Signal, 1) - signal.Notify(sig, os.Interrupt) - <-sig - stopper.Stop(ctx) - }() - return m.Run() -} diff --git a/pkg/acceptance/test_main.go b/pkg/acceptance/test_main.go deleted file mode 100644 index 86201f1c6da..00000000000 --- a/pkg/acceptance/test_main.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2016 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -//go:build !acceptance -// +build !acceptance - -// Our Docker-based acceptance tests are comparatively slow to run, so we use -// the above build tag to separate invocations of `go test` which are intended -// to run the acceptance tests from those which are not. The corollary file to -// this one is main_test.go. Acceptance tests against remote clusters (which -// aren't run unless `-remote` is passed explicitly) are run through this file -// via the standard facilities (i.e. `make test` and without `acceptance` build -// tag). - -package acceptance - -import ( - "fmt" - "os" - "testing" -) - -// MainTest is an exported implementation of TestMain for use by other -// packages. -func MainTest(m *testing.M) { - fmt.Fprintln(os.Stderr, "not running with `acceptance` build tag; skipping") -} diff --git a/pkg/acceptance/testdata/Dockerfile b/pkg/acceptance/testdata/Dockerfile deleted file mode 100644 index d7beb77ad16..00000000000 --- a/pkg/acceptance/testdata/Dockerfile +++ /dev/null @@ -1,130 +0,0 @@ -FROM ubuntu:18.04 - -# This Dockerfile bundles several language runtimes and test frameworks into one -# container for our acceptance tests. -# -# Here are some tips for keeping this file maintainable. -# -# - When possible, keep commands that are slow to rebuild towards the top of -# the file and keep commands that change frequently towards the bottom of -# the file. Changing a command invalidates the build cache for all following -# commands. -# -# - Group logical stages into one RUN command, but not when it groups a slow -# step that's unlikely to change with a step that's likely to change -# frequently. -# -# - Follow the visual indentation scheme. -# -# - Don't cargo cult installation instructions from the web. There are many -# ways to add a new APT repository, for example; we should only use one. -# There are many ways to invoke curl and tar; we should be consistent. -# -# - Store artifacts in /opt rather than cluttering the root directory. -# -# - Prefer packages from APT to packages from each language's package -# managers. - -RUN apt-get update \ - && apt-get install --yes --no-install-recommends ca-certificates curl - -# The forward reference version is the oldest version from which we support -# upgrading. The bidirectional reference version is the oldest version that we -# support upgrading from and downgrading to. -ENV FORWARD_REFERENCE_VERSION="v2.0.0" -ENV BIDIRECTIONAL_REFERENCE_VERSION="v2.0.0" -RUN mkdir /opt/forward-reference-version /opt/bidirectional-reference-version \ - && curl -fsSL https://binaries.cockroachdb.com/cockroach-${FORWARD_REFERENCE_VERSION}.linux-amd64.tgz \ - | tar xz -C /opt/forward-reference-version --strip-components=1 \ - && curl -fsSL https://binaries.cockroachdb.com/cockroach-${BIDIRECTIONAL_REFERENCE_VERSION}.linux-amd64.tgz \ - | tar xz -C /opt/bidirectional-reference-version --strip-components=1 - -RUN apt-get install --yes --no-install-recommends openjdk-8-jdk \ - && curl -fsSL https://github.com/cockroachdb/finagle-postgres/archive/94b1325270.tar.gz | tar xz \ - && cd finagle-postgres-* \ - && ./sbt assembly \ - && mv target/scala-2.11/finagle-postgres-tests.jar /opt/finagle-postgres-tests.jar \ - && rm -rf /finagle-postgres-* ~/.ivy2 - -RUN curl -fsSL https://dl.yarnpkg.com/debian/pubkey.gpg > /etc/apt/trusted.gpg.d/yarn.asc \ - && echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ - && curl -fsSL https://packages.microsoft.com/keys/microsoft.asc > /etc/apt/trusted.gpg.d/microsoft.asc \ - && echo "deb https://packages.microsoft.com/repos/microsoft-ubuntu-bionic-prod/ bionic main" > /etc/apt/sources.list.d/microsoft.list \ - && apt-get install --yes --no-install-recommends gnupg \ - && curl https://packages.erlang-solutions.com/erlang-solutions_2.0_all.deb > erlang-solutions_2.0_all.deb && dpkg -i erlang-solutions_2.0_all.deb \ - && apt-get update \ - && apt-get install --yes --no-install-recommends \ - dotnet-sdk-2.1 \ - dotnet-runtime-2.1 \ - expect \ - elixir \ - esl-erlang \ - libc6-dev \ - libcurl4 \ - libpq-dev \ - libpqtypes-dev \ - make \ - maven \ - nodejs \ - gcc \ - golang \ - php-cli \ - php-pgsql \ - postgresql-client \ - python \ - python-psycopg2 \ - ruby \ - ruby-pg \ - xmlstarlet \ - yarn - -RUN curl -fsSL https://github.com/benesch/autouseradd/releases/download/1.0.0/autouseradd-1.0.0-amd64.tar.gz \ - | tar xz -C /usr --strip-components 1 - -# When system packages are not available for a language's PostgreSQL driver, -# fall back to using that language's package manager. The high-level process -# looks like this: -# -# 1. Configure the package manager to install dependencies into a system-wide -# location (/var/lib/...) instead of a user-specific location (/root/...). -# /root is not world-readable, and we want the packages to be available to -# non-root users. -# -# 2. Instruct the package manager to install the dependencies by reading -# whatever language-specific package manifest exists in testdata/LANG. -# -# 3. Either remove the package manager entirely, if it's not used to drive -# tests, or put it into offline mode. This ensures that future dependencies -# get baked into the container, lest we accidentally introduce a CI-time -# dependency on the remote package repository. - -COPY . /testdata - -# Handle C# dependencies using the NuGet package manager. -ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 NUGET_PACKAGES=/var/lib/nuget/packages -RUN (cd /testdata/csharp && dotnet restore --no-cache) \ - && xmlstarlet ed --inplace \ - --subnode /configuration --type elem --name packageRestore \ - --subnode '$prev' --type elem --name add \ - --insert '$prev' --type attr --name key --value enabled \ - --insert '$prev' --type attr --name value --value false \ - --delete /configuration/packageSources \ - ~/.nuget/NuGet/NuGet.Config - -# Handle Java dependencies using the Maven package manager. -RUN xmlstarlet ed --inplace \ - --subnode /_:settings --type elem --name localRepository --value /var/lib/maven/repository \ - /usr/share/maven/conf/settings.xml \ - && (cd /testdata/java && mvn -Dtest=NoopTest dependency:go-offline package) \ - && xmlstarlet ed --inplace \ - --subnode /_:settings --type elem --name offline --value true \ - /usr/share/maven/conf/settings.xml - -# Handle Node dependencies using the Yarn package manager. -RUN (cd /testdata/node && yarn install && mv node_modules /usr/lib/node) \ - && apt-get purge --yes yarn - -RUN apt-get purge --yes \ - curl \ - xmlstarlet \ - && rm -rf /tmp/* /var/lib/apt/lists/* /testdata diff --git a/pkg/acceptance/testdata/README.md b/pkg/acceptance/testdata/README.md deleted file mode 100644 index 153f926f0a9..00000000000 --- a/pkg/acceptance/testdata/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# Acceptance Test Harnesses - -The Dockerfile in this directory builds an image, `cockroachdb/acceptance`, in -which we run our language-specific acceptance tests. For each language we -support, we install the compiler or runtime, and typically the Postgres driver, -into the image. Where possible, we use packages provided by Debian; otherwise we -invoke the language's package manager while building the image. - -In all cases, the language's package manager is removed or put into offline mode -before the image is fully baked to ensure that we don't accidentally introduce a -dependency on an external package repository into our CI pipeline. That means -that if you update a language's dependencies (e.g., `node/package.json`), you'll -need to update the image. - -To build a new acceptance image: - -```bash -$ docker build -t cockroachdb/acceptance . -``` - -To push the just-built acceptance image to the registry: - -```bash -$ version=$(date +%Y%m%d-%H%M%S) -$ docker tag cockroachdb/acceptance cockroachdb/acceptance:$version -$ docker push cockroachdb/acceptance:$version -``` - -No need to have your changes reviewed before you push an image, as we pin the -container version to use in `../util_docker.go`. Once your changes are ready for -review, update the pinned version and submit a PR. diff --git a/pkg/acceptance/testdata/c/.gitignore b/pkg/acceptance/testdata/c/.gitignore deleted file mode 100644 index 9daeafb9864..00000000000 --- a/pkg/acceptance/testdata/c/.gitignore +++ /dev/null @@ -1 +0,0 @@ -test diff --git a/pkg/acceptance/testdata/c/Makefile b/pkg/acceptance/testdata/c/Makefile deleted file mode 100644 index 3dcb6e8fb9f..00000000000 --- a/pkg/acceptance/testdata/c/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -CC += -std=c99 -CPPFLAGS += -I/usr/include/postgresql -LDLIBS += -lpq -lpqtypes diff --git a/pkg/acceptance/testdata/c/test.c b/pkg/acceptance/testdata/c/test.c deleted file mode 100644 index 6ca36ee2981..00000000000 --- a/pkg/acceptance/testdata/c/test.c +++ /dev/null @@ -1,471 +0,0 @@ -#include -#include -#include -#include -#include - -int intervalEqual(PGinterval left, PGinterval right) { - if (left.years != right.years) { - return 0; - } - if (left.mons != right.mons) { - return 0; - } - if (left.days != right.days) { - return 0; - } - if (left.hours != right.hours) { - return 0; - } - if (left.mins != right.mins) { - return 0; - } - if (left.secs != right.secs) { - return 0; - } - if (left.usecs != right.usecs) { - return 0; - } - return 1; -} - -void intervalPrint(PGinterval interval) { - fprintf(stderr, "years=%d\n", interval.years); - fprintf(stderr, "mons=%d\n", interval.mons); - fprintf(stderr, "days=%d\n", interval.days); - fprintf(stderr, "hours=%d\n", interval.hours); - fprintf(stderr, "mins=%d\n", interval.mins); - fprintf(stderr, "secs=%d\n", interval.secs); - fprintf(stderr, "usecs=%d\n", interval.usecs); -} - -int dateEqual(PGdate left, PGdate right) { - // Only check the fields we explicitly set. The uninitialized fields for our - // expected result will have non-deterministic values, and those same fields - // may be populated in the actual result by libpqtypes after decoding the - // database query's output. - if (left.isbc != right.isbc) { - return 0; - } - if (left.year != right.year) { - return 0; - } - if (left.mon != right.mon) { - return 0; - } - if (left.mday != right.mday) { - return 0; - } - return 1; -} - -void datePrint(PGdate date) { - fprintf(stderr, "isbc=%d\n", date.isbc); - fprintf(stderr, "year=%d\n", date.year); - fprintf(stderr, "mon=%d\n", date.mon); - fprintf(stderr, "mday=%d\n", date.mday); -} - -int timeEqual(PGtime left, PGtime right) { - // Only check the fields we explicitly set. See comment on dateEqual. - if (left.hour != right.hour) { - return 0; - } - if (left.min != right.min) { - return 0; - } - if (left.sec != right.sec) { - return 0; - } - if (left.usec != right.usec) { - return 0; - } - if (left.withtz != right.withtz) { - return 0; - } - if (left.gmtoff != right.gmtoff) { - return 0; - } - return 1; -} - -void timePrint(PGtime time) { - fprintf(stderr, "hour=%d\n", time.hour); - fprintf(stderr, "min=%d\n", time.min); - fprintf(stderr, "sec=%d\n", time.sec); - fprintf(stderr, "usec=%d\n", time.usec); - fprintf(stderr, "withtz=%d\n", time.withtz); - fprintf(stderr, "gmtoff=%d\n", time.gmtoff); -} - -int timestampEqual(PGtimestamp left, PGtimestamp right) { - if (left.epoch != right.epoch) { - return 0; - } - if (!dateEqual(left.date, right.date)) { - return 0; - } - if (!timeEqual(left.time, right.time)) { - return 0; - } - return 1; -} - -void timestampPrint(PGtimestamp ts) { - fprintf(stderr, "epoch=%lld\n", ts.epoch); - fprintf(stderr, "date:\n"); - datePrint(ts.date); - fprintf(stderr, "time:\n"); - timePrint(ts.time); -} - -int main(int argc, char *argv[]) { - if (argc != 2) { - fprintf(stderr, "usage: %s QUERY\n", argv[0]); - return 1; - } - char *query = argv[1]; - - PGconn *conn = PQconnectdb(""); - if (PQstatus(conn) != CONNECTION_OK) { - fprintf(stderr, "Connection to database failed: %s\n", PQerrorMessage(conn)); - return 1; - } - - const char *version = PQparameterStatus(conn, "crdb_version"); - if (version == NULL) { - fprintf(stderr, "ERROR PQparameterStatus: crdb_version not reported: %s\n", PQgeterror()); - return 1; - } - if (strncmp(version, "CockroachDB ", strlen("CockroachDB ")) != 0) { - fprintf(stderr, "crdb_version mismatch: '%s' doesn't start with 'CockroachDB '\n", version); - return 1; - } - - - /* Always call first on any conn that is to be used with libpqtypes */ - PQinitTypes(conn); - - PGparam *param = PQparamCreate(conn); - - PGbool b = 1; - if (!PQputf(param, "%bool", b)) { - fprintf(stderr, "ERROR PQputf(bool): %s\n", PQgeterror()); - return 1; - } - - char bytes[] = "hello"; - PGbytea bytea; - bytea.len = sizeof(bytes); - bytea.data = bytes; - if (!PQputf(param, "%bytea", &bytea)) { - fprintf(stderr, "ERROR PQputf(bytea): %s\n", PQgeterror()); - return 1; - } - - // '1401-01-19' - PGdate date; - // TODO(jordan): put this back when #28099 is fixed. - // date.isbc = 1; - date.isbc = 0; - date.year = 1401; - date.mon = 0; - date.mday = 19; - if (!PQputf(param, "%date", &date)) { - fprintf(stderr, "ERROR PQputf(date): %s\n", PQgeterror()); - return 1; - } - - PGnumeric numeric1 = "42"; - if (!PQputf(param, "%numeric", numeric1)) { - fprintf(stderr, "ERROR PQputf(numeric): %s\n", PQgeterror()); - return 1; - } - - PGnumeric numeric2 = "-1728718718271827121233.1212121212"; - if (!PQputf(param, "%numeric", numeric2)) { - fprintf(stderr, "ERROR PQputf(numeric): %s\n", PQgeterror()); - return 1; - } - - PGfloat8 f8 = 123456.789; - if (!PQputf(param, "%float8", f8)) { - fprintf(stderr, "ERROR PQputf(float8): %s\n", PQgeterror()); - return 1; - } - - PGint8 i8 = INT_MAX; - if (!PQputf(param, "%int8", i8)) { - fprintf(stderr, "ERROR PQputf(int8): %s\n", PQgeterror()); - return 1; - } - - // "20 years 8 months 9 hours 10 mins 15 secs 123456 usecs" - PGinterval interval; - interval.years = 20; - interval.mons = 8; - interval.days = 0; // not used, set to 0 - interval.hours = 9; - interval.mins = 10; - interval.secs = 15; - interval.usecs = 123456; - // TODO(tamird,nvanbenschoten): implement interval binary encoding/decoding. - if (0) { - if (!PQputf(param, "%interval", &interval)) { - fprintf(stderr, "ERROR PQputf(interval): %s\n", PQgeterror()); - return 1; - } - } - - PGtext text = "foobar"; - if (!PQputf(param, "%text", text)) { - fprintf(stderr, "ERROR PQputf(text): %s\n", PQgeterror()); - return 1; - } - - // '2000-01-19 10:41:06' - PGtimestamp ts; - ts.epoch = 948278466; // expected, but not used in PQputf. - ts.date.isbc = 0; - ts.date.year = 2000; - ts.date.mon = 0; - ts.date.mday = 19; - ts.time.hour = 10; - ts.time.min = 41; - ts.time.sec = 6; - ts.time.usec = 0; - ts.time.withtz = 0; - ts.time.gmtoff = 0; // PQputf normalizes to GMT, so set and expect 0. - if (!PQputf(param, "%timestamp", &ts)) { - fprintf(stderr, "ERROR PQputf(timestamp): %s\n", PQgeterror()); - return 1; - } - - // '2000-01-19 10:41:06-05' - PGtimestamp tstz; - tstz.epoch = 948278466; - tstz.date.isbc = 0; - tstz.date.year = 2000; - tstz.date.mon = 0; - tstz.date.mday = 19; - tstz.time.hour = 10; - tstz.time.min = 41; - tstz.time.sec = 6; - tstz.time.usec = 0; - tstz.time.withtz = 1; - tstz.time.gmtoff = 0; - - if (!PQputf(param, "%timestamptz", &tstz)) { - fprintf(stderr, "ERROR PQputf(timestamptz): %s\n", PQgeterror()); - return 1; - } - - char uuidBytes[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; - PGuuid uuid = uuidBytes; - if (!PQputf(param, "%uuid", uuid)) { - fprintf(stderr, "ERROR PQputf(uuid): %s\n", PQgeterror()); - return 1; - } - - PGint8 i; - PGarray arr; - - arr.ndims = 0; - arr.param = PQparamCreate(conn); - int arrLen = 100; - PGint8 expectedArr[arrLen]; - - for (i = 0; i < arrLen; i++) { - expectedArr[i] = i; - if (!PQputf(arr.param, "%int8", i)) { - fprintf(stderr, "ERROR PQputf(arr elem): %s\n", PQgeterror()); - return 1; - } - } - if (!PQputf(param, "%int8[]", &arr)) { - fprintf(stderr, "ERROR PQputf(arr): %s\n", PQgeterror()); - return 1; - } - - // resultFormat: 0 for text, 1 for binary. - for (int resultFormat = 0; resultFormat <= 1; ++resultFormat) { - PGresult *result = PQparamExec(conn, param, query, resultFormat); - if(!result) { - fprintf(stderr, "ERROR: %s\n", PQgeterror()); - return 1; - } - - int i = 0; - - PGbool recvb; - if (!PQgetf(result, 0, "%bool", i++, &recvb)) { - fprintf(stderr, "ERROR resultFormat=%d PQgetf(bool): %s\n", resultFormat, PQgeterror()); - return 1; - } - if (recvb != b) { - fprintf(stderr, "resultFormat=%d expected: %d, got: %d\n", resultFormat, b, recvb); - return 1; - } - - PGbytea recvbytea; - if (!PQgetf(result, 0, "%bytea", i++, &recvbytea)) { - fprintf(stderr, "ERROR resultFormat=%d PQgetf(bytea): %s\n", resultFormat, PQgeterror()); - return 1; - } - if (memcmp(recvbytea.data, bytea.data, - MIN(recvbytea.len, bytea.len) // lint: uppercase function OK - ) != 0) { - fprintf(stderr, "resultFormat=%d expected (%d bytes): ", resultFormat, bytea.len); - for (int i = 0; i < bytea.len; ++i) { - fprintf(stderr, "%c", bytea.data[i]); - } - fprintf(stderr, " got (%d bytes): ", recvbytea.len); - for (int i = 0; i < recvbytea.len; ++i) { - fprintf(stderr, "%c", recvbytea.data[i]); - } - fprintf(stderr, "\n"); - return 1; - } - - PGdate recvdate; - if (!PQgetf(result, 0, "%date", i++, &recvdate)) { - fprintf(stderr, "ERROR resultFormat=%d PQgetf(date): %s\n", resultFormat, PQgeterror()); - return 1; - } - if (!dateEqual(recvdate, date)) { - fprintf(stderr, "resultFormat=%d expected:\n", resultFormat); - datePrint(date); - fprintf(stderr, "\ngot:\n"); - datePrint(recvdate); - return 1; - } - - PGnumeric recvnumeric1; - if (!PQgetf(result, 0, "%numeric", i++, &recvnumeric1)) { - fprintf(stderr, "ERROR resultFormat=%d PQgetf(numeric): %s\n", resultFormat, PQgeterror()); - return 1; - } - if (strcmp(recvnumeric1, numeric1)) { - fprintf(stderr, "resultFormat=%d expected: %s, got: %s\n", resultFormat, numeric1, recvnumeric1); - return 1; - } - - PGnumeric recvnumeric2; - if (!PQgetf(result, 0, "%numeric", i++, &recvnumeric2)) { - fprintf(stderr, "ERROR resultFormat=%d PQgetf(numeric): %s\n", resultFormat, PQgeterror()); - return 1; - } - if (strcmp(recvnumeric2, numeric2)) { - fprintf(stderr, "resultFormat=%d expected: %s, got: %s\n", resultFormat, numeric2, recvnumeric2); - return 1; - } - - PGfloat8 recvf8; - if (!PQgetf(result, 0, "%float8", i++, &recvf8)) { - fprintf(stderr, "ERROR resultFormat=%d PQgetf(float8): %s\n", resultFormat, PQgeterror()); - return 1; - } - if (recvf8 != f8) { - fprintf(stderr, "resultFormat=%d expected: %f, got: %f\n", resultFormat, f8, recvf8); - return 1; - } - - PGint8 recvi8; - if (!PQgetf(result, 0, "%int8", i++, &recvi8)) { - fprintf(stderr, "ERROR resultFormat=%d PQgetf(int8): %s\n", resultFormat, PQgeterror()); - return 1; - } - if (recvi8 != i8) { - fprintf(stderr, "resultFormat=%d expected: %lld, got: %lld\n", resultFormat, i8, recvi8); - return 1; - } - - // TODO(tamird,nvanbenschoten): implement interval binary encoding/decoding. - if (0) { - PGinterval recvinterval; - if (!PQgetf(result, 0, "%interval", i++, &recvinterval)) { - fprintf(stderr, "ERROR resultFormat=%d PQgetf(interval): %s\n", resultFormat, PQgeterror()); - return 1; - } - if (!intervalEqual(recvinterval, interval)) { - fprintf(stderr, "resultFormat=%d expected:\n", resultFormat); - intervalPrint(interval); - fprintf(stderr, "\ngot:\n"); - intervalPrint(recvinterval); - return 1; - } - } - - PGtext recvtext; - if (!PQgetf(result, 0, "%text", i++, &recvtext)) { - fprintf(stderr, "ERROR resultFormat=%d PQgetf(text): %s\n", resultFormat, PQgeterror()); - return 1; - } - if (strcmp(recvtext, text)) { - fprintf(stderr, "resultFormat=%d expected: %s, got: %s\n", resultFormat, text, recvtext); - return 1; - } - - PGtimestamp recvts; - if (!PQgetf(result, 0, "%timestamp", i++, &recvts)) { - fprintf(stderr, "ERROR resultFormat=%d PQgetf(timestamp): %s\n", resultFormat, PQgeterror()); - return 1; - } - if (!timestampEqual(recvts, ts)) { - fprintf(stderr, "resultFormat=%d expected:\n", resultFormat); - timestampPrint(ts); - fprintf(stderr, "\ngot:\n"); - timestampPrint(recvts); - return 1; - } - - PGtimestamp recvtstz; - if (!PQgetf(result, 0, "%timestamptz", i++, &recvtstz)) { - fprintf(stderr, "ERROR resultFormat=%d PQgetf(timestamptz): %s\n", resultFormat, PQgeterror()); - return 1; - } - - if (!timestampEqual(recvtstz, tstz)) { - fprintf(stderr, "resultFormat=%d expected:\n", resultFormat); - timestampPrint(tstz); - fprintf(stderr, "\ngot:\n"); - timestampPrint(recvtstz); - return 1; - } - - PGuuid recvuuid; - if (!PQgetf(result, 0, "%uuid", i++, &recvuuid)) { - fprintf(stderr, "ERROR resultFormat=%d PQgetf(uuid): %s\n", resultFormat, PQgeterror()); - return 1; - } - if (strcmp(recvuuid, uuid)) { - fprintf(stderr, "resultFormat=%d expected: %s, got: %s\n", resultFormat, uuid, recvuuid); - return 1; - } - - // Libpqtypes doesn't support text array decoding. - if (resultFormat == 1) { - PGarray recvarr; - if (!PQgetf(result, 0, "%int8[]", i++, &recvarr)) { - fprintf(stderr, "ERROR resultFormat=%d PQgetf(arr): %s\n", resultFormat, PQgeterror()); - return 1; - } - int n = PQntuples(recvarr.res); - if (arrLen != n) { - fprintf(stderr, "expected array of size %d, got %d\n", arrLen, n); - return 1; - } - int result[arrLen]; - PGint8 val; - for (int i = 0; i < arrLen; i++) { - PQgetf(recvarr.res, i, "%int8", 0, &val); - if (val != expectedArr[i]) { - fprintf(stderr, "resultFormat=%d expected %lld at pos %d; got %lld\n", resultFormat, expectedArr[i], i, val); - return 1; - } - } - } - } - - return 0; -} diff --git a/pkg/acceptance/testdata/csharp/.gitignore b/pkg/acceptance/testdata/csharp/.gitignore deleted file mode 100644 index a12245e16a3..00000000000 --- a/pkg/acceptance/testdata/csharp/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -bin -obj -node.pfx diff --git a/pkg/acceptance/testdata/csharp/Program.cs b/pkg/acceptance/testdata/csharp/Program.cs deleted file mode 100644 index 9b1e7defa92..00000000000 --- a/pkg/acceptance/testdata/csharp/Program.cs +++ /dev/null @@ -1,434 +0,0 @@ -using System; -using System.Data; -using System.Linq; -using Npgsql; -using System.Security.Cryptography.X509Certificates; -using System.Security.Cryptography; - -namespace CockroachDrivers -{ - class MainClass - { - static void Main(string[] args) - { - var connStringBuilder = new NpgsqlConnectionStringBuilder(); - connStringBuilder.Port = int.Parse(Environment.GetEnvironmentVariable("PGPORT")); - connStringBuilder.Host = Environment.GetEnvironmentVariable("PGHOST"); - connStringBuilder.Username = Environment.GetEnvironmentVariable("PGUSER"); - connStringBuilder.SslMode = SslMode.Require; - connStringBuilder.TrustServerCertificate = true; - // Npgsql needs to connect to a database that already exists. - connStringBuilder.Database = "system"; - Simple(connStringBuilder.ConnectionString); - ArrayTest(connStringBuilder.ConnectionString); - TxnSample(connStringBuilder.ConnectionString); - UnitTest(connStringBuilder.ConnectionString); - } - - static void ProvideClientCertificatesCallback(X509CertificateCollection clientCerts) - { - clientCerts.Add(new X509Certificate2("/certs/client.root.pk12")); - } - - static void Simple(string connString) - { - using (var conn = new NpgsqlConnection(connString)) - { - conn.ProvideClientCertificatesCallback += new ProvideClientCertificatesCallback(ProvideClientCertificatesCallback); - conn.Open(); - - // Create the database if it doesn't exist. - new NpgsqlCommand("CREATE DATABASE IF NOT EXISTS bank", conn).ExecuteNonQuery(); - - // Create the "accounts" table. - new NpgsqlCommand("CREATE TABLE IF NOT EXISTS bank.accounts (id INT PRIMARY KEY, balance INT)", conn).ExecuteNonQuery(); - - // Insert two rows into the "accounts" table. - using (var cmd = new NpgsqlCommand()) - { - cmd.Connection = conn; - cmd.CommandText = "UPSERT INTO bank.accounts(id, balance) VALUES(@id1, @val1), (@id2, @val2)"; - cmd.Parameters.AddWithValue("id1", 1); - cmd.Parameters.AddWithValue("val1", 1000); - cmd.Parameters.AddWithValue("id2", 2); - cmd.Parameters.AddWithValue("val2", 250); - cmd.ExecuteNonQuery(); - } - - // Print out the balances. - System.Console.WriteLine("Initial balances:"); - using (var cmd = new NpgsqlCommand("SELECT id, balance FROM bank.accounts", conn)) - using (var reader = cmd.ExecuteReader()) - while (reader.Read()) - Console.Write("\taccount {0}: {1}\n", reader.GetString(0), reader.GetString(1)); - } - } - - static void TransferFunds(NpgsqlConnection conn, NpgsqlTransaction tran, int from, int to, int amount) - { - int balance = 0; - using (var cmd = new NpgsqlCommand(String.Format("SELECT balance FROM bank.accounts WHERE id = {0}", from), conn, tran)) - using (var reader = cmd.ExecuteReader()) - { - if (reader.Read()) - { - balance = reader.GetInt32(0); - } - else - { - throw new DataException(String.Format("Account id={0} not found", from)); - } - } - if (balance < amount) - { - throw new DataException(String.Format("Insufficient balance in account id={0}", from)); - } - using (var cmd = new NpgsqlCommand(String.Format("UPDATE bank.accounts SET balance = balance - {0} where id = {1}", amount, from), conn, tran)) - { - cmd.ExecuteNonQuery(); - } - using (var cmd = new NpgsqlCommand(String.Format("UPDATE bank.accounts SET balance = balance + {0} where id = {1}", amount, to), conn, tran)) - { - cmd.ExecuteNonQuery(); - } - } - - static void TxnSample(string connString) - { - using (var conn = new NpgsqlConnection(connString)) - { - conn.ProvideClientCertificatesCallback += new ProvideClientCertificatesCallback(ProvideClientCertificatesCallback); - conn.Open(); - try - { - using (var tran = conn.BeginTransaction()) - { - tran.Save("cockroach_restart"); - while (true) - { - try - { - TransferFunds(conn, tran, 1, 2, 100); - tran.Commit(); - break; - } - catch (NpgsqlException e) - { - // Check if the error code indicates a SERIALIZATION_FAILURE. - if (e.ErrorCode == 40001) - { - // Signal the database that we will attempt a retry. - tran.Rollback("cockroach_restart"); - } - else - { - throw; - } - } - } - } - } - catch (DataException e) - { - Console.WriteLine(e.Message); - } - - // Now printout the results. - Console.WriteLine("Final balances:"); - using (var cmd = new NpgsqlCommand("SELECT id, balance FROM bank.accounts", conn)) - using (var reader = cmd.ExecuteReader()) - while (reader.Read()) - Console.Write("\taccount {0}: {1}\n", reader.GetString(0), reader.GetString(1)); - } - } - - static void UnitTest(string connString) - { - using (var conn = new NpgsqlConnection(connString)) - { - conn.ProvideClientCertificatesCallback += new ProvideClientCertificatesCallback(ProvideClientCertificatesCallback); - conn.Open(); - - using (var cmd = new NpgsqlCommand("DROP DATABASE IF EXISTS test CASCADE", conn)) - { - cmd.Prepare(); - var rows = cmd.ExecuteNonQuery(); - if (rows != -1) - { - throw new DataException(String.Format("DROP DATABASE reports {0} rows changed, expecting -1", rows)); - } - } - - using (var cmd = new NpgsqlCommand("CREATE DATABASE test", conn)) - { - cmd.Prepare(); - var rows = cmd.ExecuteNonQuery(); - if (rows != -1) - { - throw new DataException(String.Format("CREATE DATABASE reports {0} rows changed, expecting -1", rows)); - } - } - - using (var cmd = new NpgsqlCommand("CREATE TABLE test.f(x INT, ts TIMESTAMP)", conn)) - { - cmd.Prepare(); - var rows = cmd.ExecuteNonQuery(); - if (rows != -1) - { - throw new DataException(String.Format("CREATE TABLE reports {0} rows changed, expecting -1", rows)); - } - } - - using (var cmd = new NpgsqlCommand("INSERT INTO test.f VALUES (42, timestamp '2015-05-07 18:20:00')", conn)) - { - cmd.Prepare(); - var rows = cmd.ExecuteNonQuery(); - if (rows != 1) - { - throw new DataException(String.Format("INSERT reports {0} rows changed, expecting 1", rows)); - } - } - - using (var cmd = new NpgsqlCommand("SELECT * FROM test.f", conn)) - { - cmd.Prepare(); - using (var reader = cmd.ExecuteReader()) - { - reader.Read(); - var a = reader.GetInt32(0); - if (a != 42) - { - throw new DataException(String.Format("SELECT can't find inserted value: read {0}, expecting 42", a)); - } - var ts = reader.GetTimeStamp(1); - var expectedTs = new NpgsqlTypes.NpgsqlDateTime(2015, 5, 7, 18, 20, 0, DateTimeKind.Unspecified); - if (ts != expectedTs) - { - throw new DataException(String.Format("SELECT unexpected value for ts: read {0}, expecting {1}", ts, expectedTs)); - } - } - } - - using (var cmd = new NpgsqlCommand("INSERT INTO test.f VALUES (@x, @ts)", conn)) - { - cmd.Parameters.Add("x", NpgsqlTypes.NpgsqlDbType.Integer).Value = 1; - cmd.Parameters.Add("ts", NpgsqlTypes.NpgsqlDbType.Timestamp).Value = new NpgsqlTypes.NpgsqlDateTime(DateTime.Now); - cmd.Prepare(); - var rows = cmd.ExecuteNonQuery(); - if (rows != 1) - { - throw new DataException(String.Format("INSERT reports {0} rows changed, expecting 1", rows)); - } - } - - using (var cmd = new NpgsqlCommand("DROP TABLE test.f", conn)) - { - cmd.Prepare(); - var rows = cmd.ExecuteNonQuery(); - if (rows != -1) - { - throw new DataException(String.Format("DROP TABLE reports {0} rows changed, expecting -1", rows)); - } - } - - using (var cmd = new NpgsqlCommand("SELECT 1, 2 > @comp, @int::int, @bool::string, @long::string, @real::string, @double::string, @short::string", conn)) - { - cmd.Parameters.Add("comp", NpgsqlTypes.NpgsqlDbType.Integer).Value = 3; - cmd.Parameters.Add("int", NpgsqlTypes.NpgsqlDbType.Integer).Value = 3; - cmd.Parameters.Add("bool", NpgsqlTypes.NpgsqlDbType.Boolean).Value = true; - cmd.Parameters.Add("long", NpgsqlTypes.NpgsqlDbType.Bigint).Value = -4L; - cmd.Parameters.Add("real", NpgsqlTypes.NpgsqlDbType.Real).Value = 5.31f; - cmd.Parameters.Add("double", NpgsqlTypes.NpgsqlDbType.Double).Value = -6.21d; - cmd.Parameters.Add("short", NpgsqlTypes.NpgsqlDbType.Smallint).Value = (short)7; - cmd.Prepare(); - using (var reader = cmd.ExecuteReader()) - { - reader.Read(); - var a = reader.GetInt32(0); - if (a != 1) - { - throw new DataException(String.Format("SELECT returns {0}, expected 1", a)); - } - var b = reader.GetBoolean(1); - if (b) - { - throw new DataException(String.Format("SELECT returns {0}, expected false", b)); - } - var c = reader.GetInt32(2); - if (c != 3) - { - throw new DataException(String.Format("SELECT returns {0}, expected 3", c)); - } - var d = reader.GetString(3); - if (!d.Equals("true")) - { - throw new DataException(String.Format("SELECT returns {0}, expected true", d)); - } - var e = reader.GetString(4); - if (!e.Equals("-4")) - { - throw new DataException(String.Format("SELECT returns {0}, expected -4", e)); - } - var f = reader.GetString(5); - if (!f.StartsWith("5.3", StringComparison.Ordinal)) - { - throw new DataException(String.Format("SELECT returns {0}, expected that it starts with 5.3", f)); - } - var g = reader.GetString(6); - if (!g.StartsWith("-6.2", StringComparison.Ordinal)) - { - throw new DataException(String.Format("SELECT returns {0}, expected that it starts with -6.2", g)); - } - var h = reader.GetString(7); - if (!h.Equals("7")) - { - throw new DataException(String.Format("SELECT returns {0}, expected 7", h)); - } - } - } - - using (var cmd = new NpgsqlCommand("USE test", conn)) - { - cmd.Prepare(); - var rows = cmd.ExecuteNonQuery(); - if (rows != -1) - { - throw new DataException(String.Format("USE reports {0} rows changed, expecting -1", rows)); - } - } - - using (var cmd = new NpgsqlCommand("CREATE TABLE accounts (id INT PRIMARY KEY, balance INT, cdate DATE)", conn)) - { - cmd.Prepare(); - var rows = cmd.ExecuteNonQuery(); - if (rows != -1) - { - throw new DataException(String.Format("CREATE TABLE reports {0} rows changed, expecting -1", rows)); - } - } - - using (var cmd = new NpgsqlCommand("INSERT INTO accounts (id, balance, cdate) VALUES ( @id, @balance, @cdate )", conn)) - { - cmd.Parameters.Add("id", NpgsqlTypes.NpgsqlDbType.Integer).Value = 1; - cmd.Parameters.Add("balance", NpgsqlTypes.NpgsqlDbType.Integer).Value = 1000; - cmd.Parameters.Add("cdate", NpgsqlTypes.NpgsqlDbType.Date).Value = new NpgsqlTypes.NpgsqlDate(DateTime.Now); - cmd.Prepare(); - var rows = cmd.ExecuteNonQuery(); - if (rows != 1) - { - throw new DataException(String.Format("INSERT reports {0} rows changed, expecting 1", rows)); - } - } - - using (var cmd = new NpgsqlCommand("CREATE TABLE empty()", conn)) - { - cmd.Prepare(); - var rows = cmd.ExecuteNonQuery(); - if (rows != -1) - { - throw new DataException(String.Format("CREATE TABLE reports {0} rows changed, expecting -1", rows)); - } - } - - using (var cmd = new NpgsqlCommand("SELECT * from empty", conn)) - { - cmd.Prepare(); - using (var reader = cmd.ExecuteReader()) - { - if (reader.FieldCount != 0) - { - throw new DataException(String.Format("SELECT returns {0} columns, expected 0", reader.FieldCount)); - } - } - } - - using (var cmd = new NpgsqlCommand("CREATE TABLE str (s STRING)", conn)) - { - cmd.Prepare(); - var rows = cmd.ExecuteNonQuery(); - if (rows != -1) - { - throw new DataException(String.Format("CREATE TABLE reports {0} rows changed, expecting -1", rows)); - } - } - - using (var cmd = new NpgsqlCommand("UPDATE str SET s = @s", conn)) - { - cmd.Parameters.Add("s", NpgsqlTypes.NpgsqlDbType.Varchar).Value = "hello"; - cmd.Prepare(); - var rows = cmd.ExecuteNonQuery(); - if (rows != 0) - { - throw new DataException(String.Format("UPDATE reports {0} rows changed, expecting 0", rows)); - } - } - - using (var cmd = new NpgsqlCommand("SELECT @x", conn)) - { - var uuid = new Guid(); - cmd.Parameters.Add("x", NpgsqlTypes.NpgsqlDbType.Uuid).Value = uuid; - cmd.Prepare(); - using (var reader = cmd.ExecuteReader()) - { - reader.Read(); - var actualUUID = reader.GetGuid(0); - if (actualUUID != uuid) - { - throw new DataException(String.Format("SELECT returns {0}, expected {1}", actualUUID, uuid)); - } - } - } - - // Check that imprecise placeholder typing works correctly. See issues - // #14245 and #14311 for more detail. - using (var cmd = new NpgsqlCommand("CREATE TABLE t (price decimal(5,2) NOT NULL)", conn)) - { - cmd.Prepare(); - var rows = cmd.ExecuteNonQuery(); - if (rows != -1) - { - throw new DataException(String.Format("CREATE TABLE reports {0} rows changed, expecting -1", rows)); - } - } - - using (var cmd = new NpgsqlCommand("INSERT INTO test.t VALUES(@f)", conn)) - { - cmd.Parameters.Add("f", NpgsqlTypes.NpgsqlDbType.Real).Value = 3.3f; - cmd.Prepare(); - var rows = cmd.ExecuteNonQuery(); - if (rows != 1) - { - throw new DataException(String.Format("INSERT reports {0} rows changed, expecting 1", rows)); - } - } - } - } - - static void ArrayTest(string connString) - { - using (var conn = new NpgsqlConnection(connString)) - { - conn.ProvideClientCertificatesCallback += new ProvideClientCertificatesCallback(ProvideClientCertificatesCallback); - conn.Open(); - - new NpgsqlCommand("CREATE DATABASE IF NOT EXISTS test", conn).ExecuteNonQuery(); - new NpgsqlCommand("CREATE TABLE IF NOT EXISTS test.arrays (a INT[])", conn).ExecuteNonQuery(); - using (var cmd = new NpgsqlCommand()) - { - cmd.Connection = conn; - cmd.CommandText = "INSERT INTO test.arrays(a) VALUES(@val)"; - cmd.Parameters.AddWithValue("val", new int[] {1, 2, 3}); - cmd.ExecuteNonQuery(); - } - using (var cmd = new NpgsqlCommand("SELECT a FROM test.arrays", conn)) - using (var reader = cmd.ExecuteReader()) - while (reader.Read()) { - var ary = reader["a"] as long[]; - if (!ary.SequenceEqual(new long[] {1, 2, 3})) { - throw new DataException(String.Format("Expected result to be [1, 2, 3], was {0}", String.Join(", ", ary))); - } - } - } - } - } -} diff --git a/pkg/acceptance/testdata/csharp/acceptance.csproj b/pkg/acceptance/testdata/csharp/acceptance.csproj deleted file mode 100644 index 95d8257f1b3..00000000000 --- a/pkg/acceptance/testdata/csharp/acceptance.csproj +++ /dev/null @@ -1,9 +0,0 @@ - - - Exe - netcoreapp2.0 - - - - - diff --git a/pkg/acceptance/testdata/elixir/test_crdb/.formatter.exs b/pkg/acceptance/testdata/elixir/test_crdb/.formatter.exs deleted file mode 100644 index d2cda26eddc..00000000000 --- a/pkg/acceptance/testdata/elixir/test_crdb/.formatter.exs +++ /dev/null @@ -1,4 +0,0 @@ -# Used by "mix format" -[ - inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] -] diff --git a/pkg/acceptance/testdata/elixir/test_crdb/.gitignore b/pkg/acceptance/testdata/elixir/test_crdb/.gitignore deleted file mode 100644 index 7dc11ecb065..00000000000 --- a/pkg/acceptance/testdata/elixir/test_crdb/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# The directory Mix will write compiled artifacts to. -/_build/ - -# If you run "mix test --cover", coverage assets end up here. -/cover/ - -# The directory Mix downloads your dependencies sources to. -/deps/ - -# Where third-party dependencies like ExDoc output generated docs. -/doc/ - -# Ignore .fetch files in case you like to edit your project deps locally. -/.fetch - -# If the VM crashes, it generates a dump, let's ignore it too. -erl_crash.dump - -# Also ignore archive artifacts (built via "mix archive.build"). -*.ez - -# Ignore package tarball (built via "mix hex.build"). -debug-*.tar - diff --git a/pkg/acceptance/testdata/elixir/test_crdb/mix.exs b/pkg/acceptance/testdata/elixir/test_crdb/mix.exs deleted file mode 100644 index e3a480c0b63..00000000000 --- a/pkg/acceptance/testdata/elixir/test_crdb/mix.exs +++ /dev/null @@ -1,22 +0,0 @@ -defmodule Cockroach.MixProject do - use Mix.Project - - def project do - [ - app: :debug, - version: "0.1.0", - start_permanent: Mix.env() == :prod, - deps: deps() - ] - end - - def application do - [ - extra_applications: [:logger] - ] - end - - defp deps do - [{:postgrex, "~> 0.13", hex: :postgrex_cdb, override: true}] - end -end diff --git a/pkg/acceptance/testdata/elixir/test_crdb/mix.lock b/pkg/acceptance/testdata/elixir/test_crdb/mix.lock deleted file mode 100644 index e1f3a9c0edd..00000000000 --- a/pkg/acceptance/testdata/elixir/test_crdb/mix.lock +++ /dev/null @@ -1,6 +0,0 @@ -%{ - "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm", "4a0850c9be22a43af9920a71ab17c051f5f7d45c209e40269a1938832510e4d9"}, - "db_connection": {:hex, :db_connection, "1.1.3", "89b30ca1ef0a3b469b1c779579590688561d586694a3ce8792985d4d7e575a61", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm", "5f0a16a58312a610d5eb0b07506280c65f5137868ad479045f2a2dc4ced80550"}, - "decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm", "3cb154b00225ac687f6cbd4acc4b7960027c757a5152b369923ead9ddbca7aec"}, - "postgrex": {:hex, :postgrex_cdb, "0.13.5", "83f818ea1b959d176c694bb60c36f42080c36a7413f0d3b05d58ef2093fe17f5", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "1a01ba25472ad33bafbac6f042fde2dbab93e23bdaa49ffa3722926165c1052f"}, -} diff --git a/pkg/acceptance/testdata/elixir/test_crdb/test/decimal_test.exs b/pkg/acceptance/testdata/elixir/test_crdb/test/decimal_test.exs deleted file mode 100644 index 5d84bc68edc..00000000000 --- a/pkg/acceptance/testdata/elixir/test_crdb/test/decimal_test.exs +++ /dev/null @@ -1,63 +0,0 @@ -defmodule Cockroach.TestDecimal do - use ExUnit.Case - - test "handles decimal correctly" do - ssl_opts = [certfile: System.get_env("PGSSLCERT"), keyfile: System.get_env("PGSSLKEY")] - {port, _} = Integer.parse(System.get_env("PGPORT") || "26257") - {start_ok, pid} = Postgrex.start_link( - hostname: System.get_env("PGHOST") || "localhost", - username: System.get_env("PGUSER") || "root", - password: "", - database: "testdb", - port: port, - ssl: (System.get_env("PGSSLCERT") != nil && true) || false, - ssl_opts: ssl_opts - ) - assert start_ok - for dec <- [ - Decimal.new("0"), - Decimal.new("0.0"), - Decimal.new("0.00"), - Decimal.new("0.000"), - Decimal.new("0.0000"), - Decimal.new("0.00000"), - Decimal.new("0.00001"), - Decimal.new("0.000012"), - Decimal.new("0.0000012"), - Decimal.new("0.00000012"), - Decimal.new("0.00000012"), - Decimal.new(".00000012"), - Decimal.new("1"), - Decimal.new("1.0"), - Decimal.new("1.000"), - Decimal.new("1.0000"), - Decimal.new("1.00000"), - Decimal.new("1.000001"), - Decimal.new("12345"), - Decimal.new("12345.0"), - Decimal.new("12345.000"), - Decimal.new("12345.0000"), - Decimal.new("12345.00000"), - Decimal.new("12345.000001"), - Decimal.new("12340"), - Decimal.new("123400"), - Decimal.new("1234000"), - Decimal.new("12340000"), - Decimal.new("12340.00"), - Decimal.new("123400.00"), - Decimal.new("1234000.00"), - Decimal.new("12340000.00"), - Decimal.new("1.2345e-10"), - Decimal.new("1.23450e-10"), - Decimal.new("1.234500e-10"), - Decimal.new("1.2345000e-10"), - Decimal.new("1.23450000e-10"), - Decimal.new("1.234500000e-10"), - ] do - {result_ok, result} = Postgrex.query(pid, "SELECT CAST($1 AS DECIMAL)", [dec]) - assert result_ok - [[ret]] = result.rows - assert ret == dec - end - end -end diff --git a/pkg/acceptance/testdata/elixir/test_crdb/test/test_helper.exs b/pkg/acceptance/testdata/elixir/test_crdb/test/test_helper.exs deleted file mode 100644 index 869559e709e..00000000000 --- a/pkg/acceptance/testdata/elixir/test_crdb/test/test_helper.exs +++ /dev/null @@ -1 +0,0 @@ -ExUnit.start() diff --git a/pkg/acceptance/testdata/java/.gitignore b/pkg/acceptance/testdata/java/.gitignore deleted file mode 100644 index 0616a511bfd..00000000000 --- a/pkg/acceptance/testdata/java/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.class -target -key.pk8 diff --git a/pkg/acceptance/testdata/java/README.md b/pkg/acceptance/testdata/java/README.md deleted file mode 100644 index a4ae1ff0e6e..00000000000 --- a/pkg/acceptance/testdata/java/README.md +++ /dev/null @@ -1,12 +0,0 @@ -## Java acceptance test - -On Mac, you can install Java and Maven with: - -```sh -brew cask install java -brew install maven -``` - -Run `mvn install` in this directory to get the jars locally for IDE support. - -Run `mvn test` to run the tests. diff --git a/pkg/acceptance/testdata/java/pom.xml b/pkg/acceptance/testdata/java/pom.xml deleted file mode 100644 index 93c1df3ea56..00000000000 --- a/pkg/acceptance/testdata/java/pom.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - 4.0.0 - - java - java - 1.0-SNAPSHOT - - - 1.8 - 1.8 - - - - - junit - junit - 4.8.1 - - - - org.postgresql - postgresql - 42.1.4 - - - - - src/main - - diff --git a/pkg/acceptance/testdata/java/src/main/java/CockroachDBTest.java b/pkg/acceptance/testdata/java/src/main/java/CockroachDBTest.java deleted file mode 100644 index 01ef0ebfe9d..00000000000 --- a/pkg/acceptance/testdata/java/src/main/java/CockroachDBTest.java +++ /dev/null @@ -1,66 +0,0 @@ -import org.junit.After; -import org.junit.Before; - -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.PreparedStatement; - -public abstract class CockroachDBTest { - protected Connection conn; - - @Before - public void setupConnection() throws Exception { - Class.forName("org.postgresql.Driver"); - - Connection conn; - conn = DriverManager.getConnection(getDBUrl(), "root", ""); - PreparedStatement stmt = conn.prepareStatement("CREATE DATABASE test"); - int res = stmt.executeUpdate(); - if (res != 0) { - throw new Exception("unexpected: CREATE DATABASE reports " + res + " rows changed, expecting 0"); - } - - stmt = conn.prepareStatement("USE test"); - stmt.executeUpdate(); - - this.conn = conn; - } - - public String getDBUrl() { - String DBUrl = "jdbc:postgresql://"; - DBUrl += pgHost() + ":" + pgPort(); - DBUrl += "/test"; - if (System.getenv("PGSSLCERT") != null) { - DBUrl += "?ssl=true"; - DBUrl += "&sslcert=" + System.getenv("PGSSLCERT"); - DBUrl += "&sslkey=/certs/client.root.key.pk8"; - DBUrl += "&sslrootcert=/certs/ca.crt"; - DBUrl += "&sslfactory=org.postgresql.ssl.jdbc4.LibPQFactory"; - } else { - DBUrl += "?sslmode=disable"; - } - return DBUrl; - } - - public String pgHost() { - String host = System.getenv("PGHOST"); - if (host == null) { - host = "localhost"; - } - return host; - } - - public String pgPort() { - String port = System.getenv("PGPORT"); - if (port == null) { - port = "26257"; - } - return port; - } - - @After - public void cleanup() throws Exception { - PreparedStatement stmt = this.conn.prepareStatement("DROP DATABASE test"); - stmt.execute(); - } -} diff --git a/pkg/acceptance/testdata/java/src/main/java/JSONTest.java b/pkg/acceptance/testdata/java/src/main/java/JSONTest.java deleted file mode 100644 index d56fc072871..00000000000 --- a/pkg/acceptance/testdata/java/src/main/java/JSONTest.java +++ /dev/null @@ -1,31 +0,0 @@ -import org.junit.Assert; -import org.junit.Test; - -import java.sql.Array; -import java.sql.PreparedStatement; -import java.sql.ResultSet; - -// jdbc doesn't actually have any special treatment of JSON, so -// let's just make sure the OID doesn't make it blow up or anything. -public class JSONTest extends CockroachDBTest { - @Test - public void testSelectJSON() throws Exception { - PreparedStatement stmt = conn.prepareStatement("SELECT '123'::JSONB"); - ResultSet rs = stmt.executeQuery(); - rs.next(); - Assert.assertEquals("123", rs.getString(1)); - } - - @Test - public void testInsertAndSelectJSON() throws Exception { - PreparedStatement stmt = conn.prepareStatement("CREATE TABLE x (j JSON)"); - stmt.executeUpdate(); - stmt = conn.prepareStatement("INSERT INTO x VALUES (?)"); - stmt.setString(1, "{\"a\":\"b\"}"); - stmt.executeUpdate(); - stmt = conn.prepareStatement("SELECT j FROM x"); - ResultSet rs = stmt.executeQuery(); - rs.next(); - Assert.assertEquals("{\"a\": \"b\"}", rs.getString(1)); - } -} diff --git a/pkg/acceptance/testdata/java/src/main/java/MainTest.java b/pkg/acceptance/testdata/java/src/main/java/MainTest.java deleted file mode 100644 index 6d3c43ee3c2..00000000000 --- a/pkg/acceptance/testdata/java/src/main/java/MainTest.java +++ /dev/null @@ -1,385 +0,0 @@ -import org.junit.*; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import static junit.framework.TestCase.fail; -import org.postgresql.util.PSQLException; - -import java.math.BigDecimal; -import java.sql.Array; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.ResultSetMetaData; -import java.sql.Statement; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - -public class MainTest extends CockroachDBTest { - @Rule - public ExpectedException exception = ExpectedException.none(); - - @Test - public void testNoOp() throws Exception { - // This is a test we can target when building the Docker image to ensure - // Maven downloads all packages necessary for testing before we enter - // offline mode. (Maven dependency:go-offline is not bulletproof, and - // leaves out surefire-junit.) - } - - @Test - public void testSimpleTable() throws Exception { - PreparedStatement stmt = conn.prepareStatement("CREATE TABLE f (x INT, ts TIMESTAMP)"); - int res = stmt.executeUpdate(); - Assert.assertEquals(0, res); - - stmt = conn.prepareStatement("INSERT INTO test.f VALUES (42, timestamp '2015-05-07 18:20:00')"); - res = stmt.executeUpdate(); - Assert.assertEquals(1, res); - - stmt = conn.prepareStatement("SELECT * FROM f"); - ResultSet rs = stmt.executeQuery(); - rs.next(); - Assert.assertEquals(42, rs.getInt(1)); - Assert.assertEquals("2015-05-07 18:20:00.0", rs.getTimestamp(2).toString()); - } - - @Test - public void testBindParameters() throws Exception { - PreparedStatement stmt = conn.prepareStatement( - "SELECT 1, 2 > ?, ?::int, ?::string, ?::string, ?::string, ?::string, ?::string" - ); - stmt.setInt(1, 3); - stmt.setInt(2, 3); - stmt.setBoolean(3, true); - stmt.setLong(4, -4L); - stmt.setFloat(5, 5.31f); - stmt.setDouble(6, -6.21d); - stmt.setShort(7, (short) 7); - - ResultSet rs = stmt.executeQuery(); - rs.next(); - Assert.assertEquals(1, rs.getInt(1)); - Assert.assertEquals(false, rs.getBoolean(2)); - Assert.assertEquals(3, rs.getInt(3)); - Assert.assertEquals("true", rs.getString(4)); - Assert.assertEquals(-4, rs.getLong(5)); - Assert.assertEquals(5.31, rs.getFloat(6), 0.01); - Assert.assertEquals(-6.21, rs.getFloat(7), 0.01); - Assert.assertEquals(7, rs.getShort(8)); - } - - @Test - public void testInsertWithParameters() throws Exception { - PreparedStatement stmt = conn.prepareStatement( - "CREATE TABLE accounts (id INT PRIMARY KEY, balance INT, cdate DATE)" - ); - int res = stmt.executeUpdate(); - Assert.assertEquals(0, res); - - stmt = conn.prepareStatement("INSERT INTO accounts (id, balance, cdate) VALUES ( ?, ?, ? )"); - stmt.setObject(1, 1); - stmt.setObject(2, 1000); - stmt.setObject(3, new java.sql.Date(System.currentTimeMillis())); - stmt.executeUpdate(); - - stmt = conn.prepareStatement("SELECT * FROM accounts"); - ResultSet rs = stmt.executeQuery(); - rs.next(); - Assert.assertEquals(1, rs.getInt(1)); - Assert.assertEquals(1000, rs.getInt(2)); - Assert.assertEquals(3, rs.getMetaData().getColumnCount()); - } - - @Test - public void testUpdate() throws Exception { - PreparedStatement stmt = conn.prepareStatement("CREATE TABLE str (s STRING)"); - stmt.executeUpdate(); - stmt = conn.prepareStatement("INSERT INTO str VALUES ('hello')"); - stmt.executeUpdate(); - stmt = conn.prepareStatement("UPDATE str SET s = ?"); - stmt.setString(1, "world"); - stmt.execute(); - stmt = conn.prepareStatement("SELECT * FROM str"); - ResultSet rs = stmt.executeQuery(); - rs.next(); - Assert.assertEquals("world", rs.getString(1)); - } - - @Test - public void testEmptyTable() throws Exception { - PreparedStatement stmt = conn.prepareStatement("CREATE TABLE empty()"); - int res = stmt.executeUpdate(); - Assert.assertEquals(0, res); - - stmt = conn.prepareStatement("SELECT * FROM empty"); - ResultSet rs = stmt.executeQuery(); - Assert.assertEquals(0, rs.getMetaData().getColumnCount()); - } - - @Test - public void testDecimal() throws Exception { - PreparedStatement stmt = conn.prepareStatement("SELECT 1.0::DECIMAL"); - ResultSet rs = stmt.executeQuery(); - rs.next(); - // The JDBC Postgres driver's getObject has different behavior than both - // its getString and getBigDecimal with respect to how it handles the type modifier: - // https://github.com/pgjdbc/pgjdbc/blob/REL42.1.1/pgjdbc/src/main/java/org/postgresql/jdbc/PgResultSet.java#L188 - Object dec = rs.getObject(1); - Assert.assertEquals("1.0", dec.toString()); - - stmt = conn.prepareStatement("SELECT 1e1::DECIMAL"); - rs = stmt.executeQuery(); - rs.next(); - BigDecimal bigdec = rs.getBigDecimal(1); - Assert.assertEquals("1E+1", bigdec.toString()); - } - - @Test - public void testIntTypes() throws Exception { - PreparedStatement stmt = conn.prepareStatement("CREATE TABLE x (a SMALLINT, b INT4, c INT, d BIGINT)"); - stmt.execute(); - stmt = conn.prepareStatement("INSERT INTO x VALUES (1, 1, 1, 1)"); - stmt.execute(); - stmt = conn.prepareStatement("SELECT a, b, c, d FROM x"); - ResultSet rs = stmt.executeQuery(); - rs.next(); - Short s = rs.getShort(1); - Assert.assertEquals(1, (short)s); - Integer i = rs.getInt(2); - Assert.assertEquals(1, (int)i); - Long l = rs.getLong(3); - Assert.assertEquals(1L, (long)l); - l = rs.getLong(4); - Assert.assertEquals(1L, (long)l); - } - - @Test - public void testFloatTypes() throws Exception { - PreparedStatement stmt = conn.prepareStatement("CREATE TABLE x (a FLOAT4, b FLOAT8)"); - stmt.execute(); - stmt = conn.prepareStatement("INSERT INTO x VALUES (1.2, 1.2)"); - stmt.execute(); - stmt = conn.prepareStatement("SELECT a, b FROM x"); - ResultSet rs = stmt.executeQuery(); - rs.next(); - Float f = rs.getFloat(1); - Assert.assertEquals((float)1.2, (float)f, 0.0001); - Double d = rs.getDouble(2); - Assert.assertEquals(1.2, (double)d, 0.0001); - } - - @Test - public void testTime() throws Exception { - PreparedStatement stmt = conn.prepareStatement("SELECT '01:02:03.456'::TIME"); - ResultSet rs = stmt.executeQuery(); - rs.next(); - String actual = new SimpleDateFormat("HH:mm:ss.SSS").format(rs.getTime(1)); - Assert.assertEquals("01:02:03.456", actual); - } - - @Test - public void testTimeTZ() throws Exception { - PreparedStatement stmt = conn.prepareStatement("SELECT '01:02:03.456-07:00'::TIMETZ"); - ResultSet rs = stmt.executeQuery(); - rs.next(); - String actual = new SimpleDateFormat("HH:mm:ss.SSSZ").format(rs.getTime(1)); - Assert.assertEquals("08:02:03.456+0000", actual); - } - - @Test - public void testUUID() throws Exception { - UUID uuid = UUID.randomUUID(); - PreparedStatement stmt = conn.prepareStatement("SELECT ?"); - stmt.setObject(1, uuid); - ResultSet rs = stmt.executeQuery(); - rs.next(); - Assert.assertEquals(uuid, rs.getObject(1)); - } - - @Test - public void testArrays() throws Exception { - PreparedStatement stmt = conn.prepareStatement("SELECT ?"); - Array array = conn.createArrayOf("FLOAT", new Double[]{1.0, 2.0, 3.0}); - stmt.setArray(1, array); - ResultSet rs = stmt.executeQuery(); - rs.next(); - Array ar = rs.getArray(1); - Double[] fs = (Double[]) ar.getArray(); - Assert.assertArrayEquals(new Double[]{1.0, 2.0, 3.0}, fs); - } - - @Test - public void testArrayWithProps() throws Exception { - PreparedStatement stmt = conn.prepareStatement("CREATE TABLE x (a SMALLINT[], b INT4[], c BIGINT[])"); - stmt.execute(); - stmt = conn.prepareStatement("INSERT INTO x VALUES (ARRAY[123], ARRAY[123], ARRAY[123])"); - stmt.execute(); - stmt = conn.prepareStatement("SELECT a, b, c FROM x"); - ResultSet rs = stmt.executeQuery(); - rs.next(); - - Array ar = rs.getArray(1); - Integer[] fs = (Integer[]) ar.getArray(); - Assert.assertArrayEquals(new Integer[]{123}, fs); - ar = rs.getArray(2); - fs = (Integer[]) ar.getArray(); - Assert.assertArrayEquals(new Integer[]{123}, fs); - ar = rs.getArray(3); - Long[] longs = (Long[]) ar.getArray(); - Assert.assertArrayEquals(new Long[]{123L}, longs); - } - - @Test - public void testStringArray() throws Exception { - PreparedStatement stmt = conn.prepareStatement("SELECT '{123,\"hello\",\"\\\"hello\\\"\"}'::STRING[]"); - ResultSet rs = stmt.executeQuery(); - rs.next(); - String[] result = (String[])rs.getArray(1).getArray(); - Assert.assertArrayEquals(new String[]{"123", "hello", "\"hello\""}, result); - } - - @Test - public void testSequence() throws Exception { - PreparedStatement createSeq = conn.prepareStatement("CREATE SEQUENCE foo"); - int res = createSeq.executeUpdate(); - Assert.assertEquals(res, 0); - - PreparedStatement getNextVal = conn.prepareStatement("SELECT nextval('foo')"); - - ResultSet rs = getNextVal.executeQuery(); - rs.next(); - Assert.assertEquals(rs.getInt(1), 1); - - rs = getNextVal.executeQuery(); - rs.next(); - Assert.assertEquals(rs.getInt(1), 2); - } - - @Test - public void selectLimitExplicitTxn() throws Exception { - conn.setAutoCommit(false); - PreparedStatement stmt = conn.prepareStatement("SELECT * from generate_series(1, 10)"); - stmt.setFetchSize(1); - ResultSet rs = stmt.executeQuery(); - rs.next(); - Assert.assertEquals(1, rs.getInt(1)); - rs.next(); - Assert.assertEquals(2, rs.getInt(1)); - rs.setFetchSize(0); - rs.next(); - Assert.assertEquals(3, rs.getInt(1)); - conn.setAutoCommit(true); - } - - // Regression for 30538: SQL query with wrong parameter value crashes - // database. Unlike the Go client, the JDBC client sets placeholder type - // hints. When these do not match the types inferred during type checking, - // placeholder eval will try to convert to the needed type. If the conversion - // fails, AssignPlaceholders needs to gracefully report that error rather than - // panicking. - @Test - public void testPlaceholderTypeError() throws Exception { - PreparedStatement stmt1 = conn.prepareStatement("CREATE TABLE x (a INT PRIMARY KEY, b UUID)"); - stmt1.execute(); - - // Send a UUID that's malformed (not long enough) and expect error. - PreparedStatement stmt = conn.prepareStatement("SELECT * FROM x WHERE b = ?"); - stmt.setObject(1, "e81bb788-2291-4b6e-9cf3-b237fe6c2f3"); - exception.expectMessage("ERROR: could not parse \"e81bb788-2291-4b6e-9cf3-b237fe6c2f3\" as " + - "type uuid: uuid: incorrect UUID format: e81bb788-2291-4b6e-9cf3-b237fe6c2f3"); - stmt.executeQuery(); - } - - // Regression for 33340: temporary columns used by window functions should - // be projected out after they are no longer necessary. This problem - // presented itself only when invoked via an external driver by crashing - // the server. - @Test - public void testWindowFunctions() throws Exception { - PreparedStatement stmt = conn.prepareStatement("CREATE TABLE t (a INT, b INT)"); - stmt.execute(); - stmt = conn.prepareStatement("INSERT INTO t VALUES (0, 10), (1, 11)"); - stmt.execute(); - stmt = conn.prepareStatement("SELECT sum(b) OVER (ORDER BY a) FROM t"); - ResultSet rs = stmt.executeQuery(); - rs.next(); - Assert.assertEquals(rs.getInt(1), 10); - rs.next(); - Assert.assertEquals(rs.getInt(1), 21); - } - - // Regression for 34429: empty string decimals shouldn't crash the server. - @Test - public void testEmptyStringDec() throws Exception { - PreparedStatement stmt = conn.prepareStatement("create table product_temp_tb (product_master_id int primary key, weight DECIMAL(10,2) NOT NULL DEFAULT 0)"); - stmt.execute(); - stmt = conn.prepareStatement("INSERT INTO product_temp_tb values(0,0)"); - stmt.execute(); - stmt = conn.prepareStatement("UPDATE product_temp_tb SET weight = ? WHERE product_master_id = ?"); - stmt.setObject(1, ""); - stmt.setInt(2, 1); - exception.expectMessage("ERROR: could not parse \"\" as type decimal"); - stmt.execute(); - } - - // Regression test for #60533: virtual table OIDs should work even they - // use a 32-bit int greater than MaxInt32. - @Test - public void testVirtualTableMetadata() throws Exception { - PreparedStatement p = conn.prepareStatement("select oid, proname from pg_proc limit 100"); - p.execute(); - ResultSet r = p.getResultSet(); - while (r.next()) { - ResultSetMetaData m = r.getMetaData(); - int colCount = m.getColumnCount(); - for (int i = 1; i <= colCount; i++) { - String tableName = m.getTableName(i); - Assert.assertEquals("pg_proc", tableName); - } - } - } - - // Regression test for #42912: using setFetchSize in a transaction should - // not cause issues. - @Test - public void testSetFetchSize() throws Exception { - for (int fetchSize = 0; fetchSize <= 3; fetchSize++) { - testSetFetchSize(fetchSize, true); - testSetFetchSize(fetchSize, false); - } - } - - private void testSetFetchSize(int fetchSize, boolean useTransaction) throws Exception { - int expectedResults = fetchSize; - if (fetchSize == 0 || fetchSize == 3) { - expectedResults = 2; - } - - try (final Connection testConn = DriverManager.getConnection(getDBUrl(), "root", "")) { - testConn.setAutoCommit(!useTransaction); - try (final Statement stmt = testConn.createStatement()) { - stmt.setFetchSize(fetchSize); - ResultSet result = stmt.executeQuery("select n from generate_series(0,1) n"); - for (int i = 0; i < expectedResults; i++) { - Assert.assertTrue(result.next()); - Assert.assertEquals(i, result.getInt(1)); - } - if (useTransaction) { - // This should implicitly close the ResultSet (i.e. portal). - testConn.commit(); - if (fetchSize != 0 && fetchSize != 3) { - try { - result.next(); - fail("expected portal to be closed"); - } catch (PSQLException e) { - Assert.assertTrue(e.getMessage().contains("unknown portal")); - } - } - } - } - } - } -} diff --git a/pkg/acceptance/testdata/java/src/main/java/NoopTest.java b/pkg/acceptance/testdata/java/src/main/java/NoopTest.java deleted file mode 100644 index b40a942dbd3..00000000000 --- a/pkg/acceptance/testdata/java/src/main/java/NoopTest.java +++ /dev/null @@ -1,11 +0,0 @@ -import org.junit.Test; - -public class NoopTest { - @Test - public void testNoop() throws Exception { - // This is a test we can target when building the Docker image to ensure - // Maven downloads all packages necessary for testing before we enter - // offline mode. (Maven dependency:go-offline is not bulletproof, and - // leaves out surefire-junit.) - } -} diff --git a/pkg/acceptance/testdata/node/.gitignore b/pkg/acceptance/testdata/node/.gitignore deleted file mode 100644 index c2658d7d1b3..00000000000 --- a/pkg/acceptance/testdata/node/.gitignore +++ /dev/null @@ -1 +0,0 @@ -node_modules/ diff --git a/pkg/acceptance/testdata/node/README.md b/pkg/acceptance/testdata/node/README.md deleted file mode 100644 index b386ef975fc..00000000000 --- a/pkg/acceptance/testdata/node/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Node Acceptance Test - -This test runs the `pg` driver against Cockroach. -If it detects it's running in an acceptance test it will run against the -assigned env vars, otherwise it will run against a locally running Cockroach -(`localhost:26257`) to allow quicker feedback cycles. - -## To run tests locally: - -* Run a Cockroach instance on the default port -* `yarn && yarn test` - -## To add a dependency - -We don't want to install the deps on every CI run, so to add a dependency to -this test, you must: - -* Add it to the `package.json` so that anyone running the tests locally will be - able to install it. -* Rebuild the cockroach-acceptance container (see [../Dockerfile]), which will - automatically bake in the new dependency. - -[../Dockerfile]: ../Dockerfile diff --git a/pkg/acceptance/testdata/node/base-test.js b/pkg/acceptance/testdata/node/base-test.js deleted file mode 100644 index 485601b1233..00000000000 --- a/pkg/acceptance/testdata/node/base-test.js +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2019 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -const fs = require('fs'); -const assert = require('assert'); -const rejectsWithPGError = require('./rejects-with-pg-error'); -const client = require('./client'); - -// We orchestrate a failure here to ensure that a failing test actually causes -// the docker build to fail. -if (process.env.SHOULD_FAIL) { - describe('failure smoke test', () => { - it('causes the docker build to fail on a test failure', () => { - assert.fail(); - }); - }); -} - -describe('select', () => { - it('lets you select values', () => { - return client.query("SELECT 1 as first, 2+$1 as second, ARRAY['\"','',''] as third", [3]) - .then(results => { - assert.deepEqual(results.rows, [{ - first: 1, - second: 5, - third: ['"', '', ''] - }]); - }); - }); -}); - -describe('error cases', () => { - const cases = [{ - name: 'not enough params', - query: { text: 'SELECT 3', values: ['foo'] }, - msg: "expected 0 arguments, got 1", - code: '08P01', - }, { - name: 'invalid utf8', - query: { text: 'SELECT $1::STRING', values: [new Buffer([167])] }, - msg: "invalid UTF-8 sequence", - code: '22021', - }]; - - cases.forEach(({ name, query, msg, code }) => { - it(`${name} # ${query.text}`, () => { - return rejectsWithPGError(client.query(query), { msg, code }); - }); - }); -}); - -const NUMERIC_TYPES = ['INT', 'FLOAT', 'DECIMAL']; - -describe('arrays', () => { - it('can be selected', () => { - return client.query('SELECT ARRAY[1, 2, 3] a') - .then(results => { - assert.deepEqual([1, 2, 3], results.rows[0].a); - }); - }); - - NUMERIC_TYPES.forEach(t => { - it(`can be passed as a placeholder for a ${t}[]`, () => { - return client.query(`SELECT $1:::${t}[] a`, [[1, 2, 3]]) - .then(results => { - assert.deepEqual([1, 2, 3], results.rows[0].a); - }); - }); - }); -}); - -describe('regression tests', () => { - it('allows you to switch between format modes for arrays', () => { - return client.query({ - text: 'SELECT $1:::int[] as b', - values: [[1, 2, 8]], - binary: false, - }).then(r => { - return client.query({ - text: 'SELECT $1:::int[] a', - values: [[4, 5, 6]], - binary: true, - }); - }).then(results => { - assert.deepEqual([4, 5, 6], results.rows[0].a); - }); - }); -}) diff --git a/pkg/acceptance/testdata/node/client.js b/pkg/acceptance/testdata/node/client.js deleted file mode 100644 index b362303022d..00000000000 --- a/pkg/acceptance/testdata/node/client.js +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2019 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -const pg = require('pg'); -const fs = require('fs'); - -// If we are running within the acceptance tests, we should use the env vars -// they've set. Otherwise, assume that we just want to run against a local -// Cockroach instance. -const config = { - user: 'root', - host: process.env.PGHOST || 'localhost', - port: process.env.PGPORT || 26257, -}; -if (process.env.PGSSLCERT && process.env.PGSSLKEY) { - config.ssl = { - cert: fs.readFileSync(process.env.PGSSLCERT), - key: fs.readFileSync(process.env.PGSSLKEY), - }; -} - -const client = new pg.Client(config); - -before(done => { - client - .connect() - .then(() => { - return client.query('DROP DATABASE IF EXISTS node_test'); - }) - .then(() => { - return client.query('CREATE DATABASE node_test'); - }) - .then(() => { - return client.query('USE node_test'); - }) - .then(() => { - done(); - }); -}); - -after(() => { - client.query('DROP DATABASE IF EXISTS node_test').catch(() => {}); - client.query('USE ""').catch(() => {}); - client.end(); -}); - -module.exports = client; diff --git a/pkg/acceptance/testdata/node/json-test.js b/pkg/acceptance/testdata/node/json-test.js deleted file mode 100644 index ad6000cc136..00000000000 --- a/pkg/acceptance/testdata/node/json-test.js +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2019 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -const assert = require('assert'); -const client = require('./client'); -const rejectsWithPGError = require('./rejects-with-pg-error'); - -['JSON', 'JSONB'].forEach(t => { - describe(t, () => { - describe('round-tripping a value', () => { - const cases = [ - `123`, - `"hello"`, - `{}`, - `[]`, - `0`, - `0.0000`, - `""`, - '"\uD83D\uDE80"', - '{"\uD83D\uDE80": "hello"}', - `[1, 2, 3]`, - `{"foo": 123}`, - ]; - - before(() => { - return client.query(`CREATE TABLE x (j ${t})`); - }); - - after(() => { - return client.query(`DROP TABLE x`); - }); - - cases.forEach(json => { - describe(json, () => { - beforeEach(() => { - return client.query(`DELETE FROM x`); - }); - it(`can be selected directly`, () => { - return client.query(`SELECT $1::${t} j`, [json]).then(results => { - assert.deepStrictEqual(results.rows[0].j, JSON.parse(json)); - }); - }); - - it(`can be inserted into a table and then retrieved`, () => { - return client.query(`INSERT INTO x VALUES ($1)`, [json]) - .then(() => client.query(`SELECT j FROM x`)) - .then(results => { - assert.deepStrictEqual(results.rows[0].j, JSON.parse(json)); - }) - }); - }); - }); - }); - - it('gives the right error code on invalid JSON', () => { - return rejectsWithPGError( - client.query({text: `SELECT '{"foo": 123'::JSONB`}), - {msg: 'unexpected EOF', code: '22P02'} - ); - }); - }); -}); diff --git a/pkg/acceptance/testdata/node/package.json b/pkg/acceptance/testdata/node/package.json deleted file mode 100644 index b38d5140500..00000000000 --- a/pkg/acceptance/testdata/node/package.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "cockroach-db-test", - "version": "1.0.0", - "description": "CockroachDB integration tests", - "main": "", - "scripts": { - "test": "mocha ." - }, - "author": "Cockroach Labs", - "license": "UNLICENSED", - "devDependencies": { - "mocha": "4.0.1" - }, - "dependencies": { - "pg": "7.3.0", - "pg-error-codes": "1.0.0", - "sequelize": "^4.37.1", - "sequelize-cockroachdb": "^1.0.2" - }, - "resolutions": { - "**/lodash": "^4.17.11" - } -} diff --git a/pkg/acceptance/testdata/node/rejects-with-pg-error.js b/pkg/acceptance/testdata/node/rejects-with-pg-error.js deleted file mode 100644 index 49f5e54046b..00000000000 --- a/pkg/acceptance/testdata/node/rejects-with-pg-error.js +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2019 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -const assert = require('assert'); -const errorCodes = require('pg-error-codes'); - -function errCode(code) { - return errorCodes[code] || 'unknown error code'; -} - -// rejectsWithPGError takes a promise produced from a pg query and rejects if it -// fails with a superstring of an asserted error message and a specified -// Postgres error code. -function rejectsWithPGError(promise, { msg, code }) { - return promise.then( - () => { - assert.fail(`expected failure with message "${msg}" and Postgres error code ${code}, but no failure occurred`); - }, - err => { - assert(err.toString().indexOf(msg) > -1, `expected "${err.toString()}" to contain "${msg}"`); - assert.equal(code, err.code, `expected error code to be ${code} (${errCode(code)}), was ${err.code} (${errCode(err.code)})`); - } - ); -} - -module.exports = rejectsWithPGError; diff --git a/pkg/acceptance/testdata/node/sequelize-test.js b/pkg/acceptance/testdata/node/sequelize-test.js deleted file mode 100644 index 90baa9f3955..00000000000 --- a/pkg/acceptance/testdata/node/sequelize-test.js +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2019 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -const assert = require('assert'); -const fs = require('fs'); - -const Sequelize = require('sequelize-cockroachdb'); -const Op = Sequelize.Op; - -const config = { - dialect: 'postgres', - host: process.env.PGHOST || 'localhost', - port: process.env.PGPORT || 26257, - logging: false, -}; - -if (process.env.PGSSLCERT && process.env.PGSSLKEY) { - config.ssl = true; - config.dialectOptions = { - ssl: { - cert: fs.readFileSync(process.env.PGSSLCERT), - key: fs.readFileSync(process.env.PGSSLKEY) - } - }; -} - -const sequelize = new Sequelize('node_test', 'root', '', config); - -describe('sequelize', () => { - after(() => { - sequelize.close(); - }); - - it('can create a model with a json field', () => { - var Cat = sequelize.define('cat', { - id: {type: Sequelize.INTEGER, primaryKey: true}, - data: {type: Sequelize.JSONB}, - }); - - return Cat.sync({force: true}) - .then(() => { - return Cat.bulkCreate([ - {id: 1, data: {name: 'smudge'}}, - {id: 2, data: {name: 'sissel'}}, - ]); - }) - .then(() => { - return Cat.findAll(); - }) - .then(result => { - assert.deepEqual(result[0].dataValues.id, 1); - assert.deepEqual(result[0].dataValues.data, {name: 'smudge'}); - assert.deepEqual(result[1].dataValues.id, 2); - assert.deepEqual(result[1].dataValues.data, {name: 'sissel'}); - }); - }); - - it('can create a model with an inverted index', () => { - var Android = sequelize.define( - 'androids', - { - id: {type: Sequelize.INTEGER, primaryKey: true}, - data: {type: Sequelize.JSONB}, - }, - { - // Not sure of a good but not fragile way to verify that this index was - // actually created. - indexes: [ - { - fields: ['data'], - using: 'gin', - }, - ], - } - ); - - return Android.sync({force: true}) - .then(() => { - return Android.bulkCreate([ - {id: 1, data: {name: '2B'}}, - {id: 2, data: {name: '9S'}}, - ]); - }) - .then(() => { - return Android.findAll({ - where: { - data: { - [Op.contains]: {name: '2B'}, - }, - }, - }); - }) - .then(result => { - assert.deepEqual(result[0].dataValues.id, 1); - assert.deepEqual(result[0].dataValues.data, {name: '2B'}); - }); - }); -}); diff --git a/pkg/acceptance/testdata/node/yarn.lock b/pkg/acceptance/testdata/node/yarn.lock deleted file mode 100644 index 0c10a60aedf..00000000000 --- a/pkg/acceptance/testdata/node/yarn.lock +++ /dev/null @@ -1,397 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@types/geojson@^1.0.0": - version "1.0.6" - resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-1.0.6.tgz#3e02972728c69248c2af08d60a48cbb8680fffdf" - -"@types/node@*": - version "9.4.7" - resolved "http://registry.npmjs.org/@types/node/-/node-9.4.7.tgz#57d81cd98719df2c9de118f2d5f3b1120dcd7275" - -balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - -bluebird@^3.4.6, bluebird@^3.5.0: - version "3.5.1" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" - -brace-expansion@^1.1.7: - version "1.1.8" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -browser-stdout@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f" - -buffer-writer@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-1.0.1.tgz#22a936901e3029afcd7547eb4487ceb697a3bf08" - -cls-bluebird@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cls-bluebird/-/cls-bluebird-2.1.0.tgz#37ef1e080a8ffb55c2f4164f536f1919e7968aee" - dependencies: - is-bluebird "^1.0.2" - shimmer "^1.1.0" - -commander@2.11.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563" - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - -debug@3.1.0, debug@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" - dependencies: - ms "2.0.0" - -debug@^2.6.9: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - dependencies: - ms "2.0.0" - -depd@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - -diff@3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/diff/-/diff-3.3.1.tgz#aa8567a6eed03c531fc89d3f711cd0e5259dec75" - -dottie@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/dottie/-/dottie-2.0.0.tgz#da191981c8b8d713ca0115d5898cf397c2f0ddd0" - -escape-string-regexp@1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - -generic-pool@2.4.3: - version "2.4.3" - resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-2.4.3.tgz#780c36f69dfad05a5a045dd37be7adca11a4f6ff" - -generic-pool@^3.4.0: - version "3.4.2" - resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-3.4.2.tgz#92ff7196520d670839a67308092a12aadf2f6a59" - -glob@7.1.2: - version "7.1.2" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -growl@1.10.3: - version "1.10.3" - resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.3.tgz#1926ba90cf3edfe2adb4927f5880bc22c66c790f" - -has-flag@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" - -he@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" - -inflection@1.12.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/inflection/-/inflection-1.12.0.tgz#a200935656d6f5f6bc4dc7502e1aecb703228416" - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - -is-bluebird@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-bluebird/-/is-bluebird-1.0.2.tgz#096439060f4aa411abee19143a84d6a55346d6e2" - -js-string-escape@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef" - -lodash@^4.17.1, lodash@^4.17.11: - version "4.17.11" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" - integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== - -minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - dependencies: - brace-expansion "^1.1.7" - -minimist@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" - -mkdirp@0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" - dependencies: - minimist "0.0.8" - -mocha@4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-4.0.1.tgz#0aee5a95cf69a4618820f5e51fa31717117daf1b" - dependencies: - browser-stdout "1.3.0" - commander "2.11.0" - debug "3.1.0" - diff "3.3.1" - escape-string-regexp "1.0.5" - glob "7.1.2" - growl "1.10.3" - he "1.1.1" - mkdirp "0.5.1" - supports-color "4.4.0" - -moment-timezone@^0.5.14: - version "0.5.14" - resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.14.tgz#4eb38ff9538b80108ba467a458f3ed4268ccfcb1" - dependencies: - moment ">= 2.9.0" - -"moment@>= 2.9.0", moment@^2.20.0: - version "2.21.0" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.21.0.tgz#2a114b51d2a6ec9e6d83cf803f838a878d8a023a" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - -object-assign@4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" - -once@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - dependencies: - wrappy "1" - -packet-reader@0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/packet-reader/-/packet-reader-0.3.1.tgz#cd62e60af8d7fea8a705ec4ff990871c46871f27" - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - -pg-connection-string@0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-0.1.3.tgz#da1847b20940e42ee1492beaf65d49d91b245df7" - -pg-error-codes@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/pg-error-codes/-/pg-error-codes-1.0.0.tgz#cfba6fa653a695bd8883fc69942e84a58decfccb" - -pg-int8@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c" - -pg-pool@1.*: - version "1.8.0" - resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-1.8.0.tgz#f7ec73824c37a03f076f51bfdf70e340147c4f37" - dependencies: - generic-pool "2.4.3" - object-assign "4.1.0" - -pg-pool@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-2.0.3.tgz#c022032c8949f312a4f91fb6409ce04076be3257" - -pg-types@1.*: - version "1.13.0" - resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-1.13.0.tgz#75f490b8a8abf75f1386ef5ec4455ecf6b345c63" - dependencies: - pg-int8 "1.0.1" - postgres-array "~1.0.0" - postgres-bytea "~1.0.0" - postgres-date "~1.0.0" - postgres-interval "^1.1.0" - -pg-types@~1.12.1: - version "1.12.1" - resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-1.12.1.tgz#d64087e3903b58ffaad279e7595c52208a14c3d2" - dependencies: - postgres-array "~1.0.0" - postgres-bytea "~1.0.0" - postgres-date "~1.0.0" - postgres-interval "^1.1.0" - -pg@7.3.0: - version "7.3.0" - resolved "https://registry.yarnpkg.com/pg/-/pg-7.3.0.tgz#275e27466e54a645f6b4a16f6acadf6b849ad83b" - dependencies: - buffer-writer "1.0.1" - js-string-escape "1.0.1" - packet-reader "0.3.1" - pg-connection-string "0.1.3" - pg-pool "~2.0.3" - pg-types "~1.12.1" - pgpass "1.x" - semver "4.3.2" - -pg@^6.1.3: - version "6.4.2" - resolved "https://registry.yarnpkg.com/pg/-/pg-6.4.2.tgz#c364011060eac7a507a2ae063eb857ece910e27f" - dependencies: - buffer-writer "1.0.1" - js-string-escape "1.0.1" - packet-reader "0.3.1" - pg-connection-string "0.1.3" - pg-pool "1.*" - pg-types "1.*" - pgpass "1.*" - semver "4.3.2" - -pgpass@1.*, pgpass@1.x: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pgpass/-/pgpass-1.0.2.tgz#2a7bb41b6065b67907e91da1b07c1847c877b306" - dependencies: - split "^1.0.0" - -postgres-array@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-1.0.2.tgz#8e0b32eb03bf77a5c0a7851e0441c169a256a238" - -postgres-bytea@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/postgres-bytea/-/postgres-bytea-1.0.0.tgz#027b533c0aa890e26d172d47cf9ccecc521acd35" - -postgres-date@~1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/postgres-date/-/postgres-date-1.0.3.tgz#e2d89702efdb258ff9d9cee0fe91bd06975257a8" - -postgres-interval@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/postgres-interval/-/postgres-interval-1.1.1.tgz#acdb0f897b4b1c6e496d9d4e0a853e1c428f06f0" - dependencies: - xtend "^4.0.0" - -retry-as-promised@^2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/retry-as-promised/-/retry-as-promised-2.3.2.tgz#cd974ee4fd9b5fe03cbf31871ee48221c07737b7" - dependencies: - bluebird "^3.4.6" - debug "^2.6.9" - -semver@4.3.2: - version "4.3.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.2.tgz#c7a07158a80bedd052355b770d82d6640f803be7" - -semver@^5.5.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" - -sequelize-cockroachdb@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/sequelize-cockroachdb/-/sequelize-cockroachdb-1.0.2.tgz#0f0d0f67f182fcff66e74896f3e10cefffffb620" - dependencies: - pg "^6.1.3" - sequelize "3.30.2 - 4" - -"sequelize@3.30.2 - 4", sequelize@^4.37.1: - version "4.37.1" - resolved "https://registry.yarnpkg.com/sequelize/-/sequelize-4.37.1.tgz#17aa97f269b7621015c73e77aa6b2134f45da7d7" - dependencies: - bluebird "^3.5.0" - cls-bluebird "^2.1.0" - debug "^3.1.0" - depd "^1.1.0" - dottie "^2.0.0" - generic-pool "^3.4.0" - inflection "1.12.0" - lodash "^4.17.1" - moment "^2.20.0" - moment-timezone "^0.5.14" - retry-as-promised "^2.3.2" - semver "^5.5.0" - terraformer-wkt-parser "^1.1.2" - toposort-class "^1.0.1" - uuid "^3.2.1" - validator "^9.4.1" - wkx "^0.4.1" - -shimmer@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.0.tgz#f966f7555789763e74d8841193685a5e78736665" - -split@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9" - dependencies: - through "2" - -supports-color@4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.4.0.tgz#883f7ddabc165142b2a61427f3352ded195d1a3e" - dependencies: - has-flag "^2.0.0" - -terraformer-wkt-parser@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/terraformer-wkt-parser/-/terraformer-wkt-parser-1.1.2.tgz#336a0c8fc82094a5aff83288f69aedecd369bf0c" - dependencies: - terraformer "~1.0.5" - -terraformer@~1.0.5: - version "1.0.8" - resolved "https://registry.yarnpkg.com/terraformer/-/terraformer-1.0.8.tgz#51e0ad89746fcf2161dc6f65aa70e42377c8b593" - dependencies: - "@types/geojson" "^1.0.0" - -through@2: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - -toposort-class@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/toposort-class/-/toposort-class-1.0.1.tgz#7ffd1f78c8be28c3ba45cd4e1a3f5ee193bd9988" - -uuid@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14" - -validator@^9.4.1: - version "9.4.1" - resolved "https://registry.yarnpkg.com/validator/-/validator-9.4.1.tgz#abf466d398b561cd243050112c6ff1de6cc12663" - -wkx@^0.4.1: - version "0.4.4" - resolved "https://registry.yarnpkg.com/wkx/-/wkx-0.4.4.tgz#cf751b672e4b45e162f9fd30124878e73d96c9b2" - dependencies: - "@types/node" "*" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - -xtend@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" diff --git a/pkg/acceptance/testdata/php/test.php b/pkg/acceptance/testdata/php/test.php deleted file mode 100644 index c941147e51d..00000000000 --- a/pkg/acceptance/testdata/php/test.php +++ /dev/null @@ -1,35 +0,0 @@ - $1, $1', [intval($argv[1])]) - or kill('Query failed: ' . pg_last_error()); -$arr = pg_fetch_row($result); -($arr === ['1', 'f', '3']) or kill('Unexpected: ' . print_r($arr, true)); - -$dbh = new PDO('pgsql:','root', null, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION)); -$dbh->exec('CREATE database bank'); -$dbh->exec('CREATE table bank.accounts (id INT PRIMARY KEY, balance INT)'); -$dbh->exec('INSERT INTO bank.accounts (id, balance) VALUES (1, 1000), (2, 250)'); -$dbh->beginTransaction(); -$stmt = $dbh->prepare('UPDATE bank.accounts SET balance = balance + :deposit WHERE id=:account'); -$stmt->execute(array('account' => 1, 'deposit' => 10)); -$stmt->execute(array('account' => 2, 'deposit' => -10)); -$dbh->commit(); - -// Regression test for #59007. -$stmt = $dbh->prepare("insert into a_table (id, a) select ?, ?, ?, ? returning id"); -$stmt->bindValue(1, 'ed66e7c0-5c39-11eb-8992-89bd28f48e75'); -$stmt->bindValue(2, 'bugging_a'); -$stmt->bindValue(3, 'bugging_b'); -try { - $stmt->execute(); - assert(false, "expected exception in execute"); -} catch (Exception $e) { - assert(strpos($e.getMessage(), "expected 4 arguments, got 3")); -} diff --git a/pkg/acceptance/testdata/psql/test-psql-notls.sh b/pkg/acceptance/testdata/psql/test-psql-notls.sh deleted file mode 100755 index 0be261ef3b1..00000000000 --- a/pkg/acceptance/testdata/psql/test-psql-notls.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env bash - -CERTS_DIR=${CERTS_DIR:-/certs} -crdb=$1 -trap "set -x; killall cockroach cockroachshort || true" EXIT HUP - -set -euo pipefail - -# Disable automatic network access by psql. -unset PGHOST -unset PGPORT -# Use root access. -export PGUSER=root - -set +x -echo "Testing non-TLS TCP connection via secure server." -set -x - -# Start a server in secure mode and allow non-TLS SQL clients. -"$crdb" start-single-node --background \ - --certs-dir="$CERTS_DIR" --socket-dir=/tmp \ - --accept-sql-without-tls \ - --listen-addr=:12345 - -# Wait for server ready; also create a user that can log in. -"$crdb" sql --certs-dir="$CERTS_DIR" -e "create user foo with password 'pass'" -p 12345 - -# verify that psql can connect to the server without TLS but auth -# fails if they present the wrong password. -(env PGPASSWORD=wrongpass psql 'postgres://foo@localhost:12345?sslmode=disable' -c "select 1" 2>&1 || true) | grep "password authentication failed" - -# now verify that psql can connect to the server without TLS with -# the proper password. -env PGPASSWORD=pass psql 'postgres://foo@localhost:12345?sslmode=disable' -c "select 1" | grep "1 row" - -set +x -# Done. -"$crdb" quit --certs-dir="$CERTS_DIR" -p 12345 diff --git a/pkg/acceptance/testdata/psql/test-psql-unix.sh b/pkg/acceptance/testdata/psql/test-psql-unix.sh deleted file mode 100755 index dd90dacef51..00000000000 --- a/pkg/acceptance/testdata/psql/test-psql-unix.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env bash - -CERTS_DIR=${CERTS_DIR:-/certs} -crdb=$1 -trap "set -x; killall cockroach cockroachshort || true" EXIT HUP - -set -euo pipefail - -# Disable automatic network access by psql. -unset PGHOST -unset PGPORT -# Use root access. -export PGUSER=root - -echo "Testing Unix socket connection via insecure server." -set -x - -# Start an insecure CockroachDB server. -# We use a different port number from standard for an extra guarantee that -# "psql" is not going to find it. -"$crdb" start-single-node --background --insecure \ - --socket-dir=/tmp \ - --listen-addr=:12345 - -# Wait for server ready. -"$crdb" sql --insecure -e "select 1" -p 12345 - -# Verify that psql can connect to the server. -psql -h /tmp -p 12345 -c "select 1" | grep "1 row" - -# It worked. -"$crdb" quit --insecure -p 12345 -sleep 1; killall -9 cockroach cockroachshort || true - -set +x -echo "Testing Unix socket connection via secure server." -set -x - -# Restart the server in secure mode. -"$crdb" start-single-node --background \ - --certs-dir="$CERTS_DIR" --socket-dir=/tmp \ - --listen-addr=:12345 - -# Wait for server ready; also create a user that can log in. -"$crdb" sql --certs-dir="$CERTS_DIR" -e "create user foo with password 'pass'" -p 12345 - -# Also verify that psql can connect to the server. -env PGPASSWORD=pass psql -U foo -h /tmp -p 12345 -c "select 1" | grep "1 row" - -set +x -# Done. -"$crdb" quit --certs-dir="$CERTS_DIR" -p 12345 diff --git a/pkg/acceptance/testdata/psql/test-psql.sh b/pkg/acceptance/testdata/psql/test-psql.sh deleted file mode 100755 index 6581f012961..00000000000 --- a/pkg/acceptance/testdata/psql/test-psql.sh +++ /dev/null @@ -1,89 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -# Check that psql works in the first place. -psql -c "select 1" | grep "1 row" - -# Check that COPY works outside of a transaction (#13395) -psql -d testdb < import.sql -echo -n -e '3\trope\tgreen\teast\t2015-01-02\t192.168.0.1' >> import.sql -psql -d testdb < import.sql -psql -d testdb -c "SELECT * FROM playground" | grep green -psql -d testdb -c "SELECT * FROM playground" | grep 192.168.0.1 - -# Test lack of newlines at EOF with slash-dot. -echo 'COPY playground (equip_id, type, color, location, install_date, ip) FROM stdin;' > import.sql -echo -e '4\tsand\tbrown\twest\t2016-03-04\t192.168.0.1' >> import.sql -echo -n '\.' >> import.sql -psql -d testdb < import.sql -psql -d testdb -c "SELECT * FROM playground" | grep brown - -# Test that the app name set in the pgwire init exchange is propagated -# down the session. -psql -d testdb -c "show application_name" | grep psql - -# Test that errors in COPY FROM STDIN don't screw up the connection -# See #16393 -echo 'COPY playground (equip_id, type, color, location, install_date, ip) FROM stdin;' > import.sql -echo -e '3\tjunk\tgreen\teast\t2015-01-02\t192.168.0.1' >> import.sql -echo 'garbage' >> import.sql -echo '\.' >> import.sql -echo "SELECT 'hooray'" >> import.sql -psql -d testdb < import.sql | grep hooray -# Assert the junk line wasn't added. -psql -d testdb -c "SELECT * from playground WHERE type='junk'" | grep "0 rows" - -# Test that large COPY FROM STDIN commands don't create a bad connection status. -# See issue #17941. -echo 'COPY ints FROM stdin;' > import.sql -for i in {1..1000}; do - echo $i >> import.sql -done -echo "\." >> import.sql -psql -d testdb < import.sql -psql -d testdb -c "SELECT count(*) FROM ints" | grep "1000" - -# Test that a row larger than 8192 bytes is handled OK. That's when psql splits -# it into multiple packets. -echo "Testing large row" -psql -d testdb -c "create table large_strings (s string)" -row=$(eval printf '=%.0s' {1..10000}) -echo 'copy large_strings from stdin;' > import.sql -echo $row>> import.sql -echo "\." >> import.sql -psql -d testdb < import.sql -psql -d testdb -c "select count(*) from large_strings" | grep "1" -psql -d testdb -c "select length(s) from large_strings" | grep "10000" - -# Test that attempting to copy into a missing table returns the expected error -# to the client. It didn't use to. -echo 'Testing copy error' -output="$(psql -d testdb -c 'copy missing from stdin' 2>&1 || true)" -echo $output | grep 'relation "missing" does not exist' - -# Test that CREATE TABLE AS returns tag CREATE TABLE AS, not CREATE (#20227). -psql -d testdb -c "CREATE TABLE ctas AS SELECT 1" | grep "CREATE TABLE AS" diff --git a/pkg/acceptance/testdata/python/test.py b/pkg/acceptance/testdata/python/test.py deleted file mode 100644 index dbc6d67f9df..00000000000 --- a/pkg/acceptance/testdata/python/test.py +++ /dev/null @@ -1,44 +0,0 @@ -import decimal -import sys -import psycopg2 - -conn = psycopg2.connect('') -cur = conn.cursor() -cur.execute("SELECT 1, 2+{}".format(sys.argv[1])) -v = cur.fetchall() -assert v == [(1, 5)] - -# Verify #6597 (timestamp format) is fixed. -cur = conn.cursor() -cur.execute("SELECT now()") -v = cur.fetchall() - -# Verify round-trip of strings containing backslashes. -# https://github.com/cockroachdb/cockroachdb-python/issues/23 -s = ('\\\\',) -cur.execute("SELECT %s", s) -v = cur.fetchall() -assert v == [s], (v, s) - -# Verify decimals with exponents can be parsed. -cur = conn.cursor() -cur.execute("SELECT 1e1::decimal") -v = cur.fetchall() -d = v[0][0] -assert type(d) is decimal.Decimal -# Use of compare_total here guarantees that we didn't just get '10' back, we got '1e1'. -assert d.compare_total(decimal.Decimal('1e1')) == 0 - -# Verify arrays with strings can be parsed. -cur = conn.cursor() -cur.execute("SELECT ARRAY['foo','bar','baz']") -v = cur.fetchall() -d = v[0][0] -assert d == ["foo","bar","baz"] - -# Verify JSON values come through properly. -cur = conn.cursor() -cur.execute("SELECT '{\"a\":\"b\"}'::JSONB") -v = cur.fetchall() -d = v[0][0] -assert d == {"a": "b"} diff --git a/pkg/acceptance/testdata/ruby/test.rb b/pkg/acceptance/testdata/ruby/test.rb deleted file mode 100644 index cf0ef0b16f4..00000000000 --- a/pkg/acceptance/testdata/ruby/test.rb +++ /dev/null @@ -1,8 +0,0 @@ -require 'pg' - -conn = PG.connect() -res = conn.exec_params('SELECT 1, 2 > $1, $1', [ARGV[0].to_i]) -raise 'Unexpected: ' + res.values.to_s unless res.values == [["1", "f", "3"]] - -res = conn.exec('SELECT 1e1::decimal') -raise 'Unexpected: ' + res.values.to_s unless res.values == [["1E+1"]] diff --git a/pkg/acceptance/util_cluster.go b/pkg/acceptance/util_cluster.go deleted file mode 100644 index f57f6938977..00000000000 --- a/pkg/acceptance/util_cluster.go +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright 2015 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -package acceptance - -import ( - "context" - "path/filepath" - "regexp" - "strings" - "testing" - "time" - - "github.com/cockroachdb/cockroach/pkg/acceptance/cluster" - "github.com/cockroachdb/cockroach/pkg/testutils" - "github.com/cockroachdb/cockroach/pkg/util/log" - "github.com/cockroachdb/cockroach/pkg/util/stop" - "github.com/cockroachdb/errors" -) - -const ( - dockerTest = "runMode=docker" -) - -var stopper = stop.NewStopper() - -// RunDocker runs the given acceptance test using a Docker cluster. -func RunDocker(t *testing.T, testee func(t *testing.T)) { - t.Run(dockerTest, testee) -} - -// turns someTest#123 into someTest when invoked with ReplicaAllLiteralString. -// This is useful because the go test harness automatically disambiguates -// subtests in that way when they are invoked multiple times with the same name, -// and we sometimes call RunDocker multiple times in tests. -var reStripTestEnumeration = regexp.MustCompile(`#\d+$`) - -// StartCluster starts a cluster from the relevant flags. All test clusters -// should be created through this command since it sets up the logging in a -// unified way. -func StartCluster(ctx context.Context, t *testing.T, cfg cluster.TestConfig) (c cluster.Cluster) { - var completed bool - defer func() { - if !completed && c != nil { - c.AssertAndStop(ctx, t) - } - }() - - parts := strings.Split(t.Name(), "/") - if len(parts) < 2 { - t.Fatal("must invoke RunDocker") - } - - var runMode string - for _, part := range parts[1:] { - part = reStripTestEnumeration.ReplaceAllLiteralString(part, "") - switch part { - case dockerTest: - if runMode != "" { - t.Fatalf("test has more than one run mode: %s and %s", runMode, part) - } - runMode = part - } - } - - switch runMode { - case dockerTest: - logDir := *flagLogDir - if logDir != "" { - logDir = filepath.Join(logDir, filepath.Clean(t.Name())) - } - l := cluster.CreateDocker(ctx, cfg, logDir, stopper) - l.Start(ctx) - c = l - - default: - t.Fatalf("unable to run in mode %q, use RunDocker", runMode) - } - - // Don't wait for replication unless requested (usually it is). - if !cfg.NoWait && cfg.InitMode != cluster.INIT_NONE { - wantedReplicas := 3 - if numNodes := c.NumNodes(); numNodes < wantedReplicas { - wantedReplicas = numNodes - } - - // Looks silly, but we actually start zero-node clusters in the - // reference tests. - if wantedReplicas > 0 { - log.Infof(ctx, "waiting for first range to have %d replicas", wantedReplicas) - - testutils.SucceedsSoon(t, func() error { - select { - case <-stopper.ShouldQuiesce(): - t.Fatal("interrupted") - case <-time.After(time.Second): - } - - // Always talk to node 0 because it's guaranteed to exist. - db, err := c.NewDB(ctx, 0) - if err != nil { - t.Fatal(err) - } - rows, err := db.Query(`SELECT array_length(replicas, 1) FROM crdb_internal.ranges LIMIT 1`) - if err != nil { - // Versions <= 1.1 do not contain the crdb_internal table, which is what's used - // to determine whether a cluster has up-replicated. This is relevant for the - // version upgrade acceptance test. Just skip the replication check for this case. - if testutils.IsError(err, "(table|relation) \"crdb_internal.ranges\" does not exist") { - return nil - } - t.Fatal(err) - } - defer rows.Close() - var foundReplicas int - if rows.Next() { - if err = rows.Scan(&foundReplicas); err != nil { - t.Fatalf("unable to scan for length of replicas array: %s", err) - } - if log.V(1) { - log.Infof(ctx, "found %d replicas", foundReplicas) - } - } else { - return errors.Errorf("no ranges listed") - } - - if foundReplicas < wantedReplicas { - return errors.Errorf("expected %d replicas, only found %d", wantedReplicas, foundReplicas) - } - return nil - }) - } - - // Ensure that all nodes are serving SQL by making sure a simple - // read-only query succeeds. - for i := 0; i < c.NumNodes(); i++ { - testutils.SucceedsSoon(t, func() error { - db, err := c.NewDB(ctx, i) - if err != nil { - return err - } - if _, err := db.Exec("SHOW DATABASES"); err != nil { - return err - } - return nil - }) - } - } - - completed = true - return c -} diff --git a/pkg/acceptance/util_docker.go b/pkg/acceptance/util_docker.go deleted file mode 100644 index 51e59c0c9a5..00000000000 --- a/pkg/acceptance/util_docker.go +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2015 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -package acceptance - -import ( - "context" - "fmt" - "io/ioutil" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/cockroachdb/cockroach/pkg/acceptance/cluster" - "github.com/cockroachdb/cockroach/pkg/base" - "github.com/cockroachdb/cockroach/pkg/build/bazel" - "github.com/cockroachdb/cockroach/pkg/security" - "github.com/containerd/containerd/platforms" - "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/container" -) - -func defaultContainerConfig() container.Config { - return container.Config{ - Image: acceptanceImage, - Env: []string{ - fmt.Sprintf("PGUSER=%s", security.RootUser), - fmt.Sprintf("PGPORT=%s", base.DefaultPort), - "PGSSLCERT=/certs/client.root.crt", - "PGSSLKEY=/certs/client.root.key", - }, - Entrypoint: []string{"autouseradd", "-u", "roach", "-C", "/home/roach", "--"}, - } -} - -// testDockerFail ensures the specified docker cmd fails. -func testDockerFail(ctx context.Context, t *testing.T, name string, cmd []string) { - containerConfig := defaultContainerConfig() - containerConfig.Cmd = cmd - if err := testDockerSingleNode(ctx, t, name, containerConfig); err == nil { - t.Error("expected failure") - } -} - -// testDockerSuccess ensures the specified docker cmd succeeds. -func testDockerSuccess(ctx context.Context, t *testing.T, name string, cmd []string) { - containerConfig := defaultContainerConfig() - containerConfig.Cmd = cmd - if err := testDockerSingleNode(ctx, t, name, containerConfig); err != nil { - t.Error(err) - } -} - -const ( - // Iterating against a locally built version of the docker image can be done - // by changing acceptanceImage to the hash of the container. - acceptanceImage = "docker.io/cockroachdb/acceptance:20200303-091324" -) - -func testDocker( - ctx context.Context, t *testing.T, num int, name string, containerConfig container.Config, -) error { - var err error - RunDocker(t, func(t *testing.T) { - cfg := cluster.TestConfig{ - Name: name, - Duration: *flagDuration, - } - for i := 0; i < num; i++ { - cfg.Nodes = append(cfg.Nodes, cluster.NodeConfig{Stores: []cluster.StoreConfig{{}}}) - } - l := StartCluster(ctx, t, cfg).(*cluster.DockerCluster) - defer l.AssertAndStop(ctx, t) - - if len(l.Nodes) > 0 { - containerConfig.Env = append(containerConfig.Env, "PGHOST="+l.Hostname(0)) - } - var pwd string - pwd, err = os.Getwd() - if err != nil { - return - } - testdataDir := filepath.Join(pwd, "testdata") - if bazel.BuiltWithBazel() { - testdataDir, err = ioutil.TempDir("", "") - if err != nil { - t.Fatal(err) - } - // Copy runfiles symlink content to a temporary directory to avoid broken symlinks in docker. - err = copyRunfiles("testdata", testdataDir) - if err != nil { - t.Fatal(err) - } - defer func() { - _ = os.RemoveAll(testdataDir) - }() - } - hostConfig := container.HostConfig{ - NetworkMode: "host", - Binds: []string{testdataDir + ":/mnt/data"}, - } - if bazel.BuiltWithBazel() { - interactivetestsDir, err := ioutil.TempDir("", "") - if err != nil { - t.Fatal(err) - } - // Copy runfiles symlink content to a temporary directory to avoid broken symlinks in docker. - err = copyRunfiles("../cli/interactive_tests", interactivetestsDir) - if err != nil { - t.Fatal(err) - } - defer func() { - _ = os.RemoveAll(interactivetestsDir) - }() - hostConfig.Binds = append(hostConfig.Binds, interactivetestsDir+":/mnt/interactive_tests") - } - err = l.OneShot( - ctx, acceptanceImage, types.ImagePullOptions{}, containerConfig, hostConfig, - platforms.DefaultSpec(), "docker-"+name, - ) - preserveLogs := err != nil - l.Cleanup(ctx, preserveLogs) - }) - return err -} - -// Bazel uses symlinks in the runfiles directory. If a directory with symlinks is mounted inside a docker container, -// the symlinks point to not existing destination. -// This function copies the content of the symlinks to another directory, -// so the files can be used inside a docker container. The caller function is responsible for cleaning up. -// This function doesn't copy the original file permissions and uses 755 for directories and files. -func copyRunfiles(source, destination string) error { - return filepath.WalkDir(source, func(path string, dirEntry os.DirEntry, walkErr error) error { - if walkErr != nil { - return walkErr - } - relPath := strings.Replace(path, source, "", 1) - if relPath == "" { - return nil - } - if dirEntry.IsDir() { - return os.Mkdir(filepath.Join(destination, relPath), 0755) - } - data, err := ioutil.ReadFile(filepath.Join(source, relPath)) - if err != nil { - return err - } - return ioutil.WriteFile(filepath.Join(destination, relPath), data, 0755) - }) -} - -func testDockerSingleNode( - ctx context.Context, t *testing.T, name string, containerConfig container.Config, -) error { - return testDocker(ctx, t, 1, name, containerConfig) -} - -func testDockerOneShot( - ctx context.Context, t *testing.T, name string, containerConfig container.Config, -) error { - return testDocker(ctx, t, 0, name, containerConfig) -} diff --git a/pkg/cmd/allocsim/main.go b/pkg/cmd/allocsim/main.go index 8be396677f3..10dc202da92 100644 --- a/pkg/cmd/allocsim/main.go +++ b/pkg/cmd/allocsim/main.go @@ -25,8 +25,6 @@ import ( "syscall" "time" - "github.com/cockroachdb/cockroach/pkg/acceptance/localcluster" - "github.com/cockroachdb/cockroach/pkg/acceptance/localcluster/tc" "github.com/cockroachdb/cockroach/pkg/cli" "github.com/cockroachdb/cockroach/pkg/cli/exit" "github.com/cockroachdb/cockroach/pkg/kv/kvserver" diff --git a/pkg/cmd/zerosum/main.go b/pkg/cmd/zerosum/main.go index fd09c1f5e38..d8709dd31e2 100644 --- a/pkg/cmd/zerosum/main.go +++ b/pkg/cmd/zerosum/main.go @@ -26,8 +26,6 @@ import ( "time" "github.com/cockroachdb/cockroach-go/v2/crdb" - "github.com/cockroachdb/cockroach/pkg/acceptance/cluster" - "github.com/cockroachdb/cockroach/pkg/acceptance/localcluster" "github.com/cockroachdb/cockroach/pkg/cli/exit" "github.com/cockroachdb/cockroach/pkg/keys" "github.com/cockroachdb/cockroach/pkg/util/encoding" diff --git a/pkg/testutils/docker/docker-fsnotify/listen_file_creation.go b/pkg/testutils/docker/docker-fsnotify/listen_file_creation.go deleted file mode 100644 index 10ef0e1bc19..00000000000 --- a/pkg/testutils/docker/docker-fsnotify/listen_file_creation.go +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2021 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -// Usage: go run ./listen_file_creation.go parent_folder_path file_name [timeout_duration] - -package main - -import ( - "fmt" - "os" - "path/filepath" - "strconv" - "strings" - "time" - - "github.com/cockroachdb/errors" - "github.com/fsnotify/fsnotify" -) - -type result struct { - finished bool - err error -} - -const defaultTimeout = 30 * time.Second - -func main() { - if len(os.Args) < 2 { - panic(errors.Wrap( - fmt.Errorf("must provide the folder to watch and the file to listen to"), - "fail to run fsnotify to listen to file creation"), - ) - } - - var err error - - folderPath := os.Args[1] - wantedFileName := os.Args[2] - - timeout := defaultTimeout - - var timeoutVal int - if len(os.Args) > 3 { - timeoutVal, err = strconv.Atoi(os.Args[3]) - if err != nil { - panic(errors.Wrap(err, "timeout argument must be an integer")) - } - } - - timeout = time.Duration(timeoutVal) * time.Second - - watcher, err := fsnotify.NewWatcher() - if err != nil { - panic(errors.Wrap(err, "cannot create new fsnotify file watcher")) - } - defer func() { - if err := watcher.Close(); err != nil { - panic(errors.Wrap(err, "error closing the file watcher in docker-fsnotify")) - } - }() - - done := make(chan result) - - go func() { - for { - if _, err := os.Stat(filepath.Join(folderPath, wantedFileName)); errors.Is(err, os.ErrNotExist) { - } else { - done <- result{finished: true, err: nil} - } - time.Sleep(time.Second * 1) - } - }() - - go func() { - for { - select { - case event, ok := <-watcher.Events: - if !ok { - return - } - fileName := event.Name[strings.LastIndex(event.Name, "/")+1:] - if event.Op&fsnotify.Write == fsnotify.Write && fileName == wantedFileName { - done <- result{finished: true, err: nil} - } - case err, ok := <-watcher.Errors: - if !ok { - return - } - done <- result{finished: false, err: err} - } - } - }() - - err = watcher.Add(folderPath) - if err != nil { - fmt.Printf("error: %v", err) - return - } - - select { - case res := <-done: - if res.finished && res.err == nil { - fmt.Println("finished") - } else { - fmt.Printf("error in docker-fsnotify: %v", res.err) - } - - case <-time.After(timeout): - fmt.Printf("timeout for %s", timeout) - } -} diff --git a/pkg/testutils/docker/empty.go b/pkg/testutils/docker/empty.go deleted file mode 100644 index b1971832c8f..00000000000 --- a/pkg/testutils/docker/empty.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2021 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -package docker - -// This file is here so go test always finds at least one file. diff --git a/pkg/testutils/docker/single_node_docker_test.go b/pkg/testutils/docker/single_node_docker_test.go deleted file mode 100644 index a201b8a5512..00000000000 --- a/pkg/testutils/docker/single_node_docker_test.go +++ /dev/null @@ -1,580 +0,0 @@ -// Copyright 2021 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -//go:build docker -// +build docker - -package docker - -import ( - "bytes" - "context" - "encoding/binary" - "fmt" - "io" - "io/ioutil" - "math" - "os" - "path/filepath" - "regexp" - "strconv" - "strings" - "testing" - "time" - - "github.com/cockroachdb/cockroach/pkg/util/contextutil" - "github.com/cockroachdb/cockroach/pkg/util/log" - "github.com/cockroachdb/errors" - "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/filters" - "github.com/docker/docker/client" - "github.com/docker/docker/pkg/stdcopy" - "github.com/docker/go-connections/nat" -) - -const fsnotifyBinName = "docker-fsnotify-bin" - -// sqlQuery consists of a sql query and the expected result. -type sqlQuery struct { - query string - expectedResult string -} - -// runContainerArgs are equivalent to arguments passed to a `docker run` -// command. -type runContainerArgs struct { - // envSetting is to set the environment variables for the docker container. - envSetting []string - // volSetting is to set how local directories will be mounted to the container. - volSetting []string - // cmd is the command to run when starting the container. - cmd []string -} - -// singleNodeDockerTest consists of two main parts: start the container with -// a single-node cockroach server using runContainerArgs, -// and execute sql queries in this running container. -type singleNodeDockerTest struct { - testName string - runContainerArgs runContainerArgs - containerName string - // sqlOpts are arguments passed to a `cockroach sql` command. - sqlOpts []string - // sqlQueries are queries to run in this container, and their expected results. - sqlQueries []sqlQuery -} - -func TestSingleNodeDocker(t *testing.T) { - ctx := context.Background() - pwd, err := os.Getwd() - if err != nil { - t.Fatal(errors.NewAssertionErrorWithWrappedErrf(err, "cannot get pwd")) - } - - fsnotifyPath := filepath.Join(filepath.Dir(filepath.Dir(filepath.Dir(filepath.Dir(filepath.Dir(filepath.Dir(pwd)))))), "docker-fsnotify") - - var dockerTests = []singleNodeDockerTest{ - { - testName: "single-node-secure-mode", - containerName: "roach1", - runContainerArgs: runContainerArgs{ - envSetting: []string{ - "COCKROACH_DATABASE=mydb", - "COCKROACH_USER=myuser", - "COCKROACH_PASSWORD=23333", - }, - volSetting: []string{ - fmt.Sprintf("%s/testdata/single-node-test/docker-entrypoint-initdb.d/:/docker-entrypoint-initdb.d", pwd), - fmt.Sprintf("%s/docker-fsnotify-bin:/cockroach/docker-fsnotify", fsnotifyPath), - }, - cmd: []string{"start-single-node", "--certs-dir=certs"}, - }, - sqlOpts: []string{ - "--format=csv", - "--certs-dir=certs", - "--user=myuser", - "--url=postgresql://myuser:23333@127.0.0.1:26257/mydb?sslcert=certs%2Fclient.myuser.crt&sslkey=certs%2Fclient.myuser.key&sslmode=verify-full&sslrootcert=certs%2Fca.crt", - }, - sqlQueries: []sqlQuery{ - {"SELECT current_user", "current_user\nmyuser"}, - {"SELECT current_database()", "current_database\nmydb"}, - {"CREATE TABLE hello (X INT)", "CREATE TABLE"}, - {"INSERT INTO hello VALUES (1), (2), (3)", "INSERT 3"}, - {"SELECT * FROM hello", "x\n1\n2\n3"}, - {"SELECT * FROM bello", "id,name\n1,a\n2,b\n3,c"}, - }, - }, - { - testName: "single-node-insecure-mode", - containerName: "roach2", - runContainerArgs: runContainerArgs{ - envSetting: []string{ - "COCKROACH_DATABASE=mydb", - }, - volSetting: []string{ - fmt.Sprintf("%s/testdata/single-node-test/docker-entrypoint-initdb.d/:/docker-entrypoint-initdb.d", pwd), - fmt.Sprintf("%s/docker-fsnotify-bin:/cockroach/docker-fsnotify", fsnotifyPath), - }, - cmd: []string{"start-single-node", "--insecure"}, - }, - sqlOpts: []string{ - "--format=csv", - "--insecure", - "--database=mydb", - }, - sqlQueries: []sqlQuery{ - {"SELECT current_user", "current_user\nroot"}, - {"SELECT current_database()", "current_database\nmydb"}, - {"CREATE TABLE hello (X INT)", "CREATE TABLE"}, - {"INSERT INTO hello VALUES (1), (2), (3)", "INSERT 3"}, - {"SELECT * FROM hello", "x\n1\n2\n3"}, - {"SELECT * FROM bello", "id,name\n1,a\n2,b\n3,c"}, - }, - }, - { - testName: "single-node-insecure-mem-mode", - containerName: "roach3", - runContainerArgs: runContainerArgs{ - envSetting: []string{ - "COCKROACH_DATABASE=mydb", - }, - volSetting: []string{ - fmt.Sprintf("%s/testdata/single-node-test/docker-entrypoint-initdb.d/:/docker-entrypoint-initdb.d", pwd), - fmt.Sprintf("%s/docker-fsnotify-bin:/cockroach/docker-fsnotify", fsnotifyPath), - }, - cmd: []string{"start-single-node", "--insecure", "--store=type=mem,size=0.25"}, - }, - sqlOpts: []string{ - "--format=csv", - "--insecure", - "--database=mydb", - }, - sqlQueries: []sqlQuery{ - {"SELECT current_user", "current_user\nroot"}, - {"SELECT current_database()", "current_database\nmydb"}, - {"CREATE TABLE hello (X INT)", "CREATE TABLE"}, - {"INSERT INTO hello VALUES (1), (2), (3)", "INSERT 3"}, - {"SELECT * FROM hello", "x\n1\n2\n3"}, - {"SELECT * FROM bello", "id,name\n1,a\n2,b\n3,c"}, - }, - }, - } - - cl, err := client.NewClientWithOpts(client.FromEnv) - cl.NegotiateAPIVersion(ctx) - - if err != nil { - t.Fatal(err) - } - dn := dockerNode{ - cl: cl, - } - - if err := removeLocalData(); err != nil { - t.Fatal(err) - } - - if err := contextutil.RunWithTimeout( - ctx, - "remove all containers using current image", - defaultTimeout, - func(ctx context.Context) error { - return dn.removeAllContainers(ctx) - }); err != nil { - t.Errorf("%v", err) - } - - for _, test := range dockerTests { - t.Run(test.testName, func(t *testing.T) { - - if err := contextutil.RunWithTimeout( - ctx, - "start container", - defaultTimeout, - func(ctx context.Context) error { - return dn.startContainer( - ctx, - test.containerName, - test.runContainerArgs.envSetting, - test.runContainerArgs.volSetting, - test.runContainerArgs.cmd, - ) - }, - ); err != nil { - t.Fatal(err) - } - - if err := contextutil.RunWithTimeout( - ctx, - "wait for the server to finish the initialization", - waitInitTimeout, - func(ctx context.Context) error { - return dn.waitInitFinishes(ctx) - }, - ); err != nil { - t.Fatal(err) - } - - if err := contextutil.RunWithTimeout( - ctx, - "show log", - defaultTimeout, - func(ctx context.Context) error { - return dn.showContainerLog(ctx, fmt.Sprintf("%s.log", test.testName)) - }, - ); err != nil { - log.Warningf(ctx, "cannot show container log: %v", err) - } - - for _, qe := range test.sqlQueries { - query := qe.query - expected := qe.expectedResult - - if err := contextutil.RunWithTimeout( - ctx, - fmt.Sprintf("execute command \"%s\"", query), - defaultTimeout, - func(ctx context.Context) error { - resp, err := dn.execSQLQuery(ctx, query, test.sqlOpts) - if err != nil { - return err - } - cleanedOutput, err := cleanQueryResult(resp.stdOut) - if err != nil { - return err - } - if cleanedOutput != expected { - return fmt.Errorf("executing %s, expect:\n%#v\n, got\n%#v", query, expected, cleanedOutput) - } - return nil - }, - ); err != nil { - t.Errorf("%v", err) - } - } - - if err := contextutil.RunWithTimeout( - ctx, - "remove current container", - defaultTimeout, - func(ctx context.Context) error { - return dn.rmContainer(ctx) - }, - ); err != nil { - t.Errorf("%v", err) - } - - }) - } - -} - -const ( - imageName = "cockroachdb/cockroach-ci:latest" - defaultTimeout = 10 * time.Second - waitInitTimeout = 80 * time.Second - initSuccessFile = "init_success" - cockroachEntrypoint = "./cockroach" - hostPort = "8080" - cockroachPort = "26257" - hostIP = "127.0.0.1" -) - -type dockerNode struct { - cl client.APIClient - contID string -} - -// removeLocalData removes existing database saved in cockroach-data. -func removeLocalData() error { - err := os.RemoveAll("./cockroach-data") - if err != nil { - return errors.Wrap(err, "cannot remove local data") - } - return nil -} - -// showContainerLog outputs the container's logs to the logFile and stderr. -func (dn *dockerNode) showContainerLog(ctx context.Context, logFileName string) error { - - cmdLog, err := os.Create(logFileName) - if err != nil { - return errors.Wrap(err, "cannot create log file") - } - out := io.MultiWriter(cmdLog, os.Stderr) - - rc, err := dn.cl.ContainerLogs(ctx, dn.contID, types.ContainerLogsOptions{ - ShowStdout: true, - ShowStderr: true, - }) - if err != nil { - return errors.Wrap(err, "cannot create docker logs") - } - - // The docker log output is not quite plaintext: each line has a - // prefix consisting of one byte file descriptor (stdout vs stderr), - // three bytes padding, four byte length. We could use this to - // disentangle stdout and stderr if we wanted to output them into - // separate streams, but we don't really care. - for { - var header uint64 - if err := binary.Read(rc, binary.BigEndian, &header); err == io.EOF { - break - } else if err != nil { - return err - } - size := header & math.MaxUint32 - if _, err := io.CopyN(out, rc, int64(size)); err != nil { - return err - } - } - - if err := rc.Close(); err != nil { - return errors.Wrap(err, "cannot close docker log") - } - - return nil -} - -// startContainer starts a container with given setting for environment -// variables, mounted volumes, and command to run. -func (dn *dockerNode) startContainer( - ctx context.Context, containerName string, envSetting []string, volSetting []string, cmd []string, -) error { - - containerConfig := container.Config{ - Hostname: containerName, - Image: imageName, - Env: envSetting, - ExposedPorts: nat.PortSet{hostPort: struct{}{}, cockroachPort: struct{}{}}, - Cmd: cmd, - } - - hostConfig := container.HostConfig{ - Binds: volSetting, - PortBindings: map[nat.Port][]nat.PortBinding{ - nat.Port(hostPort): {{HostIP: hostIP, HostPort: hostPort}}, - nat.Port(cockroachPort): {{HostIP: hostIP, HostPort: cockroachPort}}, - }, - } - - resp, err := dn.cl.ContainerCreate( - ctx, - &containerConfig, - &hostConfig, - nil, - nil, - containerName, - ) - if err != nil { - return errors.Wrap(err, "cannot create container") - } - - dn.contID = resp.ID - - if err := dn.cl.ContainerStart(ctx, dn.contID, - types.ContainerStartOptions{}); err != nil { - return errors.Wrap(err, "cannot start container") - } - - return nil -} - -// removeAllContainers removes all running containers based on the cockroach-ci -// docker image by force. -func (dn *dockerNode) removeAllContainers(ctx context.Context) error { - filter := filters.NewArgs(filters.Arg("ancestor", imageName)) - conts, err := dn.cl.ContainerList(ctx, - types.ContainerListOptions{All: true, Filters: filter}) - if err != nil { - return errors.Wrapf( - err, - "cannot list all containers on docker image %s", - imageName, - ) - } - for _, cont := range conts { - err := dn.cl.ContainerRemove(ctx, cont.ID, - types.ContainerRemoveOptions{Force: true}) - if err != nil { - return errors.Wrapf(err, "cannot remove container %s", cont.Names) - } - } - return nil -} - -type execResult struct { - stdOut string - stdErr string - exitCode int -} - -// InspectExecResp inspects the result of a docker command execution, saves its -// stdout, stderr message and exit code to an execResult, and returns this -// execResult and a possible error. -func (dn *dockerNode) InspectExecResp(ctx context.Context, execID string) (execResult, error) { - var execRes execResult - resp, err := dn.cl.ContainerExecAttach(ctx, execID, types.ExecStartCheck{}) - if err != nil { - return execResult{}, err - } - defer resp.Close() - - var outBuf, errBuf bytes.Buffer - outputDone := make(chan error) - - go func() { - _, err = stdcopy.StdCopy(&outBuf, &errBuf, resp.Reader) - outputDone <- err - }() - - select { - case err := <-outputDone: - if err != nil { - return execRes, err - } - break - - case <-ctx.Done(): - return execRes, ctx.Err() - } - - stdout, err := ioutil.ReadAll(&outBuf) - if err != nil { - return execRes, err - } - stderr, err := ioutil.ReadAll(&errBuf) - if err != nil { - return execRes, err - } - - res, err := dn.cl.ContainerExecInspect(ctx, execID) - if err != nil { - return execRes, err - } - - execRes.exitCode = res.ExitCode - execRes.stdOut = string(stdout) - execRes.stdErr = string(stderr) - return execRes, nil -} - -// execCommand is to execute command in the current container, and returns the -// execution result and possible error. -func (dn *dockerNode) execCommand( - ctx context.Context, cmd []string, workingDir string, -) (*execResult, error) { - execID, err := dn.cl.ContainerExecCreate(ctx, dn.contID, types.ExecConfig{ - User: "root", - AttachStderr: true, - AttachStdout: true, - Tty: true, - Cmd: cmd, - WorkingDir: workingDir, - }) - - if err != nil { - return nil, errors.Wrapf( - err, - "cannot create command \"%s\"", - strings.Join(cmd, " "), - ) - } - - res, err := dn.InspectExecResp(ctx, execID.ID) - if err != nil { - return nil, errors.Wrapf( - err, - "cannot execute command \"%s\"", - strings.Join(cmd, " "), - ) - } - - if res.exitCode != 0 { - return &res, errors.Wrapf( - errors.Newf("%s", res.stdErr), - "command \"%s\" exit with code %d:\n %+v", - strings.Join(cmd, " "), - res.exitCode, - res, - ) - } - - return &res, nil -} - -// waitInitFinishes waits till the server finishes all init steps or timeout, -// whichever earlier. It keeps listening to the initSuccessFile till it is closed and -// written. -func (dn *dockerNode) waitInitFinishes(ctx context.Context) error { - var res *execResult - var err error - - // Run the binary which listens to the /cockroach folder until the - // initialization process has finished or timeout. - res, err = dn.execCommand(ctx, []string{ - "./docker-fsnotify", - "/cockroach", - initSuccessFile, - strconv.Itoa(int(waitInitTimeout.Seconds())), - }, "/cockroach") - if err != nil { - return errors.Wrapf(err, "cannot run fsnotify to listen to %s:\nres:%#v\n", initSuccessFile, res) - } - - if strings.Contains(res.stdOut, "finished\r\n") { - return nil - } - return errors.Wrap(errors.Newf("%#v", res), "error waiting the initialization to finish") -} - -// execSQLQuery executes the sql query and returns the server's output and -// possible error. -func (dn *dockerNode) execSQLQuery( - ctx context.Context, sqlQuery string, sqlQueryOpts []string, -) (*execResult, error) { - query := append([]string{cockroachEntrypoint, "sql", "-e", sqlQuery}, - sqlQueryOpts..., - ) - - res, err := dn.execCommand(ctx, query, "/cockroach") - if err != nil { - return nil, errors.Wrapf(err, "error executing query \"%s\"", sqlQuery) - } - - return res, nil -} - -//rmContainer performs a forced deletion of the current container. -func (dn *dockerNode) rmContainer(ctx context.Context) error { - if err := dn.cl.ContainerRemove(ctx, dn.contID, types.ContainerRemoveOptions{ - Force: true, - }); err != nil { - return errors.Wrapf(err, "cannot remove container %s", dn.contID) - } - dn.contID = "" - return nil -} - -// cleanQueryResult is to parse the result from a sql query to a cleaner format. -// e.g. -// "id,name\r\n1,a\r\n2,b\r\n3,c\r\n\r\n\r\nTime: 11ms\r\n\r\n" -// => "id,name\n1,a\n2,b\n3,c" -func cleanQueryResult(queryRes string) (string, error) { - formatted := strings.ReplaceAll(queryRes, "\r\n", "\n") - r := regexp.MustCompile(`([\s\S]+)\n{3}Time:.+`) - res := r.FindStringSubmatch(formatted) - if len(res) < 2 { - return "", errors.Wrapf(errors.Newf("%s", queryRes), "cannot parse the query result: %#v") - } - return res[1], nil - -} diff --git a/pkg/testutils/docker/testdata/single-node-test/docker-entrypoint-initdb.d/test1.sql b/pkg/testutils/docker/testdata/single-node-test/docker-entrypoint-initdb.d/test1.sql deleted file mode 100755 index 79cb977873b..00000000000 --- a/pkg/testutils/docker/testdata/single-node-test/docker-entrypoint-initdb.d/test1.sql +++ /dev/null @@ -1,8 +0,0 @@ -USE mydb; - -CREATE TABLE bello ( - id INT UNIQUE, - name varchar(12) -); - -INSERT INTO bello (id, name) values (1, 'a'), (2, 'b'), (3, 'c'); diff --git a/pkg/testutils/docker/testdata/single-node-test/docker-entrypoint-initdb.d/test2.sql b/pkg/testutils/docker/testdata/single-node-test/docker-entrypoint-initdb.d/test2.sql deleted file mode 100755 index c47d942bffd..00000000000 --- a/pkg/testutils/docker/testdata/single-node-test/docker-entrypoint-initdb.d/test2.sql +++ /dev/null @@ -1,7 +0,0 @@ -CREATE TABLE donut ( - id INT UNIQUE, - name varchar(12) -); - - -INSERT INTO donut (id, name) values (1, 'a'), (2, 'b'), (3, 'c');