diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 47121007a0..4939dac5b9 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -64,8 +64,6 @@ jobs: run: "go test -tags ci ./..." e2e: - # Explicitly disabling e2e testing - if: "false" name: "E2E" runs-on: "ubuntu-latest" steps: diff --git a/e2e/cockroach/cockroach.go b/e2e/cockroach/cockroach.go index 08d790852e..b705f1bf34 100644 --- a/e2e/cockroach/cockroach.go +++ b/e2e/cockroach/cockroach.go @@ -73,6 +73,30 @@ func (c *Node) Conn() *pgx.Conn { return c.conn } +// NodeID returns the cockroach-internal node id for this connection. This is +// the value that is referenced by other crdb metadata to identify range leader, +// follower nodes, etc. +func (c *Node) NodeID(ctx context.Context) (int, error) { + rows, err := c.conn.Query(ctx, "SHOW node_id") + defer rows.Close() + if err != nil { + return -1, err + } + // despite being an int, crdb returns node id as a string + var nodeID string + for rows.Next() { + if err := rows.Scan(&nodeID); err != nil { + return -1, err + } + break + } + i, err := strconv.Atoi(nodeID) + if err != nil { + return -1, err + } + return i, nil +} + // Cluster represents a set of Node nodes configured to talk to // each other. type Cluster []*Node diff --git a/e2e/generator/names.go b/e2e/generator/names.go new file mode 100644 index 0000000000..a81230f4d1 --- /dev/null +++ b/e2e/generator/names.go @@ -0,0 +1,25 @@ +package generator + +import "github.com/brianvoe/gofakeit/v6" + +type UniqueGenerator struct { + seen map[string]struct{} + regex string +} + +func NewUniqueGenerator(regex string) *UniqueGenerator { + return &UniqueGenerator{ + seen: make(map[string]struct{}, 0), + regex: regex, + } +} + +func (g *UniqueGenerator) Next() string { + for { + val := gofakeit.Regex(g.regex) + if _, ok := g.seen[val]; !ok { + g.seen[val] = struct{}{} + return val + } + } +} diff --git a/e2e/go.mod b/e2e/go.mod index db95f00763..5fae598be7 100644 --- a/e2e/go.mod +++ b/e2e/go.mod @@ -3,37 +3,44 @@ module github.com/authzed/spicedb/e2e go 1.17 require ( - github.com/authzed/authzed-go v0.3.1-0.20211130221323-9d59da6e55da + github.com/authzed/authzed-go v0.3.1-0.20211220220442-a36f72252b43 github.com/authzed/grpcutil v0.0.0-20211020204402-aba1876830e6 github.com/authzed/spicedb v0.0.0 - github.com/jackc/pgx/v4 v4.13.0 + github.com/brianvoe/gofakeit/v6 v6.10.0 + github.com/ecordell/optgen v0.0.5-0.20211217170453-18cdce036e35 + github.com/jackc/pgtype v1.9.1 + github.com/jackc/pgx/v4 v4.14.1 github.com/stretchr/testify v1.7.0 + golang.org/x/tools v0.1.8 google.golang.org/grpc v1.42.0 + mvdan.cc/gofumpt v0.2.1 ) require ( github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d // indirect + github.com/dave/jennifer v1.4.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/envoyproxy/protoc-gen-validate v0.6.2 // indirect github.com/golang/protobuf v1.5.2 // indirect + github.com/google/go-cmp v0.5.6 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect - github.com/jackc/pgconn v1.10.0 // indirect + github.com/jackc/pgconn v1.10.1 // indirect github.com/jackc/pgio v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgproto3/v2 v2.1.1 // indirect + github.com/jackc/pgproto3/v2 v2.2.0 // indirect github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect - github.com/jackc/pgtype v1.8.1 // indirect - github.com/kr/text v0.2.0 // indirect github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/shopspring/decimal v1.3.1 // indirect golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect + golang.org/x/mod v0.5.1 // indirect golang.org/x/net v0.0.0-20211104170005-ce137452f963 // indirect - golang.org/x/sys v0.0.0-20211103235746-7861aae1554b // indirect + golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect golang.org/x/text v0.3.7 // indirect - google.golang.org/genproto v0.0.0-20211102202547-e9cf271f7f2c // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1 // indirect google.golang.org/protobuf v1.27.1 // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect diff --git a/e2e/go.sum b/e2e/go.sum index 48da6fff12..d277524f56 100644 --- a/e2e/go.sum +++ b/e2e/go.sum @@ -50,8 +50,8 @@ github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg6 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/Masterminds/squirrel v1.5.1/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= -github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Masterminds/squirrel v1.5.2/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= +github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -66,14 +66,14 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5 github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/authzed/authzed-go v0.3.1-0.20211130221323-9d59da6e55da h1:dcjIyi2Om9CDAmEKiwQZsF7bV0x4PXuJeoN11BbbXXI= -github.com/authzed/authzed-go v0.3.1-0.20211130221323-9d59da6e55da/go.mod h1:bsUniBRroq4l5WZMYLO+T9osQa/P2qMwZ+Af8zoJK8Y= +github.com/authzed/authzed-go v0.3.1-0.20211220220442-a36f72252b43 h1:hnMt/mitjkBq6OKGAtSxCvbQSGNy0c8sBhyPy/NIy+w= +github.com/authzed/authzed-go v0.3.1-0.20211220220442-a36f72252b43/go.mod h1:bsUniBRroq4l5WZMYLO+T9osQa/P2qMwZ+Af8zoJK8Y= github.com/authzed/grpcutil v0.0.0-20210913124023-cad23ae5a9e8/go.mod h1:HwO/KbRU3fWXEYHE96kvXnwxzi97tkXD1hfi5UaZ71Y= github.com/authzed/grpcutil v0.0.0-20211020204402-aba1876830e6 h1:izP/rEris51ZmomXb5J0ShyJKqsxTfVKDRnJz0QGbgg= github.com/authzed/grpcutil v0.0.0-20211020204402-aba1876830e6/go.mod h1:rqjY3zyK/YP7NID9+B2BdIRRkvnK+cdf9/qya/zaFZE= github.com/aws/aws-sdk-go v1.17.4/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.41.15/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= -github.com/benbjohnson/clock v1.2.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/aws/aws-sdk-go v1.42.16/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= +github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= @@ -81,7 +81,9 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= -github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/brianvoe/gofakeit/v6 v6.10.0 h1:0lZpqKzY2xVfjmCQBn9g9+SHIGg58SX+vu/ejuSVGMc= +github.com/brianvoe/gofakeit/v6 v6.10.0/go.mod h1:palrJUk4Fyw38zIFB/uBZqsgzW5VsNllhHKKwAebzew= +github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d h1:S2NE3iHSwP0XV47EEXL8mWmRdEfGscSJ+7EgePNgt0s= github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= @@ -122,6 +124,9 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= +github.com/dave/jennifer v1.4.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= +github.com/dave/jennifer v1.4.1 h1:XyqG6cn5RQsTj3qlWQTKlRGAyrTcsk1kUmWdZBzRjDw= +github.com/dave/jennifer v1.4.1/go.mod h1:7jEdnm+qBcxl8PC0zyp7vxcpSRnzXSt9r39tpTVGlwA= 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= @@ -129,13 +134,14 @@ github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu 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/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/docker/cli v20.10.8+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/cli v20.10.9+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v20.10.11+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/docker v20.10.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v20.10.9+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/ecordell/optgen v0.0.5-0.20211217170453-18cdce036e35 h1:BWkgCFLPOJXWYvUCfQa7ArWzX22Jiw0erbnzCMZI2rc= +github.com/ecordell/optgen v0.0.5-0.20211217170453-18cdce036e35/go.mod h1:bAPkLVWcBlTX5EkXW0UTPRj3+yjq2I6VLgH8OasuQEM= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= 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= @@ -153,8 +159,11 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/set v0.2.1/go.mod h1:+RKtMCH+favT2+3YecHGxcc0b4KyVWA1QWWJUs4E0CI= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= +github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss= +github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= 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/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= @@ -265,8 +274,9 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.6.0 h1:rgxjzoDmDXw5q8HONgyHhBas4to0/XWRo/gPpJhsUNQ= github.com/grpc-ecosystem/grpc-gateway/v2 v2.6.0/go.mod h1:qrJPVzv9YlhsrxJc3P/Q85nr0w1lIRikTl4JlhdDH5w= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 h1:BZHcxBETFHIdVyhyEfOvn/RdU/QGdLI4y34qQGjGWO0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= @@ -317,8 +327,8 @@ github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsU github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= -github.com/jackc/pgconn v1.10.0 h1:4EYhlDVEMsJ30nNj0mmgwIUXoq7e9sMJrVC2ED6QlCU= -github.com/jackc/pgconn v1.10.0/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= +github.com/jackc/pgconn v1.10.1 h1:DzdIHIjG1AxGwoEEqS+mGsURyjt4enSmqzACXvVzOT8= +github.com/jackc/pgconn v1.10.1/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= @@ -334,26 +344,27 @@ github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.1.1 h1:7PQ/4gLoqnl87ZxL7xjO0DR5gYuviDCZxQJsUlFW1eI= github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.2.0 h1:r7JypeP2D3onoQTCxWdTpCtJ4D+qpKr0TxvoyMhZ5ns= +github.com/jackc/pgproto3/v2 v2.2.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= -github.com/jackc/pgtype v1.8.1 h1:9k0IXtdJXHJbyAWQgbWr1lU+MEhPXZz6RIXxfR5oxXs= -github.com/jackc/pgtype v1.8.1/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= +github.com/jackc/pgtype v1.9.1 h1:MJc2s0MFS8C3ok1wQTdQxWuXQcB6+HwAm5x1CzW7mf0= +github.com/jackc/pgtype v1.9.1/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= -github.com/jackc/pgx/v4 v4.13.0 h1:JCjhT5vmhMAf/YwBHLvrBn4OGdIQBiFG6ym8Zmdx570= -github.com/jackc/pgx/v4 v4.13.0/go.mod h1:9P4X524sErlaxj0XSGZk7s+LD0eOyu1ZDUrrpznYDF0= +github.com/jackc/pgx/v4 v4.14.1 h1:71oo1KAGI6mXhLiTMn6iDFcp3e7+zon/capWjl2OEFU= +github.com/jackc/pgx/v4 v4.14.1/go.mod h1:RgDuE4Z34o7XE92RpLsvFiOEfrAUT0Xt2KxvX73W06M= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.1.4/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.2.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= @@ -384,6 +395,8 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +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/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -396,8 +409,8 @@ github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.3 h1:v9QZf2Sn6AmjXtQeFpdoq/eaNtYP6IN+7lcrygsIAtg= -github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= +github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lyft/protoc-gen-star v0.5.1/go.mod h1:9toiA3cC7z5uVbODF7kEQ91Xn7XNFkVUl+SrEe+ZORU= github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= @@ -448,17 +461,19 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= -github.com/ory/dockertest/v3 v3.8.0/go.mod h1:9zPATATlWQru+ynXP+DytBQrsXV7Tmlx7K86H6fQaDo= +github.com/ory/dockertest/v3 v3.8.1/go.mod h1:wSRQ3wmkz+uSARYMk7kVJFDBGm8x5gSxIhI7NDc+BAQ= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -493,6 +508,9 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +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/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= @@ -576,6 +594,7 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= @@ -589,20 +608,20 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.26.0/go.mod h1:4wsfAAW5N9wUHM0QTmZS8z7fvYZ1rv3m+sVeSpf8NhU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.26.0/go.mod h1:4vatbW3QwS11DK0H0SB7FR31/VbthXcYorswdkVXdyg= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.27.0/go.mod h1:T/zQwBldOpoAEpE3HMbLnI8ydESZVz4ggw6Is4FF9LI= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.27.0/go.mod h1:bdvm3YpMxWAgEfQhtTBaVR8ceXPRuRBSQrvOBnIlHxc= go.opentelemetry.io/otel v1.0.0-RC2/go.mod h1:w1thVQ7qbAy8MHb0IFj8a5Q2QU0l2ksf8u/CN8m3NOM= -go.opentelemetry.io/otel v1.0.1/go.mod h1:OPEOD4jIT2SlZPMmwT6FqZz2C0ZNdQqiWcoK6M0SNFU= go.opentelemetry.io/otel v1.1.0/go.mod h1:7cww0OW51jQ8IaZChIEdqLwgh+44+7uiTdWsAL0wQpA= +go.opentelemetry.io/otel v1.2.0/go.mod h1:aT17Fk0Z1Nor9e0uisf98LrntPGMnk4frBO9+dkf69I= go.opentelemetry.io/otel/exporters/jaeger v1.0.0-RC2/go.mod h1:sZZqN3Vb0iT+NE6mZ1S7sNyH3t4PFk6ElK5TLGFBZ7E= go.opentelemetry.io/otel/exporters/jaeger v1.1.0/go.mod h1:D/GIBwAdrFTTqCy1iITpC9nh5rgJpIbFVgkhlz2vCXk= -go.opentelemetry.io/otel/internal/metric v0.24.0/go.mod h1:PSkQG+KuApZjBpC6ea6082ZrWUUy/w132tJ/LOU3TXk= -go.opentelemetry.io/otel/metric v0.24.0/go.mod h1:tpMFnCD9t+BEGiWY2bWF5+AwjuAdM0lSowQ4SBA3/K4= +go.opentelemetry.io/otel/internal/metric v0.25.0/go.mod h1:Nhuw26QSX7d6n4duoqAFi5KOQR4AuzyMcl5eXOgwxtc= +go.opentelemetry.io/otel/metric v0.25.0/go.mod h1:E884FSpQfnJOMMUaq+05IWlJ4rjZpk2s/F1Ju+TEEm8= go.opentelemetry.io/otel/sdk v1.0.0-RC2/go.mod h1:fgwHyiDn4e5k40TD9VX243rOxXR+jzsWBZYA2P5jpEw= go.opentelemetry.io/otel/sdk v1.1.0/go.mod h1:3aQvM6uLm6C4wJpHtT8Od3vNzeZ34Pqc6bps8MywWzo= go.opentelemetry.io/otel/trace v1.0.0-RC2/go.mod h1:JPQ+z6nNw9mqEGT8o3eoPTdnNI+Aj5JcxEsVGREIAy4= -go.opentelemetry.io/otel/trace v1.0.1/go.mod h1:5g4i4fKLaX2BQpSBsxw8YYcgKpMMSW3x7ZTuYBr3sUk= go.opentelemetry.io/otel/trace v1.1.0/go.mod h1:i47XtdcBQiktu5IsrPqOHe8w+sBmnLwwHt8wiUsWGTI= +go.opentelemetry.io/otel/trace v1.2.0/go.mod h1:N5FLswTubnxKxOJHM7XZC074qpeEdLy3CgAVsdMucK0= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -672,6 +691,8 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= 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-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -723,6 +744,7 @@ golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211104170005-ce137452f963 h1:8gJUadZl+kWvZBqG/LautX0X6qe5qTC2VI/3V3NBRAY= golang.org/x/net v0.0.0-20211104170005-ce137452f963/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -741,8 +763,9 @@ golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f h1:Qmd2pbz05z7z6lm0DrgQVVPuBm92jqujBKMHMOlOQEw= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 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-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -832,8 +855,10 @@ golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0/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-20211103235746-7861aae1554b h1:1VkfZQv42XQlA/jchYumAnv1UPo6RgF9rJFkTgZIxO4= -golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211213223007-03aa0b5f6827/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -901,6 +926,7 @@ golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200522201501-cb1345f3a375/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200717024301-6ddee64345a6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= @@ -917,6 +943,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w= +golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1018,8 +1046,8 @@ google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEc google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20211102202547-e9cf271f7f2c h1:UQDUEuW1R2dcciOjiFmuzE4skW4n/zGGNMU0RhU3hQI= -google.golang.org/genproto v0.0.0-20211102202547-e9cf271f7f2c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1 h1:b9mVrqYfq3P4bCdaLg1qtBnPzUYgglsIdjZkL/fQVOE= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1046,7 +1074,6 @@ google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQ google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= google.golang.org/grpc v1.42.0 h1:XT2/MFpuPFsEX2fWh3YQtHkZ+WYZFQRfaUgLZYj/p6A= google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= @@ -1071,6 +1098,7 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= @@ -1099,6 +1127,8 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +mvdan.cc/gofumpt v0.2.1 h1:7jakRGkQcLAJdT+C8Bwc9d0BANkVPSkHZkzNv07pJAs= +mvdan.cc/gofumpt v0.2.1/go.mod h1:a/rvZPhsNaedOJBzqRD9omnwVwHZsBdJirXHa9Gh9Ig= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/e2e/newenemy/newenemy_test.go b/e2e/newenemy/newenemy_test.go index a1841e1cfd..958072f227 100644 --- a/e2e/newenemy/newenemy_test.go +++ b/e2e/newenemy/newenemy_test.go @@ -2,34 +2,57 @@ package newenemy import ( "context" + "database/sql" + "flag" "fmt" - "io" + "log" "math" "math/rand" "os" + "strings" "testing" + "text/template" "time" v0 "github.com/authzed/authzed-go/proto/authzed/api/v0" "github.com/authzed/authzed-go/proto/authzed/api/v1alpha1" "github.com/authzed/spicedb/pkg/zookie" + "github.com/jackc/pgtype" + "github.com/jackc/pgx/v4" "github.com/stretchr/testify/require" "github.com/authzed/spicedb/e2e" "github.com/authzed/spicedb/e2e/cockroach" + "github.com/authzed/spicedb/e2e/generator" "github.com/authzed/spicedb/e2e/spice" ) -const schema = ` -definition user {} -definition resource { - relation direct: user - relation excluded: user +type SchemaData struct { + Prefixes []string +} + +const schemaText = ` +{{ range .Prefixes }} +definition {{.}}/user {} +definition {{.}}/resource { + relation direct: {{.}}/user + relation excluded: {{.}}/user permission allowed = direct - excluded } +{{ end }} ` -var testCtx context.Context +const ( + objIDRegex = "[a-zA-Z0-9_][a-zA-Z0-9/_-]{0,127}" + namespacePrefixRegex = "[a-z][a-z0-9_]{2,62}[a-z0-9]" +) + +var ( + maxIterations = flag.Int("max-iterations", 1000, "iteration cap for statistic-based tests (0 for no limit)") + + schemaTpl = template.Must(template.New("schema").Parse(schemaText)) + testCtx context.Context +) func TestMain(m *testing.M) { var cancel context.CancelFunc @@ -45,41 +68,47 @@ const ( dbName = "spicedbnetest" ) -func startCluster(ctx context.Context, t testing.TB) cockroach.Cluster { +func initializeTestCRDBCluster(ctx context.Context, t testing.TB) cockroach.Cluster { require := require.New(t) - fmt.Println("starting cockroach...") + t.Log("starting cockroach...") crdbCluster := cockroach.NewCluster(3) for _, c := range crdbCluster { require.NoError(c.Start(ctx)) } - fmt.Println("initializing crdb...") - crdbCluster.Init(ctx, os.Stdout, os.Stdout) - require.NoError(crdbCluster.SQL(ctx, os.Stdout, os.Stdout, + t.Log("initializing crdb...") + tlog := e2e.NewTLog(t) + crdbCluster.Init(ctx, tlog, tlog) + require.NoError(crdbCluster.SQL(ctx, tlog, tlog, fmt.Sprintf(createDb, dbName), )) - fmt.Println("migrating...") - require.NoError(spice.MigrateHead(ctx, "cockroachdb", crdbCluster[0].ConnectionString(dbName))) + t.Log("migrating...") + require.NoError(spice.MigrateHead(ctx, tlog, "cockroachdb", crdbCluster[0].ConnectionString(dbName))) - fmt.Println("attempting to connect...") - require.NoError(crdbCluster[2].Connect(ctx, os.Stdout, dbName)) + t.Log("attempting to connect...") + require.NoError(crdbCluster[2].Connect(ctx, tlog, dbName)) + require.NoError(crdbCluster[1].Connect(ctx, tlog, dbName)) + require.NoError(crdbCluster[0].Connect(ctx, tlog, dbName)) return crdbCluster } func TestNoNewEnemy(t *testing.T) { require := require.New(t) + rand.Seed(time.Now().UnixNano()) ctx, cancel := context.WithCancel(testCtx) defer cancel() - crdb := startCluster(ctx, t) + crdb := initializeTestCRDBCluster(ctx, t) + + tlog := e2e.NewTLog(t) t.Log("starting vulnerable spicedb...") vulnerableSpiceDb := spice.NewClusterFromCockroachCluster(crdb, spice.WithDbName(dbName)) - require.NoError(vulnerableSpiceDb.Start(ctx, os.Stdout, "vulnerable", + require.NoError(vulnerableSpiceDb.Start(ctx, tlog, "vulnerable", "--datastore-tx-overlap-strategy=insecure")) - require.NoError(vulnerableSpiceDb.Connect(ctx, os.Stdout)) + require.NoError(vulnerableSpiceDb.Connect(ctx, tlog)) t.Log("start protected spicedb cluster") protectedSpiceDb := spice.NewClusterFromCockroachCluster(crdb, @@ -89,36 +118,40 @@ func TestNoNewEnemy(t *testing.T) { spice.WithMetricsPort(9100), spice.WithDashboardPort(8100), spice.WithDbName(dbName)) - require.NoError(protectedSpiceDb.Start(ctx, os.Stdout, "protected")) - require.NoError(protectedSpiceDb.Connect(ctx, os.Stdout)) + require.NoError(protectedSpiceDb.Start(ctx, tlog, "protected")) + require.NoError(protectedSpiceDb.Connect(ctx, tlog)) t.Log("configure small ranges, single replicas, short ttl") - require.NoError(crdb.SQL(ctx, os.Stdout, os.Stdout, + require.NoError(crdb.SQL(ctx, tlog, tlog, fmt.Sprintf(setSmallRanges, dbName), )) - t.Log("modifying time") - timeDelay := 100 * time.Millisecond - require.NoError(crdb.TimeDelay(ctx, e2e.MustFile(ctx, t, "timeattack.log"), 1, -1*timeDelay)) + t.Log("fill with schemas to span multiple ranges") + // 4000 is larger than we need to span all three nodes, but a higher number + // seems to make the test converge faster + schemaData := generateSchemaData(4000, 500) + require.NoError(fillSchema(t, schemaData, vulnerableSpiceDb[1].Client().V1Alpha1().Schema())) - t.Log("modifying network") - networkDelay := timeDelay / 2 - require.NoError(crdb.NetworkDelay(ctx, e2e.MustFile(ctx, t, "netattack.log"), 1, networkDelay)) + t.Log("determining a prefix with a leader on the slow node") + slowNodeId, err := crdb[1].NodeID(testCtx) + require.NoError(err) + slowPrefix := prefixForNode(ctx, crdb[1].Conn(), schemaData, slowNodeId) - t.Log("create initial test schema") - require.NoError(initSchema(vulnerableSpiceDb[0].Client().V1Alpha1().Schema())) + t.Logf("using prefix %s for slow node %d", slowPrefix, slowNodeId) t.Log("filling with data to span multiple ranges") - rand.Seed(time.Now().UnixNano()) - fill(require, vulnerableSpiceDb[0].Client().V0().ACL(), 4000, 100) + fill(t, vulnerableSpiceDb[0].Client().V0().ACL(), slowPrefix, 4000, 1000) + + t.Log("modifying time") + require.NoError(crdb.TimeDelay(ctx, e2e.MustFile(ctx, t, "timeattack.log"), 1, -200*time.Millisecond)) const sampleSize = 5 samples := make([]int, sampleSize) for i := 0; i < sampleSize; i++ { t.Log(i, "check vulnerability with mitigations disabled") - checkCtx, checkCancel := context.WithTimeout(ctx, 5*time.Minute) - protected, attempts := checkNoNewEnemy(checkCtx, t, vulnerableSpiceDb, -1) + checkCtx, checkCancel := context.WithTimeout(ctx, 30*time.Minute) + protected, attempts := checkNoNewEnemy(checkCtx, t, schemaData, slowNodeId, crdb, vulnerableSpiceDb, -1) require.NotNil(protected, "unable to determine if spicedb displays newenemy when mitigations are disabled within the time limit") require.False(*protected) checkCancel() @@ -139,22 +172,28 @@ func TestNoNewEnemy(t *testing.T) { samplestddev := stddev / math.Sqrt(float64(sampleSize)) // how many iterations do we need to get > 3sigma from the mean? - // cap max_iterations to control test runtime - const max_iterations = 100 - iterations := int(math.Min(max_iterations, math.Ceil(3*stddev*samplestddev+mean))) + // cap maxIterations to control test runtime. + iterations := int(math.Ceil(3*stddev*samplestddev + mean)) + if *maxIterations != 0 && *maxIterations < iterations { + iterations = *maxIterations + } t.Logf("check spicedb is protected after %d attempts", iterations) - protected, _ := checkNoNewEnemy(ctx, t, protectedSpiceDb, iterations) + protected, _ := checkNoNewEnemy(ctx, t, schemaData, slowNodeId, crdb, protectedSpiceDb, iterations) require.NotNil(protected, "unable to determine if spicedb is protected within the time limit") require.True(*protected, "protection is enabled, but newenemy detected") } // checkNoNewEnemy returns true if the service is protected, false if it is vulnerable, and nil if we couldn't determine -func checkNoNewEnemy(ctx context.Context, t testing.TB, spicedb spice.Cluster, candidateCount int) (*bool, int) { +func checkNoNewEnemy(ctx context.Context, t testing.TB, schemaData []SchemaData, slowNodeId int, crdb cockroach.Cluster, spicedb spice.Cluster, candidateCount int) (*bool, int) { var attempts int + + prefix := prefixForNode(ctx, crdb[1].Conn(), schemaData, slowNodeId) + objIdGenerator := generator.NewUniqueGenerator(objIDRegex) + for { attempts++ - directs, excludes := generateTuples(1) + directs, excludes := generateTuples(prefix, 1, objIdGenerator) // write to node 1 r1, err := spicedb[0].Client().V0().ACL().Write(testCtx, &v0.WriteRequest{ @@ -165,6 +204,11 @@ func checkNoNewEnemy(ctx context.Context, t testing.TB, spicedb spice.Cluster, c continue } + // the first write has to read the namespaces from the second node, + // which will resync the timestamps. sleeping allows the clocks to get + // back out of sync + time.Sleep(100 * time.Millisecond) + // write to node 2 (clock is behind) r2, err := spicedb[1].Client().V0().ACL().Write(testCtx, &v0.WriteRequest{ Updates: []*v0.RelationTupleUpdate{directs[0]}, @@ -174,10 +218,10 @@ func checkNoNewEnemy(ctx context.Context, t testing.TB, spicedb spice.Cluster, c continue } - canHas, err := spicedb[2].Client().V0().ACL().Check(context.Background(), &v0.CheckRequest{ + canHas, err := spicedb[1].Client().V0().ACL().Check(context.Background(), &v0.CheckRequest{ TestUserset: &v0.ObjectAndRelation{ - Namespace: "resource", - ObjectId: "thegoods", + Namespace: directs[0].Tuple.ObjectAndRelation.Namespace, + ObjectId: directs[0].Tuple.ObjectAndRelation.ObjectId, Relation: "allowed", }, User: directs[0].Tuple.GetUser(), @@ -191,7 +235,21 @@ func checkNoNewEnemy(ctx context.Context, t testing.TB, spicedb spice.Cluster, c t.Log("service is subject to the new enemy problem") } - analyzeCalls(os.Stdout, r1.GetRevision(), r2.GetRevision()) + r1leader, r2leader := getLeaderNode(testCtx, crdb[1].Conn(), excludes[0].Tuple), getLeaderNode(testCtx, crdb[1].Conn(), directs[0].Tuple) + ns1Leader := getLeaderNodeForNamespace(testCtx, crdb[1].Conn(), excludes[0].Tuple.ObjectAndRelation.Namespace) + ns2Leader := getLeaderNodeForNamespace(testCtx, crdb[1].Conn(), excludes[0].Tuple.User.GetUserset().Namespace) + z1, _ := zookie.DecodeRevision(r1.GetRevision()) + z2, _ := zookie.DecodeRevision(r2.GetRevision()) + t.Log(z1, z2, z1.Sub(z2).String(), r1leader, r2leader, ns1Leader, ns2Leader) + + if ns1Leader != slowNodeId || ns2Leader != slowNodeId { + t.Log("namespace leader changed, pick new prefix and fill") + prefix = prefixForNode(ctx, crdb[1].Conn(), schemaData, slowNodeId) + // need to fill new prefix + t.Log("filling with data to span multiple ranges for prefix ", prefix) + fill(t, spicedb[0].Client().V0().ACL(), prefix, 4000, 1000) + continue + } if canHas.IsMember { t.Log("service is subject to the new enemy problem") @@ -199,6 +257,11 @@ func checkNoNewEnemy(ctx context.Context, t testing.TB, spicedb spice.Cluster, c return &protected, attempts } + if !canHas.IsMember && z1.Sub(z2).IsPositive() { + t.Log("error in test, object id has been re-used.") + continue + } + // if we find causal reversals, but no newenemy, assume we're protected if candidateCount > 0 && attempts >= candidateCount { t.Log(candidateCount, "(would be) causal reversals with no new enemy detected") @@ -206,59 +269,75 @@ func checkNoNewEnemy(ctx context.Context, t testing.TB, spicedb spice.Cluster, c return &protected, attempts } + if attempts > 1000 { + t.Log("trying with a new prefix") + attempts = 0 + prefix = prefixForNode(ctx, crdb[1].Conn(), schemaData, slowNodeId) + t.Log("filling with data to span multiple ranges for prefix ", prefix) + fill(t, spicedb[0].Client().V0().ACL(), prefix, 4000, 1000) + continue + } + select { case <-ctx.Done(): return nil, attempts default: continue } + + // allow clocks to desync + time.Sleep(100 * time.Millisecond) } } func BenchmarkBatchWrites(b *testing.B) { ctx, cancel := context.WithCancel(testCtx) defer cancel() - crdb := startCluster(ctx, b) + crdb := initializeTestCRDBCluster(ctx, b) spicedb := spice.NewClusterFromCockroachCluster(crdb, spice.WithDbName(dbName)) - require.NoError(b, spicedb.Start(ctx, os.Stdout, "")) - require.NoError(b, spicedb.Connect(ctx, os.Stdout)) + tlog := e2e.NewTLog(b) + require.NoError(b, spicedb.Start(ctx, tlog, "")) + require.NoError(b, spicedb.Connect(ctx, tlog)) - exludes, directs := generateTuples(b.N * 20) + directs, excludes := generateTuples("", b.N*20, generator.NewUniqueGenerator(objIDRegex)) b.ResetTimer() _, err := spicedb[0].Client().V0().ACL().Write(testCtx, &v0.WriteRequest{ - Updates: exludes, + Updates: excludes, }) if err != nil { - fmt.Println(err) + b.Log(err) } _, err = spicedb[0].Client().V0().ACL().Write(testCtx, &v0.WriteRequest{ Updates: directs, }) if err != nil { - fmt.Println(err) + b.Log(err) } } func BenchmarkConflictingTupleWrites(b *testing.B) { ctx, cancel := context.WithCancel(testCtx) defer cancel() - crdb := startCluster(ctx, b) + crdb := initializeTestCRDBCluster(ctx, b) spicedb := spice.NewClusterFromCockroachCluster(crdb, spice.WithDbName(dbName)) - require.NoError(b, spicedb.Start(ctx, os.Stdout, "")) - require.NoError(b, spicedb.Connect(ctx, os.Stdout)) + tlog := e2e.NewTLog(b) + require.NoError(b, spicedb.Start(ctx, tlog, "")) + require.NoError(b, spicedb.Connect(ctx, tlog)) // fill with tuples to ensure we span multiple ranges - fill(require.New(b), spicedb[0].Client().V0().ACL(), 2000, 100) + fill(b, spicedb[0].Client().V0().ACL(), "", 2000, 100) b.ResetTimer() - checkNoNewEnemy(ctx, b, spicedb, b.N) + checkNoNewEnemy(ctx, b, generateSchemaData(1, 1), 1, crdb, spicedb, b.N) } -func fill(require *require.Assertions, client v0.ACLServiceClient, fillerCount, batchSize int) { - directs, excludes := generateTuples(fillerCount) +func fill(t testing.TB, client v0.ACLServiceClient, prefix string, fillerCount, batchSize int) { + t.Log("filling prefix", prefix) + require := require.New(t) + directs, excludes := generateTuples(prefix, fillerCount, generator.NewUniqueGenerator(objIDRegex)) for i := 0; i < fillerCount/batchSize; i++ { - fmt.Println("filling ", i*batchSize, "to", (i+1)*batchSize) + t.Log("filling", i*batchSize, "to", (i+1)*batchSize) _, err := client.Write(testCtx, &v0.WriteRequest{ Updates: excludes[i*batchSize : (i+1)*batchSize], }) @@ -270,29 +349,77 @@ func fill(require *require.Assertions, client v0.ACLServiceClient, fillerCount, } } -func initSchema(schemaClient v1alpha1.SchemaServiceClient) error { - _, err := schemaClient.WriteSchema(context.Background(), &v1alpha1.WriteSchemaRequest{ - Schema: schema, - }) - return err +func fillSchema(t testing.TB, data []SchemaData, schemaClient v1alpha1.SchemaServiceClient) error { + var b strings.Builder + batchSize := len(data[0].Prefixes) + for i, d := range data { + t.Logf("filling %d to %d", i*batchSize, (i+1)*batchSize) + b.Reset() + err := schemaTpl.Execute(&b, d) + if err != nil { + return err + } + _, err = schemaClient.WriteSchema(context.Background(), &v1alpha1.WriteSchemaRequest{ + Schema: b.String(), + }) + if err != nil { + return err + } + } + return nil +} + +// prefixForNode finds a prefix with namespace leaders on the node id +func prefixForNode(ctx context.Context, conn *pgx.Conn, data []SchemaData, node int) string { + for { + // get a random prefix + d := data[rand.Intn(len(data))] + candidate := d.Prefixes[rand.Intn(len(d.Prefixes))] + ns1 := candidate + "/user" + ns2 := candidate + "/resource" + leader1 := getLeaderNodeForNamespace(ctx, conn, ns1) + leader2 := getLeaderNodeForNamespace(ctx, conn, ns2) + if leader1 == leader2 && leader1 == node { + return candidate + } + select { + case <-ctx.Done(): + return "" + default: + continue + } + } +} + +func generateSchemaData(n int, batchSize int) (data []SchemaData) { + data = make([]SchemaData, 0, n/batchSize) + prefixGenerator := generator.NewUniqueGenerator(namespacePrefixRegex) + for i := 0; i < n/batchSize; i++ { + schema := SchemaData{Prefixes: make([]string, 0, batchSize)} + for j := i * batchSize; j < (i+1)*batchSize; j++ { + schema.Prefixes = append(schema.Prefixes, prefixGenerator.Next()) + } + data = append(data, schema) + } + return } -func generateTuples(n int) (directs []*v0.RelationTupleUpdate, excludes []*v0.RelationTupleUpdate) { +func generateTuples(prefix string, n int, objIdGenerator *generator.UniqueGenerator) (directs []*v0.RelationTupleUpdate, excludes []*v0.RelationTupleUpdate) { directs = make([]*v0.RelationTupleUpdate, 0, n) excludes = make([]*v0.RelationTupleUpdate, 0, n) for i := 0; i < n; i++ { user := &v0.User{ UserOneof: &v0.User_Userset{ Userset: &v0.ObjectAndRelation{ - Namespace: "user", - ObjectId: randSeq(16), + Namespace: prefix + "/user", + ObjectId: objIdGenerator.Next(), Relation: "...", }, }, } tupleExclude := &v0.RelationTuple{ ObjectAndRelation: &v0.ObjectAndRelation{ - Namespace: "resource", + Namespace: prefix + "/resource", ObjectId: "thegoods", Relation: "excluded", }, @@ -300,7 +427,7 @@ func generateTuples(n int) (directs []*v0.RelationTupleUpdate, excludes []*v0.Re } tupleDirect := &v0.RelationTuple{ ObjectAndRelation: &v0.ObjectAndRelation{ - Namespace: "resource", + Namespace: prefix + "/resource", ObjectId: "thegoods", Relation: "direct", }, @@ -318,26 +445,52 @@ func generateTuples(n int) (directs []*v0.RelationTupleUpdate, excludes []*v0.Re return } -// after we've checked, analyze the previous calls -func analyzeCalls(out io.Writer, r1, r2 *v0.Zookie) { - z1, _ := zookie.DecodeRevision(r1) - z2, _ := zookie.DecodeRevision(r2) - - // the best we can do when mitigations are enabled is guess that timestamps - // with the same nanosecond timestamp were protected - if z2.GreaterThan(z1) && z2.IntPart() == z1.IntPart() { - fmt.Fprintln(out, "candidate found") +// getLeaderNode returns the node with the lease leader for the range containing the tuple +func getLeaderNode(ctx context.Context, conn *pgx.Conn, tuple *v0.RelationTuple) int { + t := tuple + rows, err := conn.Query(ctx, "SHOW RANGE FROM TABLE relation_tuple FOR ROW ($1::text,$2::text,$3::text,$4::text,$5::text,$6::text)", + t.ObjectAndRelation.Namespace, + t.ObjectAndRelation.ObjectId, + t.ObjectAndRelation.Relation, + t.User.GetUserset().Namespace, + t.User.GetUserset().ObjectId, + t.User.GetUserset().Relation, + ) + defer rows.Close() + if err != nil { + log.Fatalf("failed to exec: %v", err) } - - fmt.Fprintln(out, z1, z2, z1.Sub(z2).String()) + return leaderFromRangeRow(rows) } -var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") +// getLeaderNodeForNamespace returns the node with the lease leader for the range containing the namespace +func getLeaderNodeForNamespace(ctx context.Context, conn *pgx.Conn, namespace string) int { + rows, err := conn.Query(ctx, "SHOW RANGE FROM TABLE namespace_config FOR ROW ($1::text)", + namespace, + ) + defer rows.Close() + if err != nil { + log.Fatalf("failed to exec: %v", err) + } + return leaderFromRangeRow(rows) +} -func randSeq(n int) string { - b := make([]rune, n) - for i := range b { - b[i] = letters[rand.Intn(len(letters))] +func leaderFromRangeRow(rows pgx.Rows) int { + var ( + startKey sql.NullString + endKey sql.NullString + rangeID int + leaseHolder int + leaseHoldeLocality sql.NullString + replicas pgtype.Int8Array + replicaLocalities pgtype.TextArray + ) + + for rows.Next() { + if err := rows.Scan(&startKey, &endKey, &rangeID, &leaseHolder, &leaseHoldeLocality, &replicas, &replicaLocalities); err != nil { + panic(err) + } + break } - return string(b) + return leaseHolder } diff --git a/e2e/spice/spicedb.go b/e2e/spice/spicedb.go index 5fbf11966c..3eeeadf61f 100644 --- a/e2e/spice/spicedb.go +++ b/e2e/spice/spicedb.go @@ -5,38 +5,38 @@ import ( "fmt" "io" "net" - "os" "strconv" "time" "github.com/authzed/grpcutil" - "github.com/authzed/spicedb/e2e" "google.golang.org/grpc" + "github.com/authzed/spicedb/e2e" "github.com/authzed/spicedb/e2e/cockroach" ) -//go:generate go run github.com/ecordell/optgen -output spicedb_options.go . SpiceDb +//go:generate go run github.com/ecordell/optgen -output spicedb_options.go . Node // Node represents a single instance of spicedb started via exec type Node struct { - ID string - PresharedKey string - Datastore string - DbName string - URI string - GrpcPort int - HttpPort int - DispatchPort int - MetricsPort int - DashboardPort int - Pid int - Cancel context.CancelFunc - client e2e.Client + ID string + PresharedKey string + Datastore string + DbName string + URI string + GrpcPort int + HttpPort int + DispatchPort int + MetricsPort int + DashboardPort int + HedgingEnabled bool + Pid int + Cancel context.CancelFunc + client e2e.Client } // WithTestDefaults sets the default values for Node -func WithTestDefaults(opts ...SpiceDbOption) SpiceDbOption { +func WithTestDefaults(opts ...NodeOption) NodeOption { return func(s *Node) { for _, o := range opts { o(s) @@ -76,6 +76,7 @@ func (s *Node) Start(ctx context.Context, logprefix string, args ...string) erro "./spicedb", "serve", "--log-level=debug", + fmt.Sprintf("--datastore-request-hedging=%t", s.HedgingEnabled), "--grpc-preshared-key=" + s.PresharedKey, "--datastore-engine=" + s.Datastore, "--datastore-conn-uri=" + s.URI, @@ -132,11 +133,11 @@ type Cluster []*Node // NewClusterFromCockroachCluster creates a spicedb instance for every // cockroach instance, with each spicedb configured to talk to the corresponding // cockraoch node. -func NewClusterFromCockroachCluster(c cockroach.Cluster, opts ...SpiceDbOption) Cluster { +func NewClusterFromCockroachCluster(c cockroach.Cluster, opts ...NodeOption) Cluster { ss := make([]*Node, 0, len(c)) // the prototypical node will be used to generate a set of nodes - proto := NewSpiceDbWithOptions(WithTestDefaults(opts...)) + proto := NewNodeWithOptions(WithTestDefaults(opts...)) for i := 0; i < len(c); i++ { ss = append(ss, &Node{ @@ -188,9 +189,9 @@ func (c *Cluster) Connect(ctx context.Context, out io.Writer) error { } // MigrateHead migrates a Datastore to the latest revision defined in spicedb -func MigrateHead(ctx context.Context, datastore, uri string) error { +func MigrateHead(ctx context.Context, out io.Writer, datastore, uri string) error { for i := 0; i < 5; i++ { - if err := e2e.Run(ctx, os.Stdout, os.Stderr, + if err := e2e.Run(ctx, out, out, "./spicedb", "migrate", "head", "--datastore-engine="+datastore, "--datastore-conn-uri="+uri, diff --git a/e2e/spice/spicedb_options.go b/e2e/spice/spicedb_options.go index 4d980cced5..367daf214c 100644 --- a/e2e/spice/spicedb_options.go +++ b/e2e/spice/spicedb_options.go @@ -3,105 +3,112 @@ package spice import "context" -type SpiceDbOption func(s *Node) +type NodeOption func(n *Node) -// NewSpiceDbWithOptions creates a new Node with the passed in options set -func NewSpiceDbWithOptions(opts ...SpiceDbOption) *Node { - s := &Node{} +// NewNodeWithOptions creates a new Node with the passed in options set +func NewNodeWithOptions(opts ...NodeOption) *Node { + n := &Node{} for _, o := range opts { - o(s) + o(n) } - return s + return n } -// SpiceDbWithOptions configures an existing Node with the passed in options set -func SpiceDbWithOptions(s *Node, opts ...SpiceDbOption) *Node { +// NodeWithOptions configures an existing Node with the passed in options set +func NodeWithOptions(n *Node, opts ...NodeOption) *Node { for _, o := range opts { - o(s) + o(n) } - return s + return n } -// WithId returns an option that can set ID on a Node -func WithId(id string) SpiceDbOption { - return func(s *Node) { - s.ID = id +// WithID returns an option that can set ID on a Node +func WithID(iD string) NodeOption { + return func(n *Node) { + n.ID = iD } } // WithPresharedKey returns an option that can set PresharedKey on a Node -func WithPresharedKey(presharedKey string) SpiceDbOption { - return func(s *Node) { - s.PresharedKey = presharedKey +func WithPresharedKey(presharedKey string) NodeOption { + return func(n *Node) { + n.PresharedKey = presharedKey } } // WithDatastore returns an option that can set Datastore on a Node -func WithDatastore(datastore string) SpiceDbOption { - return func(s *Node) { - s.Datastore = datastore +func WithDatastore(datastore string) NodeOption { + return func(n *Node) { + n.Datastore = datastore } } // WithDbName returns an option that can set DbName on a Node -func WithDbName(dbName string) SpiceDbOption { - return func(s *Node) { - s.DbName = dbName +func WithDbName(dbName string) NodeOption { + return func(n *Node) { + n.DbName = dbName } } -// WithUri returns an option that can set URI on a Node -func WithUri(uri string) SpiceDbOption { - return func(s *Node) { - s.URI = uri +// WithURI returns an option that can set URI on a Node +func WithURI(uRI string) NodeOption { + return func(n *Node) { + n.URI = uRI } } // WithGrpcPort returns an option that can set GrpcPort on a Node -func WithGrpcPort(grpcPort int) SpiceDbOption { - return func(s *Node) { - s.GrpcPort = grpcPort +func WithGrpcPort(grpcPort int) NodeOption { + return func(n *Node) { + n.GrpcPort = grpcPort } } -// WithDispatchPort returns an option that can set DispatchPort on a Node -func WithDispatchPort(dispatchPort int) SpiceDbOption { - return func(s *Node) { - s.DispatchPort = dispatchPort +// WithHttpPort returns an option that can set HttpPort on a Node +func WithHttpPort(httpPort int) NodeOption { + return func(n *Node) { + n.HttpPort = httpPort } } -// WithHttpPort returns an option that can set HttpPort on a Node -func WithHttpPort(httpPort int) SpiceDbOption { - return func(s *Node) { - s.HttpPort = httpPort +// WithDispatchPort returns an option that can set DispatchPort on a Node +func WithDispatchPort(dispatchPort int) NodeOption { + return func(n *Node) { + n.DispatchPort = dispatchPort } } // WithMetricsPort returns an option that can set MetricsPort on a Node -func WithMetricsPort(metricsPort int) SpiceDbOption { - return func(s *Node) { - s.MetricsPort = metricsPort +func WithMetricsPort(metricsPort int) NodeOption { + return func(n *Node) { + n.MetricsPort = metricsPort } } // WithDashboardPort returns an option that can set DashboardPort on a Node -func WithDashboardPort(dashboardPort int) SpiceDbOption { - return func(s *Node) { - s.DashboardPort = dashboardPort +func WithDashboardPort(dashboardPort int) NodeOption { + return func(n *Node) { + n.DashboardPort = dashboardPort + } +} + +// WithHedgingEnabled returns an option that can set HedgingEnabled on a Node +func WithHedgingEnabled(hedgingEnabled bool) NodeOption { + return func(n *Node) { + n.HedgingEnabled = hedgingEnabled } } // WithPid returns an option that can set Pid on a Node -func WithPid(pid int) SpiceDbOption { - return func(s *Node) { - s.Pid = pid +func WithPid(pid int) NodeOption { + return func(n *Node) { + n.Pid = pid } } // WithCancel returns an option that can set Cancel on a Node -func WithCancel(cancel context.CancelFunc) SpiceDbOption { - return func(s *Node) { - s.Cancel = cancel +func WithCancel(cancel context.CancelFunc) NodeOption { + return func(n *Node) { + n.Cancel = cancel } } diff --git a/e2e/tools.go b/e2e/tools.go new file mode 100644 index 0000000000..7248233d04 --- /dev/null +++ b/e2e/tools.go @@ -0,0 +1,10 @@ +//go:build tools +// +build tools + +package tools + +import ( + _ "github.com/ecordell/optgen" + _ "golang.org/x/tools/cmd/stringer" + _ "mvdan.cc/gofumpt" +) diff --git a/e2e/util.go b/e2e/util.go index 6397f01ead..9ecc0d458a 100644 --- a/e2e/util.go +++ b/e2e/util.go @@ -8,15 +8,18 @@ import ( // TLogger wraps a testing.TB and makes it conform to io.Writer type TLogger struct { testing.TB + skip int } // Write satisfied io.Writer func (t *TLogger) Write(p []byte) (int, error) { + t.Helper() t.Log(string(p)) return len(p), nil } -// TLog returns a TLogger -func TLog(t testing.TB) io.Writer { +// NewTLog returns a TLogger +func NewTLog(t testing.TB) io.Writer { + t.Helper() return &TLogger{TB: t} } diff --git a/internal/namespace/manager.go b/internal/namespace/manager.go index b9e9d12227..235144abda 100644 --- a/internal/namespace/manager.go +++ b/internal/namespace/manager.go @@ -29,7 +29,7 @@ type Manager interface { // ReadNamespaceAndTypes reads a namespace definition, version, and type system and returns it if found. ReadNamespaceAndTypes(ctx context.Context, nsName string, revision decimal.Decimal) (*v0.NamespaceDefinition, *NamespaceTypeSystem, error) - // Closes the namespace manager, disposing of any resources. + // Close closes the namespace manager, disposing of any resources. // // NOTE: Should *not* call Close on the datastore. Close() error