diff --git a/e2e/go.mod b/e2e/go.mod index 1e2f6f6f9c..719c44825d 100644 --- a/e2e/go.mod +++ b/e2e/go.mod @@ -5,7 +5,7 @@ go 1.23.1 replace github.com/authzed/spicedb => ../ require ( - github.com/authzed/authzed-go v1.2.0 + github.com/authzed/authzed-go v1.2.2-0.20250107172318-7fd4159ab2b7 github.com/authzed/grpcutil v0.0.0-20240123092924-129dc0a6a6e1 github.com/authzed/spicedb v1.29.5 github.com/brianvoe/gofakeit/v6 v6.23.2 diff --git a/e2e/go.sum b/e2e/go.sum index fdc607bd01..9d0e904650 100644 --- a/e2e/go.sum +++ b/e2e/go.sum @@ -34,8 +34,8 @@ github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8 github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= -github.com/authzed/authzed-go v1.2.0 h1:Ep1sRJMxcArB++kYqHbYKQCb/GgdGZI0cW4gZrJ1K40= -github.com/authzed/authzed-go v1.2.0/go.mod h1:4lkFxvaCISG1roRdnUt35/Sk1StVuMD1QCwTd/BcWcM= +github.com/authzed/authzed-go v1.2.2-0.20250107172318-7fd4159ab2b7 h1:f99Iz0FoEyqPelojk1kWWgSDVJtDEMrLb+6qVdohoqs= +github.com/authzed/authzed-go v1.2.2-0.20250107172318-7fd4159ab2b7/go.mod h1:/+NblSrzA6Lm6vUO3fqZyLh8MDCLUQq2AyJMlHb32DE= github.com/authzed/cel-go v0.20.2 h1:GlmLecGry7Z8HU0k+hmaHHUV05ZHrsFxduXHtIePvck= github.com/authzed/cel-go v0.20.2/go.mod h1:pJHVFWbqUHV1J+klQoZubdKswlbxcsbojda3mye9kiU= github.com/authzed/grpcutil v0.0.0-20240123092924-129dc0a6a6e1 h1:zBfQzia6Hz45pJBeURTrv1b6HezmejB6UmiGuBilHZM= @@ -169,8 +169,8 @@ github.com/googleapis/gax-go/v2 v2.14.0 h1:f+jMrjBPl+DL9nI4IQzLUxMq7XrAqFYB7hBPq github.com/googleapis/gax-go/v2 v2.14.0/go.mod h1:lhBCnjdLrWRaPvLWhmc8IS24m9mr07qSYnHncrgo+zk= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0 h1:kQ0NI7W1B3HwiN5gAYtY+XFItDPbLBwYRxAqbFTyDes= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0/go.mod h1:zrT2dxOAjNFPRGjTUe2Xmb4q4YdUwVvQFV6xiCSf+z0= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI= github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg= github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ= github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= @@ -371,8 +371,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T 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/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.214.0 h1:h2Gkq07OYi6kusGOaT/9rnNljuXmqPnaig7WGPmKbwA= -google.golang.org/api v0.214.0/go.mod h1:bYPpLG8AyeMWwDU6NXoB00xC0DFkikVvd5MfwoxjLqE= +google.golang.org/api v0.209.0 h1:Ja2OXNlyRlWCWu8o+GgI4yUn/wz9h/5ZfFbKz+dQX+w= +google.golang.org/api v0.209.0/go.mod h1:I53S168Yr/PNDNMi5yPnDc0/LGRZO6o7PoEbl/HY3CM= 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-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= diff --git a/go.mod b/go.mod index 9692c22be4..e3dacdbcfe 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/Masterminds/semver v1.5.0 github.com/Masterminds/squirrel v1.5.4 github.com/Yiling-J/theine-go v0.6.0 - github.com/authzed/authzed-go v1.2.0 + github.com/authzed/authzed-go v1.2.2-0.20250107172318-7fd4159ab2b7 github.com/authzed/consistent v0.1.0 github.com/authzed/grpcutil v0.0.0-20240123092924-129dc0a6a6e1 github.com/aws/aws-sdk-go v1.55.5 @@ -44,13 +44,13 @@ require ( github.com/go-sql-driver/mysql v1.8.1 github.com/gogo/protobuf v1.3.2 github.com/golang/snappy v0.0.4 - github.com/golangci/golangci-lint v1.63.0 + github.com/golangci/golangci-lint v1.63.4 github.com/google/go-cmp v0.6.0 github.com/google/go-github/v43 v43.0.0 github.com/google/uuid v1.6.0 github.com/gosimple/slug v1.15.0 github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 - github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0 + github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 github.com/hashicorp/go-memdb v1.3.4 github.com/hashicorp/go-multierror v1.1.1 @@ -106,7 +106,7 @@ require ( golang.org/x/sync v0.10.0 golang.org/x/time v0.8.0 golang.org/x/vuln v1.1.3 - google.golang.org/api v0.214.0 + google.golang.org/api v0.209.0 google.golang.org/genproto/googleapis/api v0.0.0-20241219192143-6b3ec007d9bb google.golang.org/genproto/googleapis/rpc v0.0.0-20241219192143-6b3ec007d9bb google.golang.org/grpc v1.69.2 @@ -235,7 +235,6 @@ require ( github.com/golangci/go-printf-func-name v0.1.0 // indirect github.com/golangci/gofmt v0.0.0-20241223200906-057b0627d9b9 // indirect github.com/golangci/misspell v0.6.0 // indirect - github.com/golangci/modinfo v0.3.4 // indirect github.com/golangci/plugin-module-register v0.1.1 // indirect github.com/golangci/revgrep v0.5.3 // indirect github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed // indirect @@ -284,11 +283,11 @@ require ( github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/lasiar/canonicalheader v1.1.2 // indirect - github.com/ldez/exptostd v0.3.0 // indirect + github.com/ldez/exptostd v0.3.1 // indirect github.com/ldez/gomoddirectives v0.6.0 // indirect github.com/ldez/grignotin v0.7.0 // indirect github.com/ldez/tagliatelle v0.7.1 // indirect - github.com/ldez/usetesting v0.4.1 // indirect + github.com/ldez/usetesting v0.4.2 // indirect github.com/leonklingele/grouper v1.1.2 // indirect github.com/macabu/inamedparam v0.1.3 // indirect github.com/magiconair/properties v1.8.7 // indirect diff --git a/go.sum b/go.sum index 40d35c13cc..7265ee45f8 100644 --- a/go.sum +++ b/go.sum @@ -708,8 +708,8 @@ github.com/ashanbrown/forbidigo v1.6.0 h1:D3aewfM37Yb3pxHujIPSpTf6oQk9sc9WZi8ger github.com/ashanbrown/forbidigo v1.6.0/go.mod h1:Y8j9jy9ZYAEHXdu723cUlraTqbzjKF1MUyfOKL+AjcU= github.com/ashanbrown/makezero v1.2.0 h1:/2Lp1bypdmK9wDIq7uWBlDF1iMUpIIS4A+pF6C9IEUU= github.com/ashanbrown/makezero v1.2.0/go.mod h1:dxlPhHbDMC6N6xICzFBSK+4njQDdK8euNO0qjQMtGY4= -github.com/authzed/authzed-go v1.2.0 h1:Ep1sRJMxcArB++kYqHbYKQCb/GgdGZI0cW4gZrJ1K40= -github.com/authzed/authzed-go v1.2.0/go.mod h1:4lkFxvaCISG1roRdnUt35/Sk1StVuMD1QCwTd/BcWcM= +github.com/authzed/authzed-go v1.2.2-0.20250107172318-7fd4159ab2b7 h1:f99Iz0FoEyqPelojk1kWWgSDVJtDEMrLb+6qVdohoqs= +github.com/authzed/authzed-go v1.2.2-0.20250107172318-7fd4159ab2b7/go.mod h1:/+NblSrzA6Lm6vUO3fqZyLh8MDCLUQq2AyJMlHb32DE= github.com/authzed/cel-go v0.20.2 h1:GlmLecGry7Z8HU0k+hmaHHUV05ZHrsFxduXHtIePvck= github.com/authzed/cel-go v0.20.2/go.mod h1:pJHVFWbqUHV1J+klQoZubdKswlbxcsbojda3mye9kiU= github.com/authzed/consistent v0.1.0 h1:tlh1wvKoRbjRhMm2P+X5WQQyR54SRoS4MyjLOg17Mp8= @@ -1059,12 +1059,10 @@ github.com/golangci/go-printf-func-name v0.1.0 h1:dVokQP+NMTO7jwO4bwsRwLWeudOVUP github.com/golangci/go-printf-func-name v0.1.0/go.mod h1:wqhWFH5mUdJQhweRnldEywnR5021wTdZSNgwYceV14s= github.com/golangci/gofmt v0.0.0-20241223200906-057b0627d9b9 h1:t5wybL6RtO83VwoMOb7U/Peqe3gGKQlPIC66wXmnkvM= github.com/golangci/gofmt v0.0.0-20241223200906-057b0627d9b9/go.mod h1:Ag3L7sh7E28qAp/5xnpMMTuGYqxLZoSaEHZDkZB1RgU= -github.com/golangci/golangci-lint v1.63.0 h1:4HZnKnU9nA08TQpzpl5Ngrr79B/tEvv0UJVZ92K0k1k= -github.com/golangci/golangci-lint v1.63.0/go.mod h1:yZsr57ibOW/1CAtgo/R4+H0QqDmLZtRSCOvyc8QBHf8= +github.com/golangci/golangci-lint v1.63.4 h1:bJQFQ3hSfUto597dkL7ipDzOxsGEpiWdLiZ359OWOBI= +github.com/golangci/golangci-lint v1.63.4/go.mod h1:Hx0B7Lg5/NXbaOHem8+KU+ZUIzMI6zNj/7tFwdnn10I= github.com/golangci/misspell v0.6.0 h1:JCle2HUTNWirNlDIAUO44hUsKhOFqGPoC4LZxlaSXDs= github.com/golangci/misspell v0.6.0/go.mod h1:keMNyY6R9isGaSAu+4Q8NMBwMPkh15Gtc8UCVoDtAWo= -github.com/golangci/modinfo v0.3.4 h1:oU5huX3fbxqQXdfspamej74DFX0kyGLkw1ppvXoJ8GA= -github.com/golangci/modinfo v0.3.4/go.mod h1:wytF1M5xl9u0ij8YSvhkEVPP3M5Mc7XLl1pxH3B2aUM= github.com/golangci/plugin-module-register v0.1.1 h1:TCmesur25LnyJkpsVrupv1Cdzo+2f7zX0H6Jkw1Ol6c= github.com/golangci/plugin-module-register v0.1.1/go.mod h1:TTpqoB6KkwOJMV8u7+NyXMrkwwESJLOkfl9TxR1DGFc= github.com/golangci/revgrep v0.5.3 h1:3tL7c1XBMtWHHqVpS5ChmiAAoe4PF/d5+ULzV9sLAzs= @@ -1181,8 +1179,8 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDa github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0 h1:kQ0NI7W1B3HwiN5gAYtY+XFItDPbLBwYRxAqbFTyDes= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0/go.mod h1:zrT2dxOAjNFPRGjTUe2Xmb4q4YdUwVvQFV6xiCSf+z0= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= @@ -1315,16 +1313,16 @@ github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhR github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= github.com/lasiar/canonicalheader v1.1.2 h1:vZ5uqwvDbyJCnMhmFYimgMZnJMjwljN5VGY0VKbMXb4= github.com/lasiar/canonicalheader v1.1.2/go.mod h1:qJCeLFS0G/QlLQ506T+Fk/fWMa2VmBUiEI2cuMK4djI= -github.com/ldez/exptostd v0.3.0 h1:iKdMtUedzov89jDvuwmo0qpo+ARpZJg9hMp3428WwNg= -github.com/ldez/exptostd v0.3.0/go.mod h1:iZBRYaUmcW5jwCR3KROEZ1KivQQp6PHXbDPk9hqJKCQ= +github.com/ldez/exptostd v0.3.1 h1:90yWWoAKMFHeovTK8uzBms9Ppp8Du/xQ20DRO26Ymrw= +github.com/ldez/exptostd v0.3.1/go.mod h1:iZBRYaUmcW5jwCR3KROEZ1KivQQp6PHXbDPk9hqJKCQ= github.com/ldez/gomoddirectives v0.6.0 h1:Jyf1ZdTeiIB4dd+2n4qw+g4aI9IJ6JyfOZ8BityWvnA= github.com/ldez/gomoddirectives v0.6.0/go.mod h1:TuwOGYoPAoENDWQpe8DMqEm5nIfjrxZXmxX/CExWyZ4= github.com/ldez/grignotin v0.7.0 h1:vh0dI32WhHaq6LLPZ38g7WxXuZ1+RzyrJ7iPG9JMa8c= github.com/ldez/grignotin v0.7.0/go.mod h1:uaVTr0SoZ1KBii33c47O1M8Jp3OP3YDwhZCmzT9GHEk= github.com/ldez/tagliatelle v0.7.1 h1:bTgKjjc2sQcsgPiT902+aadvMjCeMHrY7ly2XKFORIk= github.com/ldez/tagliatelle v0.7.1/go.mod h1:3zjxUpsNB2aEZScWiZTHrAXOl1x25t3cRmzfK1mlo2I= -github.com/ldez/usetesting v0.4.1 h1:T/4Bk3YDX6XUBtdNDDFymlr5GBekKA4j7HUtrv1YaaI= -github.com/ldez/usetesting v0.4.1/go.mod h1:eEs46T3PpQ+9RgN9VjpY6qWdiw2/QmfiDeWmdZdrjIQ= +github.com/ldez/usetesting v0.4.2 h1:J2WwbrFGk3wx4cZwSMiCQQ00kjGR0+tuuyW0Lqm4lwA= +github.com/ldez/usetesting v0.4.2/go.mod h1:eEs46T3PpQ+9RgN9VjpY6qWdiw2/QmfiDeWmdZdrjIQ= github.com/leonklingele/grouper v1.1.2 h1:o1ARBDLOmmasUaNDesWqWCIFH3u7hoFlM84YrjT3mIY= github.com/leonklingele/grouper v1.1.2/go.mod h1:6D0M/HVkhs2yRKRFZUoGjeDy7EZTfFBE9gl4kjmIGkA= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= @@ -2261,8 +2259,8 @@ google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/ google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0= google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= -google.golang.org/api v0.214.0 h1:h2Gkq07OYi6kusGOaT/9rnNljuXmqPnaig7WGPmKbwA= -google.golang.org/api v0.214.0/go.mod h1:bYPpLG8AyeMWwDU6NXoB00xC0DFkikVvd5MfwoxjLqE= +google.golang.org/api v0.209.0 h1:Ja2OXNlyRlWCWu8o+GgI4yUn/wz9h/5ZfFbKz+dQX+w= +google.golang.org/api v0.209.0/go.mod h1:I53S168Yr/PNDNMi5yPnDc0/LGRZO6o7PoEbl/HY3CM= 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/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= diff --git a/internal/dispatch/graph/graph.go b/internal/dispatch/graph/graph.go index 94d7838530..ab6fe66db1 100644 --- a/internal/dispatch/graph/graph.go +++ b/internal/dispatch/graph/graph.go @@ -17,6 +17,7 @@ import ( log "github.com/authzed/spicedb/internal/logging" datastoremw "github.com/authzed/spicedb/internal/middleware/datastore" "github.com/authzed/spicedb/pkg/datastore" + "github.com/authzed/spicedb/pkg/middleware/nodeid" core "github.com/authzed/spicedb/pkg/proto/core/v1" v1 "github.com/authzed/spicedb/pkg/proto/dispatch/v1" "github.com/authzed/spicedb/pkg/tuple" @@ -177,10 +178,17 @@ func (ld *localDispatcher) lookupRelation(_ context.Context, ns *core.NamespaceD func (ld *localDispatcher) DispatchCheck(ctx context.Context, req *v1.DispatchCheckRequest) (*v1.DispatchCheckResponse, error) { resourceType := tuple.StringCoreRR(req.ResourceRelation) spanName := "DispatchCheck → " + resourceType + "@" + req.Subject.Namespace + "#" + req.Subject.Relation + + nodeID, err := nodeid.FromContext(ctx) + if err != nil { + log.Err(err).Msg("failed to get node ID") + } + ctx, span := tracer.Start(ctx, spanName, trace.WithAttributes( attribute.String("resource-type", resourceType), attribute.StringSlice("resource-ids", req.ResourceIds), attribute.String("subject", tuple.StringCoreONR(req.Subject)), + attribute.String("node-id", nodeID), )) defer span.End() @@ -261,8 +269,14 @@ func (ld *localDispatcher) DispatchCheck(ctx context.Context, req *v1.DispatchCh // DispatchExpand implements dispatch.Expand interface func (ld *localDispatcher) DispatchExpand(ctx context.Context, req *v1.DispatchExpandRequest) (*v1.DispatchExpandResponse, error) { + nodeID, err := nodeid.FromContext(ctx) + if err != nil { + log.Err(err).Msg("failed to get node ID") + } + ctx, span := tracer.Start(ctx, "DispatchExpand", trace.WithAttributes( attribute.String("start", tuple.StringCoreONR(req.ResourceAndRelation)), + attribute.String("node-id", nodeID), )) defer span.End() @@ -296,6 +310,11 @@ func (ld *localDispatcher) DispatchReachableResources( req *v1.DispatchReachableResourcesRequest, stream dispatch.ReachableResourcesStream, ) error { + nodeID, err := nodeid.FromContext(stream.Context()) + if err != nil { + log.Err(err).Msg("failed to get node ID") + } + resourceType := tuple.StringCoreRR(req.ResourceRelation) subjectRelation := tuple.StringCoreRR(req.SubjectRelation) spanName := "DispatchReachableResources → " + resourceType + "@" + subjectRelation @@ -303,6 +322,7 @@ func (ld *localDispatcher) DispatchReachableResources( attribute.String("resource-type", resourceType), attribute.String("subject-type", subjectRelation), attribute.StringSlice("subject-ids", req.SubjectIds), + attribute.String("node-id", nodeID), )) defer span.End() @@ -329,9 +349,15 @@ func (ld *localDispatcher) DispatchLookupResources( req *v1.DispatchLookupResourcesRequest, stream dispatch.LookupResourcesStream, ) error { + nodeID, err := nodeid.FromContext(stream.Context()) + if err != nil { + log.Err(err).Msg("failed to get node ID") + } + ctx, span := tracer.Start(stream.Context(), "DispatchLookupResources", trace.WithAttributes( attribute.String("resource-type", tuple.StringCoreRR(req.ObjectRelation)), attribute.String("subject", tuple.StringCoreONR(req.Subject)), + attribute.String("node-id", nodeID), )) defer span.End() @@ -357,11 +383,17 @@ func (ld *localDispatcher) DispatchLookupResources2( req *v1.DispatchLookupResources2Request, stream dispatch.LookupResources2Stream, ) error { + nodeID, err := nodeid.FromContext(stream.Context()) + if err != nil { + log.Err(err).Msg("failed to get node ID") + } + ctx, span := tracer.Start(stream.Context(), "DispatchLookupResources2", trace.WithAttributes( attribute.String("resource-type", tuple.StringCoreRR(req.ResourceRelation)), attribute.String("subject-type", tuple.StringCoreRR(req.SubjectRelation)), attribute.StringSlice("subject-ids", req.SubjectIds), attribute.String("terminal-subject", tuple.StringCoreONR(req.TerminalSubject)), + attribute.String("node-id", nodeID), )) defer span.End() @@ -388,6 +420,11 @@ func (ld *localDispatcher) DispatchLookupSubjects( req *v1.DispatchLookupSubjectsRequest, stream dispatch.LookupSubjectsStream, ) error { + nodeID, err := nodeid.FromContext(stream.Context()) + if err != nil { + log.Err(err).Msg("failed to get node ID") + } + resourceType := tuple.StringCoreRR(req.ResourceRelation) subjectRelation := tuple.StringCoreRR(req.SubjectRelation) spanName := "DispatchLookupSubjects → " + resourceType + "@" + subjectRelation @@ -396,6 +433,7 @@ func (ld *localDispatcher) DispatchLookupSubjects( attribute.String("resource-type", resourceType), attribute.String("subject-type", subjectRelation), attribute.StringSlice("resource-ids", req.ResourceIds), + attribute.String("node-id", nodeID), )) defer span.End() diff --git a/internal/graph/check.go b/internal/graph/check.go index b4bf4f614d..a4d6906b41 100644 --- a/internal/graph/check.go +++ b/internal/graph/check.go @@ -5,6 +5,7 @@ import ( "errors" "time" + "github.com/google/uuid" "github.com/prometheus/client_golang/prometheus" "github.com/samber/lo" "go.opentelemetry.io/otel" @@ -19,6 +20,7 @@ import ( "github.com/authzed/spicedb/internal/taskrunner" "github.com/authzed/spicedb/pkg/datastore" "github.com/authzed/spicedb/pkg/genutil/mapz" + "github.com/authzed/spicedb/pkg/middleware/nodeid" nspkg "github.com/authzed/spicedb/pkg/namespace" core "github.com/authzed/spicedb/pkg/proto/core/v1" v1 "github.com/authzed/spicedb/pkg/proto/dispatch/v1" @@ -112,12 +114,24 @@ func (cc *ConcurrentChecker) Check(ctx context.Context, req ValidatedCheckReques return resolved.Resp, resolved.Err } + nodeID, err := nodeid.FromContext(ctx) + if err != nil { + // NOTE: we ignore this error here as if the node ID is missing, the debug + // trace is still valid. + log.Err(err).Msg("failed to get node ID") + } + // Add debug information if requested. debugInfo := resolved.Resp.Metadata.DebugInfo if debugInfo == nil { debugInfo = &v1.DebugInformation{ - Check: &v1.CheckDebugTrace{}, + Check: &v1.CheckDebugTrace{ + TraceId: uuid.NewString(), + SourceId: nodeID, + }, } + } else if debugInfo.Check != nil && debugInfo.Check.SourceId == "" { + debugInfo.Check.SourceId = nodeID } // Remove the traversal bloom from the debug request to save some data over the @@ -1194,7 +1208,9 @@ func combineResponseMetadata(existing *v1.ResponseMeta, responseMetadata *v1.Res } debugInfo := &v1.DebugInformation{ - Check: &v1.CheckDebugTrace{}, + Check: &v1.CheckDebugTrace{ + TraceId: uuid.NewString(), + }, } if existing.DebugInfo != nil { diff --git a/internal/graph/checkingresourcestream.go b/internal/graph/checkingresourcestream.go index 86905532ef..cb36d4f6b9 100644 --- a/internal/graph/checkingresourcestream.go +++ b/internal/graph/checkingresourcestream.go @@ -458,7 +458,7 @@ func (crs *checkingResourceStream) runProcess(alwaysProcess bool) (bool, error) } // Issue the bulk check over all the resources. - results, checkResultMetadata, err := computed.ComputeBulkCheck( + results, checkResultMetadata, _, err := computed.ComputeBulkCheck( crs.ctx, crs.checker, computed.CheckParameters{ diff --git a/internal/graph/computed/computecheck.go b/internal/graph/computed/computecheck.go index 44c4b4b738..ed02230563 100644 --- a/internal/graph/computed/computecheck.go +++ b/internal/graph/computed/computecheck.go @@ -58,10 +58,15 @@ func ComputeCheck( resourceID string, dispatchChunkSize uint16, ) (*v1.ResourceCheckResult, *v1.ResponseMeta, error) { - resultsMap, meta, err := computeCheck(ctx, d, params, []string{resourceID}, dispatchChunkSize) + resultsMap, meta, di, err := computeCheck(ctx, d, params, []string{resourceID}, dispatchChunkSize) if err != nil { return nil, meta, err } + + spiceerrors.DebugAssert(func() bool { + return (len(di) == 0 && meta.DebugInfo == nil) || (len(di) == 1 && meta.DebugInfo != nil) + }, "mismatch in debug information returned from computeCheck") + return resultsMap[resourceID], meta, err } @@ -73,7 +78,7 @@ func ComputeBulkCheck( params CheckParameters, resourceIDs []string, dispatchChunkSize uint16, -) (map[string]*v1.ResourceCheckResult, *v1.ResponseMeta, error) { +) (map[string]*v1.ResourceCheckResult, *v1.ResponseMeta, []*v1.DebugInformation, error) { return computeCheck(ctx, d, params, resourceIDs, dispatchChunkSize) } @@ -82,18 +87,12 @@ func computeCheck(ctx context.Context, params CheckParameters, resourceIDs []string, dispatchChunkSize uint16, -) (map[string]*v1.ResourceCheckResult, *v1.ResponseMeta, error) { +) (map[string]*v1.ResourceCheckResult, *v1.ResponseMeta, []*v1.DebugInformation, error) { debugging := v1.DispatchCheckRequest_NO_DEBUG if params.DebugOption == BasicDebuggingEnabled { debugging = v1.DispatchCheckRequest_ENABLE_BASIC_DEBUGGING - if len(resourceIDs) > 1 { - return nil, nil, spiceerrors.MustBugf("debugging can only be enabled for a single resource ID") - } } else if params.DebugOption == TraceDebuggingEnabled { debugging = v1.DispatchCheckRequest_ENABLE_TRACE_DEBUGGING - if len(resourceIDs) > 1 { - return nil, nil, spiceerrors.MustBugf("debugging can only be enabled for a single resource ID") - } } setting := v1.DispatchCheckRequest_REQUIRE_ALL_RESULTS @@ -107,12 +106,13 @@ func computeCheck(ctx context.Context, bf, err := v1.NewTraversalBloomFilter(uint(params.MaximumDepth)) if err != nil { - return nil, nil, spiceerrors.MustBugf("failed to create new traversal bloom filter") + return nil, nil, nil, spiceerrors.MustBugf("failed to create new traversal bloom filter") } caveatRunner := cexpr.NewCaveatRunner() // TODO(jschorr): Should we make this run in parallel via the preloadedTaskRunner? + debugInfo := make([]*v1.DebugInformation, 0) _, err = slicez.ForEachChunkUntil(resourceIDs, dispatchChunkSize, func(resourceIDsToCheck []string) (bool, error) { checkResult, err := d.DispatchCheck(ctx, &v1.DispatchCheckRequest{ ResourceRelation: params.ResourceType.ToCoreRR(), @@ -128,6 +128,10 @@ func computeCheck(ctx context.Context, CheckHints: params.CheckHints, }) + if checkResult.Metadata.DebugInfo != nil { + debugInfo = append(debugInfo, checkResult.Metadata.DebugInfo) + } + if len(resourceIDs) == 1 { metadata = checkResult.Metadata } else { @@ -135,6 +139,7 @@ func computeCheck(ctx context.Context, DispatchCount: metadata.DispatchCount + checkResult.Metadata.DispatchCount, DepthRequired: max(metadata.DepthRequired, checkResult.Metadata.DepthRequired), CachedDispatchCount: metadata.CachedDispatchCount + checkResult.Metadata.CachedDispatchCount, + DebugInfo: nil, } } @@ -152,7 +157,7 @@ func computeCheck(ctx context.Context, return true, nil }) - return results, metadata, err + return results, metadata, debugInfo, err } func computeCaveatedCheckResult(ctx context.Context, runner *cexpr.CaveatRunner, params CheckParameters, resourceID string, checkResult *v1.DispatchCheckResponse) (*v1.ResourceCheckResult, error) { @@ -179,6 +184,7 @@ func computeCaveatedCheckResult(ctx context.Context, runner *cexpr.CaveatRunner, missingFields, _ := caveatResult.MissingVarNames() return &v1.ResourceCheckResult{ Membership: v1.ResourceCheckResult_CAVEATED_MEMBER, + Expression: result.Expression, MissingExprFields: missingFields, }, nil } diff --git a/internal/graph/computed/computecheck_test.go b/internal/graph/computed/computecheck_test.go index 1a2f4c430b..c00102fc34 100644 --- a/internal/graph/computed/computecheck_test.go +++ b/internal/graph/computed/computecheck_test.go @@ -908,7 +908,7 @@ func TestComputeBulkCheck(t *testing.T) { }) require.NoError(t, err) - resp, _, err := computed.ComputeBulkCheck(ctx, dispatch, + resp, _, _, err := computed.ComputeBulkCheck(ctx, dispatch, computed.CheckParameters{ ResourceType: tuple.RR("document", "view"), Subject: tuple.ONR("user", "tom", "..."), diff --git a/internal/graph/lookupresources2.go b/internal/graph/lookupresources2.go index e1fca79560..5ff7642352 100644 --- a/internal/graph/lookupresources2.go +++ b/internal/graph/lookupresources2.go @@ -541,7 +541,7 @@ func (crr *CursoredLookupResources2) redispatchOrReport( checkHints = append(checkHints, checkHint) } - resultsByResourceID, checkMetadata, err := computed.ComputeBulkCheck(ctx, crr.dc, computed.CheckParameters{ + resultsByResourceID, checkMetadata, _, err := computed.ComputeBulkCheck(ctx, crr.dc, computed.CheckParameters{ ResourceType: tuple.FromCoreRelationReference(parentRequest.ResourceRelation), Subject: tuple.FromCoreObjectAndRelation(parentRequest.TerminalSubject), CaveatContext: parentRequest.Context.AsMap(), diff --git a/internal/graph/lr2streams.go b/internal/graph/lr2streams.go index d03bb2d0dd..808be13c9f 100644 --- a/internal/graph/lr2streams.go +++ b/internal/graph/lr2streams.go @@ -128,7 +128,7 @@ func (rdc *checkAndDispatchRunner) runChecker(ctx context.Context, startingIndex // NOTE: we are checking the containing permission here, *not* the target relation, as // the goal is to shear for the containing permission. - resultsByResourceID, checkMetadata, err := computed.ComputeBulkCheck(ctx, rdc.checkDispatcher, computed.CheckParameters{ + resultsByResourceID, checkMetadata, _, err := computed.ComputeBulkCheck(ctx, rdc.checkDispatcher, computed.CheckParameters{ ResourceType: tuple.FromCoreRelationReference(rdc.newSubjectType), Subject: tuple.FromCoreObjectAndRelation(rdc.parentRequest.TerminalSubject), CaveatContext: rdc.parentRequest.Context.AsMap(), diff --git a/internal/services/v1/bulkcheck.go b/internal/services/v1/bulkcheck.go index 7b3193071b..f14fd5909c 100644 --- a/internal/services/v1/bulkcheck.go +++ b/internal/services/v1/bulkcheck.go @@ -2,11 +2,15 @@ package v1 import ( "context" + "slices" "sync" + "time" v1 "github.com/authzed/authzed-go/proto/authzed/api/v1" + "github.com/google/uuid" "github.com/jzelinskie/stringz" "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/durationpb" "github.com/authzed/spicedb/internal/dispatch" "github.com/authzed/spicedb/internal/graph" @@ -69,6 +73,7 @@ func (bc *bulkChecker) checkBulkPermissions(ctx context.Context, req *v1.CheckBu atRevision: atRevision, maxCaveatContextSize: bc.maxCaveatContextSize, maximumAPIDepth: bc.maxAPIDepth, + withTracing: req.WithTracing, }, req.Items) if err != nil { return nil, err @@ -138,11 +143,75 @@ func (bc *bulkChecker) checkBulkPermissions(ctx context.Context, req *v1.CheckBu return nil } - appendResultsForCheck := func(params *computed.CheckParameters, resourceIDs []string, metadata *dispatchv1.ResponseMeta, results map[string]*dispatchv1.ResourceCheckResult) error { + appendResultsForCheck := func( + params *computed.CheckParameters, + resourceIDs []string, + metadata *dispatchv1.ResponseMeta, + debugInfos []*dispatchv1.DebugInformation, + results map[string]*dispatchv1.ResourceCheckResult, + ) error { bulkResponseMutex.Lock() defer bulkResponseMutex.Unlock() + ds := datastoremw.MustFromContext(ctx).SnapshotReader(atRevision) + + schemaText := "" + if len(debugInfos) > 0 { + schema, err := getFullSchema(ctx, ds) + if err != nil { + return err + } + schemaText = schema + } + for _, resourceID := range resourceIDs { + var debugTrace *v1.DebugInformation + if len(debugInfos) > 0 { + // Find the debug info that matches the resource ID. + var debugInfo *dispatchv1.DebugInformation + for _, di := range debugInfos { + if slices.Contains(di.Check.Request.ResourceIds, resourceID) { + debugInfo = di + break + } + } + + if debugInfo != nil { + // Synthesize a new debug information with a trace "wrapping" the (potentially batched) + // trace. + localResults := make(map[string]*dispatchv1.ResourceCheckResult, 1) + if result, ok := results[resourceID]; ok { + localResults[resourceID] = result + } + wrappedDebugInfo := &dispatchv1.DebugInformation{ + Check: &dispatchv1.CheckDebugTrace{ + Request: &dispatchv1.DispatchCheckRequest{ + ResourceRelation: debugInfo.Check.Request.ResourceRelation, + ResourceIds: []string{resourceID}, + Subject: debugInfo.Check.Request.Subject, + ResultsSetting: debugInfo.Check.Request.ResultsSetting, + Debug: debugInfo.Check.Request.Debug, + }, + ResourceRelationType: debugInfo.Check.ResourceRelationType, + IsCachedResult: false, + SubProblems: []*dispatchv1.CheckDebugTrace{ + debugInfo.Check, + }, + Results: localResults, + Duration: durationpb.New(time.Duration(0)), + TraceId: uuid.New().String(), + }, + } + + // Convert to debug information. + dt, err := convertCheckDispatchDebugInformationWithSchema(ctx, params.CaveatContext, wrappedDebugInfo, ds, schemaText) + if err != nil { + return err + } + debugTrace = dt + } + } + reqItem, err := requestItemFromResourceAndParameters(params, resourceID) if err != nil { return err @@ -150,7 +219,7 @@ func (bc *bulkChecker) checkBulkPermissions(ctx context.Context, req *v1.CheckBu if err := addPair(&v1.CheckBulkPermissionsPair{ Request: reqItem, - Response: pairItemFromCheckResult(results[resourceID]), + Response: pairItemFromCheckResult(results[resourceID], debugTrace), }); err != nil { return err } @@ -187,12 +256,12 @@ func (bc *bulkChecker) checkBulkPermissions(ctx context.Context, req *v1.CheckBu } // Call bulk check to compute the check result(s) for the resource ID(s). - rcr, metadata, err := computed.ComputeBulkCheck(ctx, bc.dispatch, *group.params, resourceIDs, bc.dispatchChunkSize) + rcr, metadata, debugInfos, err := computed.ComputeBulkCheck(ctx, bc.dispatch, *group.params, resourceIDs, bc.dispatchChunkSize) if err != nil { return appendResultsForError(group.params, resourceIDs, err) } - return appendResultsForCheck(group.params, resourceIDs, metadata, rcr) + return appendResultsForCheck(group.params, resourceIDs, metadata, debugInfos, rcr) }) }) } diff --git a/internal/services/v1/debug.go b/internal/services/v1/debug.go index 156d9bdea9..a20763dde2 100644 --- a/internal/services/v1/debug.go +++ b/internal/services/v1/debug.go @@ -13,6 +13,7 @@ import ( dispatch "github.com/authzed/spicedb/pkg/proto/dispatch/v1" "github.com/authzed/spicedb/pkg/schemadsl/compiler" "github.com/authzed/spicedb/pkg/schemadsl/generator" + "github.com/authzed/spicedb/pkg/spiceerrors" "github.com/authzed/spicedb/pkg/tuple" ) @@ -28,14 +29,24 @@ func ConvertCheckDispatchDebugInformation( return nil, nil } - caveats, err := reader.ListAllCaveats(ctx) + schema, err := getFullSchema(ctx, reader) if err != nil { return nil, err } + return convertCheckDispatchDebugInformationWithSchema(ctx, caveatContext, debugInfo, reader, schema) +} + +// getFullSchema returns the full schema from the reader. +func getFullSchema(ctx context.Context, reader datastore.Reader) (string, error) { + caveats, err := reader.ListAllCaveats(ctx) + if err != nil { + return "", err + } + namespaces, err := reader.ListAllNamespaces(ctx) if err != nil { - return nil, err + return "", err } defs := make([]compiler.SchemaDefinition, 0, len(namespaces)+len(caveats)) @@ -48,9 +59,19 @@ func ConvertCheckDispatchDebugInformation( schema, _, err := generator.GenerateSchema(defs) if err != nil { - return nil, err + return "", err } + return schema, nil +} + +func convertCheckDispatchDebugInformationWithSchema( + ctx context.Context, + caveatContext map[string]any, + debugInfo *dispatch.DebugInformation, + reader datastore.Reader, + schema string, +) (*v1.DebugInformation, error) { converted, err := convertCheckTrace(ctx, caveatContext, debugInfo.Check, reader) if err != nil { return nil, err @@ -90,8 +111,15 @@ func convertCheckTrace(ctx context.Context, caveatContext map[string]any, ct *di } var caveatEvalInfo *v1.CaveatEvalInfo - if permissionship == v1.CheckDebugTrace_PERMISSIONSHIP_CONDITIONAL_PERMISSION && len(partialResults) == 1 { + + // NOTE: Bulk check gives the *fully resolved* results, rather than the result pre-caveat + // evaluation. In that case, we skip re-evaluating here. + // TODO(jschorr): Add support for evaluating *each* result distinctly. + if permissionship == v1.CheckDebugTrace_PERMISSIONSHIP_CONDITIONAL_PERMISSION && len(partialResults) == 1 && + len(partialResults[0].MissingExprFields) == 0 { partialCheckResult := partialResults[0] + spiceerrors.DebugAssertNotNil(partialCheckResult.Expression, "got nil caveat expression") + computedResult, err := cexpr.RunSingleCaveatExpression(ctx, partialCheckResult.Expression, caveatContext, reader, cexpr.RunCaveatExpressionWithDebugInformation) if err != nil { return nil, err @@ -128,6 +156,17 @@ func convertCheckTrace(ctx context.Context, caveatContext map[string]any, ct *di } } + // If there is more than a single result, mark the overall permissionship + // as unspecified if *all* results needed to be true and at least one is not. + if len(ct.Request.ResourceIds) > 1 && ct.Request.ResultsSetting == dispatch.DispatchCheckRequest_REQUIRE_ALL_RESULTS { + for _, resourceID := range ct.Request.ResourceIds { + if result, ok := ct.Results[resourceID]; !ok || result.Membership != dispatch.ResourceCheckResult_MEMBER { + permissionship = v1.CheckDebugTrace_PERMISSIONSHIP_UNSPECIFIED + break + } + } + } + if len(ct.SubProblems) > 0 { subProblems := make([]*v1.CheckDebugTrace, 0, len(ct.SubProblems)) for _, subProblem := range ct.SubProblems { @@ -144,6 +183,7 @@ func convertCheckTrace(ctx context.Context, caveatContext map[string]any, ct *di }) return &v1.CheckDebugTrace{ + TraceOperationId: ct.TraceId, Resource: &v1.ObjectReference{ ObjectType: ct.Request.ResourceRelation.Namespace, ObjectId: strings.Join(ct.Request.ResourceIds, ","), @@ -165,10 +205,12 @@ func convertCheckTrace(ctx context.Context, caveatContext map[string]any, ct *di }, }, Duration: ct.Duration, + Source: ct.SourceId, }, nil } return &v1.CheckDebugTrace{ + TraceOperationId: ct.TraceId, Resource: &v1.ObjectReference{ ObjectType: ct.Request.ResourceRelation.Namespace, ObjectId: strings.Join(ct.Request.ResourceIds, ","), @@ -188,5 +230,6 @@ func convertCheckTrace(ctx context.Context, caveatContext map[string]any, ct *di WasCachedResult: ct.IsCachedResult, }, Duration: ct.Duration, + Source: ct.SourceId, }, nil } diff --git a/internal/services/v1/debug_test.go b/internal/services/v1/debug_test.go index 003b9a39ea..3cf6df6581 100644 --- a/internal/services/v1/debug_test.go +++ b/internal/services/v1/debug_test.go @@ -2,6 +2,7 @@ package v1_test import ( "context" + "fmt" "sort" "strings" "testing" @@ -546,3 +547,381 @@ func TestCheckPermissionWithDebug(t *testing.T) { }) } } + +type bulkCheckItem struct { + toCheck string + rda []rda +} + +type frameInfo struct { + resourceType string + resourceIDs []string + permission string + permissionship v1.CheckDebugTrace_Permissionship +} + +func expectOrderedFrames(frames ...frameInfo) rda { + return func(req *require.Assertions, debugInfo *v1.DebugInformation) { + expectFrames(req, frames, debugInfo.Check) + } +} + +func expectFrames(req *require.Assertions, frames []frameInfo, check *v1.CheckDebugTrace) { + if len(frames) == 0 { + return + } + + frame := frames[0] + req.Equal(frame.resourceType, check.Resource.ObjectType) + req.Equal(frame.resourceIDs, strings.Split(check.Resource.ObjectId, ",")) + req.Equal(frame.permission, check.Permission) + req.Equal(frame.permissionship, check.Result) + + remainingFrames := frames[1:] + if len(remainingFrames) > 0 { + req.NotNil(check.GetSubProblems(), "expected subproblems") + expectFrames(req, remainingFrames, check.GetSubProblems().Traces[0]) + } +} + +func TestBulkCheckPermissionWithDebug(t *testing.T) { + tcs := []struct { + name string + schema string + relationships []tuple.Relationship + toTest []bulkCheckItem + }{ + { + "basic batching", + `definition user {} + + definition document { + relation viewer: user + permission view = viewer + nil + } + `, + []tuple.Relationship{ + tuple.MustParse("document:first#viewer@user:tom"), + tuple.MustParse("document:second#viewer@user:tom"), + }, + []bulkCheckItem{ + { + toCheck: "document:first#view@user:tom", + rda: []rda{ + expectOrderedFrames( + frameInfo{ + resourceType: "document", + resourceIDs: []string{"first"}, + permission: "view", + permissionship: v1.CheckDebugTrace_PERMISSIONSHIP_HAS_PERMISSION, + }, + frameInfo{ + resourceType: "document", + resourceIDs: []string{"first", "second"}, + permission: "view", + permissionship: v1.CheckDebugTrace_PERMISSIONSHIP_HAS_PERMISSION, + }, + frameInfo{ + resourceType: "document", + resourceIDs: []string{"first", "second"}, + permission: "viewer", + permissionship: v1.CheckDebugTrace_PERMISSIONSHIP_HAS_PERMISSION, + }, + ), + }, + }, + { + toCheck: "document:second#view@user:tom", + rda: []rda{ + expectOrderedFrames( + frameInfo{ + resourceType: "document", + resourceIDs: []string{"second"}, + permission: "view", + permissionship: v1.CheckDebugTrace_PERMISSIONSHIP_HAS_PERMISSION, + }, + frameInfo{ + resourceType: "document", + resourceIDs: []string{"first", "second"}, + permission: "view", + permissionship: v1.CheckDebugTrace_PERMISSIONSHIP_HAS_PERMISSION, + }, + frameInfo{ + resourceType: "document", + resourceIDs: []string{"first", "second"}, + permission: "viewer", + permissionship: v1.CheckDebugTrace_PERMISSIONSHIP_HAS_PERMISSION, + }, + ), + }, + }, + }, + }, + { + "batching with positive and negative", + `definition user {} + + definition document { + relation viewer: user + permission view = viewer + nil + } + `, + []tuple.Relationship{ + tuple.MustParse("document:first#viewer@user:tom"), + }, + []bulkCheckItem{ + { + toCheck: "document:first#view@user:tom", + rda: []rda{ + expectOrderedFrames( + frameInfo{ + resourceType: "document", + resourceIDs: []string{"first"}, + permission: "view", + permissionship: v1.CheckDebugTrace_PERMISSIONSHIP_HAS_PERMISSION, + }, + frameInfo{ + resourceType: "document", + resourceIDs: []string{"first", "second"}, + permission: "view", + permissionship: v1.CheckDebugTrace_PERMISSIONSHIP_UNSPECIFIED, + }, + frameInfo{ + resourceType: "document", + resourceIDs: []string{"first", "second"}, + permission: "viewer", + permissionship: v1.CheckDebugTrace_PERMISSIONSHIP_UNSPECIFIED, + }, + ), + }, + }, + { + toCheck: "document:second#view@user:tom", + rda: []rda{ + expectOrderedFrames( + frameInfo{ + resourceType: "document", + resourceIDs: []string{"second"}, + permission: "view", + permissionship: v1.CheckDebugTrace_PERMISSIONSHIP_NO_PERMISSION, + }, + frameInfo{ + resourceType: "document", + resourceIDs: []string{"first", "second"}, + permission: "view", + permissionship: v1.CheckDebugTrace_PERMISSIONSHIP_UNSPECIFIED, + }, + frameInfo{ + resourceType: "document", + resourceIDs: []string{"first", "second"}, + permission: "viewer", + permissionship: v1.CheckDebugTrace_PERMISSIONSHIP_UNSPECIFIED, + }, + ), + }, + }, + }, + }, + { + "multiple batching within the same request", + `definition user {} + + definition document { + relation viewer: user + permission view = viewer + nil + } + `, + []tuple.Relationship{ + tuple.MustParse("document:first#viewer@user:tom"), + }, + (func() []bulkCheckItem { + // 100 IDs is the default batch size, so 99 + `first`. + docsSlice := make([]string, 0, 99) + docsSlice = append(docsSlice, "first") + for i := 0; i < 99; i++ { + docsSlice = append(docsSlice, fmt.Sprintf("doc-%d", i)) + } + + items := []bulkCheckItem{ + { + toCheck: "document:first#view@user:tom", + rda: []rda{ + expectOrderedFrames( + frameInfo{ + resourceType: "document", + resourceIDs: []string{"first"}, + permission: "view", + permissionship: v1.CheckDebugTrace_PERMISSIONSHIP_HAS_PERMISSION, + }, + frameInfo{ + resourceType: "document", + resourceIDs: docsSlice, + permission: "view", + permissionship: v1.CheckDebugTrace_PERMISSIONSHIP_UNSPECIFIED, + }, + frameInfo{ + resourceType: "document", + resourceIDs: docsSlice, + permission: "viewer", + permissionship: v1.CheckDebugTrace_PERMISSIONSHIP_UNSPECIFIED, + }, + ), + }, + }, + } + + for i := 0; i < 500; i++ { + items = append(items, bulkCheckItem{ + toCheck: fmt.Sprintf("document:doc-%d#view@user:tom", i), + rda: nil, + }) + } + + return items + })(), + }, + { + "caveated branch", + `definition user {} + + caveat somecaveat(somecondition int) { + somecondition == 42 + } + + definition document { + relation viewer: user with somecaveat + permission view = viewer + nil + } + `, + []tuple.Relationship{ + tuple.MustParse(`document:first#viewer@user:tom[somecaveat:{"somecondition": 41}]`), + tuple.MustParse(`document:second#viewer@user:tom[somecaveat:{"somecondition": 42}]`), + tuple.MustParse(`document:third#viewer@user:tom[somecaveat]`), + }, + []bulkCheckItem{ + { + toCheck: "document:first#view@user:tom", + rda: []rda{ + expectOrderedFrames( + frameInfo{ + resourceType: "document", + resourceIDs: []string{"first"}, + permission: "view", + permissionship: v1.CheckDebugTrace_PERMISSIONSHIP_NO_PERMISSION, + }, + frameInfo{ + resourceType: "document", + resourceIDs: []string{"first", "second", "third"}, + permission: "view", + permissionship: v1.CheckDebugTrace_PERMISSIONSHIP_UNSPECIFIED, + }, + frameInfo{ + resourceType: "document", + resourceIDs: []string{"first", "second", "third"}, + permission: "viewer", + permissionship: v1.CheckDebugTrace_PERMISSIONSHIP_UNSPECIFIED, + }, + ), + }, + }, + { + toCheck: "document:second#view@user:tom", + rda: []rda{ + expectOrderedFrames( + frameInfo{ + resourceType: "document", + resourceIDs: []string{"second"}, + permission: "view", + permissionship: v1.CheckDebugTrace_PERMISSIONSHIP_HAS_PERMISSION, + }, + frameInfo{ + resourceType: "document", + resourceIDs: []string{"first", "second", "third"}, + permission: "view", + permissionship: v1.CheckDebugTrace_PERMISSIONSHIP_UNSPECIFIED, + }, + frameInfo{ + resourceType: "document", + resourceIDs: []string{"first", "second", "third"}, + permission: "viewer", + permissionship: v1.CheckDebugTrace_PERMISSIONSHIP_UNSPECIFIED, + }, + ), + }, + }, + { + toCheck: "document:third#view@user:tom", + rda: []rda{ + expectOrderedFrames( + frameInfo{ + resourceType: "document", + resourceIDs: []string{"third"}, + permission: "view", + permissionship: v1.CheckDebugTrace_PERMISSIONSHIP_CONDITIONAL_PERMISSION, + }, + frameInfo{ + resourceType: "document", + resourceIDs: []string{"first", "second", "third"}, + permission: "view", + permissionship: v1.CheckDebugTrace_PERMISSIONSHIP_UNSPECIFIED, + }, + frameInfo{ + resourceType: "document", + resourceIDs: []string{"first", "second", "third"}, + permission: "viewer", + permissionship: v1.CheckDebugTrace_PERMISSIONSHIP_UNSPECIFIED, + }, + ), + }, + }, + }, + }, + } + + for _, tc := range tcs { + tc := tc + t.Run(tc.name, func(t *testing.T) { + req := require.New(t) + conn, cleanup, _, revision := testserver.NewTestServer(req, 5*time.Second, memdb.DisableGC, true, + func(ds datastore.Datastore, require *require.Assertions) (datastore.Datastore, datastore.Revision) { + return tf.DatastoreFromSchemaAndTestRelationships(ds, tc.schema, tc.relationships, req) + }) + + client := v1.NewPermissionsServiceClient(conn) + t.Cleanup(cleanup) + + ctx := context.Background() + ctx = requestmeta.AddRequestHeaders(ctx, requestmeta.RequestDebugInformation) + + items := make([]*v1.CheckBulkPermissionsRequestItem, 0, len(tc.toTest)) + for _, bci := range tc.toTest { + parsed := tuple.MustParseV1Rel(bci.toCheck) + items = append(items, &v1.CheckBulkPermissionsRequestItem{ + Resource: parsed.Resource, + Permission: parsed.Relation, + Subject: parsed.Subject, + }) + } + + checkResp, err := client.CheckBulkPermissions(ctx, &v1.CheckBulkPermissionsRequest{ + Consistency: &v1.Consistency{ + Requirement: &v1.Consistency_AtLeastAsFresh{ + AtLeastAsFresh: zedtoken.MustNewFromRevision(revision), + }, + }, + WithTracing: true, + Items: items, + }) + require.NoError(t, err) + + for idx, bci := range tc.toTest { + pair := checkResp.GetPairs()[idx] + debugInfo := pair.GetItem().DebugTrace + + for _, rda := range bci.rda { + rda(req, debugInfo) + } + } + }) + } +} diff --git a/internal/services/v1/grouping.go b/internal/services/v1/grouping.go index 621eeabf4f..99b681d2ec 100644 --- a/internal/services/v1/grouping.go +++ b/internal/services/v1/grouping.go @@ -19,6 +19,7 @@ type groupingParameters struct { atRevision datastore.Revision maximumAPIDepth uint32 maxCaveatContextSize int + withTracing bool } // groupItems takes a slice of CheckBulkPermissionsRequestItem and groups them based @@ -55,12 +56,17 @@ func checkParametersFromCheckBulkPermissionsRequestItem( params groupingParameters, caveatContext map[string]any, ) *computed.CheckParameters { + debugOption := computed.NoDebugging + if params.withTracing { + debugOption = computed.BasicDebuggingEnabled + } + return &computed.CheckParameters{ ResourceType: tuple.RR(bc.Resource.ObjectType, bc.Permission), Subject: tuple.ONR(bc.Subject.Object.ObjectType, bc.Subject.Object.ObjectId, normalizeSubjectRelation(bc.Subject)), CaveatContext: caveatContext, AtRevision: params.atRevision, MaximumDepth: params.maximumAPIDepth, - DebugOption: computed.NoDebugging, + DebugOption: debugOption, } } diff --git a/internal/services/v1/permissions.go b/internal/services/v1/permissions.go index 485b38c015..b0b5c47848 100644 --- a/internal/services/v1/permissions.go +++ b/internal/services/v1/permissions.go @@ -170,12 +170,13 @@ func (ps *permissionServer) CheckBulkPermissions(ctx context.Context, req *v1.Ch return res, nil } -func pairItemFromCheckResult(checkResult *dispatch.ResourceCheckResult) *v1.CheckBulkPermissionsPair_Item { +func pairItemFromCheckResult(checkResult *dispatch.ResourceCheckResult, debugTrace *v1.DebugInformation) *v1.CheckBulkPermissionsPair_Item { permissionship, partialCaveat := checkResultToAPITypes(checkResult) return &v1.CheckBulkPermissionsPair_Item{ Item: &v1.CheckBulkPermissionsResponseItem{ Permissionship: permissionship, PartialCaveatInfo: partialCaveat, + DebugTrace: debugTrace, }, } } diff --git a/internal/services/v1/permissions_test.go b/internal/services/v1/permissions_test.go index 5a72b2f6a9..86b3a70168 100644 --- a/internal/services/v1/permissions_test.go +++ b/internal/services/v1/permissions_test.go @@ -1971,64 +1971,80 @@ func TestCheckBulkPermissions(t *testing.T) { for _, tt := range testCases { tt := tt t.Run(tt.name, func(t *testing.T) { - req := v1.CheckBulkPermissionsRequest{ - Consistency: &v1.Consistency{ - Requirement: &v1.Consistency_FullyConsistent{FullyConsistent: true}, - }, - Items: make([]*v1.CheckBulkPermissionsRequestItem, 0, len(tt.requests)), - } + for _, withTracing := range []bool{true, false} { + t.Run(fmt.Sprintf("withTracing=%t", withTracing), func(t *testing.T) { + req := v1.CheckBulkPermissionsRequest{ + Consistency: &v1.Consistency{ + Requirement: &v1.Consistency_FullyConsistent{FullyConsistent: true}, + }, + Items: make([]*v1.CheckBulkPermissionsRequestItem, 0, len(tt.requests)), + WithTracing: withTracing, + } - for _, r := range tt.requests { - req.Items = append(req.Items, mustRelToCheckBulkRequestItem(r)) - } + for _, r := range tt.requests { + req.Items = append(req.Items, mustRelToCheckBulkRequestItem(r)) + } - expected := make([]*v1.CheckBulkPermissionsPair, 0, len(tt.response)) - for _, r := range tt.response { - reqRel, err := tuple.ParseV1Rel(r.req) - require.NoError(t, err) + expected := make([]*v1.CheckBulkPermissionsPair, 0, len(tt.response)) + for _, r := range tt.response { + reqRel, err := tuple.ParseV1Rel(r.req) + require.NoError(t, err) - resp := &v1.CheckBulkPermissionsPair_Item{ - Item: &v1.CheckBulkPermissionsResponseItem{ - Permissionship: r.resp, - }, - } - pair := &v1.CheckBulkPermissionsPair{ - Request: &v1.CheckBulkPermissionsRequestItem{ - Resource: reqRel.Resource, - Permission: reqRel.Relation, - Subject: reqRel.Subject, - }, - Response: resp, - } - if reqRel.OptionalCaveat != nil { - pair.Request.Context = reqRel.OptionalCaveat.Context - } - if len(r.partial) > 0 { - resp.Item.PartialCaveatInfo = &v1.PartialCaveatInfo{ - MissingRequiredContext: r.partial, + resp := &v1.CheckBulkPermissionsPair_Item{ + Item: &v1.CheckBulkPermissionsResponseItem{ + Permissionship: r.resp, + }, + } + pair := &v1.CheckBulkPermissionsPair{ + Request: &v1.CheckBulkPermissionsRequestItem{ + Resource: reqRel.Resource, + Permission: reqRel.Relation, + Subject: reqRel.Subject, + }, + Response: resp, + } + if reqRel.OptionalCaveat != nil { + pair.Request.Context = reqRel.OptionalCaveat.Context + } + if len(r.partial) > 0 { + resp.Item.PartialCaveatInfo = &v1.PartialCaveatInfo{ + MissingRequiredContext: r.partial, + } + } + + if r.err != nil { + rewritten := shared.RewriteError(context.Background(), r.err, &shared.ConfigForErrors{}) + s, ok := status.FromError(rewritten) + require.True(t, ok, "expected provided error to be status") + pair.Response = &v1.CheckBulkPermissionsPair_Error{ + Error: s.Proto(), + } + } + expected = append(expected, pair) } - } - if r.err != nil { - rewritten := shared.RewriteError(context.Background(), r.err, &shared.ConfigForErrors{}) - s, ok := status.FromError(rewritten) - require.True(t, ok, "expected provided error to be status") - pair.Response = &v1.CheckBulkPermissionsPair_Error{ - Error: s.Proto(), + var trailer metadata.MD + actual, err := client.CheckBulkPermissions(context.Background(), &req, grpc.Trailer(&trailer)) + require.NoError(t, err) + + dispatchCount, err := responsemeta.GetIntResponseTrailerMetadata(trailer, responsemeta.DispatchedOperationsCount) + require.NoError(t, err) + require.Equal(t, tt.expectedDispatchCount, dispatchCount) + + if withTracing { + for index, pair := range actual.Pairs { + if pair.GetItem() != nil { + parsed := tuple.MustParse(tt.requests[index]) + require.NotNil(t, pair.GetItem().DebugTrace, "missing debug trace in response for item %v", pair.GetItem()) + require.True(t, pair.GetItem().DebugTrace.Check != nil, "missing check trace in response for item %v", pair.GetItem()) + require.Equal(t, parsed.Resource.ObjectID, pair.GetItem().DebugTrace.Check.Resource.ObjectId, "resource in debug trace does not match") + } + } + } else { + testutil.RequireProtoSlicesEqual(t, expected, actual.Pairs, nil, "response bulk check pairs did not match") } - } - expected = append(expected, pair) + }) } - - var trailer metadata.MD - actual, err := client.CheckBulkPermissions(context.Background(), &req, grpc.Trailer(&trailer)) - require.NoError(t, err) - - dispatchCount, err := responsemeta.GetIntResponseTrailerMetadata(trailer, responsemeta.DispatchedOperationsCount) - require.NoError(t, err) - require.Equal(t, tt.expectedDispatchCount, dispatchCount) - - testutil.RequireProtoSlicesEqual(t, expected, actual.Pairs, nil, "response bulk check pairs did not match") }) } } diff --git a/pkg/cmd/server/defaults.go b/pkg/cmd/server/defaults.go index a534bbf6f1..b1aff63b7d 100644 --- a/pkg/cmd/server/defaults.go +++ b/pkg/cmd/server/defaults.go @@ -39,6 +39,7 @@ import ( "github.com/authzed/spicedb/pkg/datastore" consistencymw "github.com/authzed/spicedb/pkg/middleware/consistency" logmw "github.com/authzed/spicedb/pkg/middleware/logging" + "github.com/authzed/spicedb/pkg/middleware/nodeid" "github.com/authzed/spicedb/pkg/middleware/requestid" "github.com/authzed/spicedb/pkg/middleware/serverversion" "github.com/authzed/spicedb/pkg/releases" @@ -164,6 +165,7 @@ var alwaysDebugOption = grpclog.WithLevels(func(code codes.Code) grpclog.Level { const ( DefaultMiddlewareRequestID = "requestid" + DefaultMiddlewareNodeID = "nodeid" DefaultMiddlewareLog = "log" DefaultMiddlewareGRPCLog = "grpclog" DefaultMiddlewareOTelGRPC = "otelgrpc" @@ -296,6 +298,11 @@ func DefaultUnaryMiddleware(opts MiddlewareOption) (*MiddlewareChain[grpc.UnaryS WithInterceptor(logmw.UnaryServerInterceptor(logmw.ExtractMetadataField(string(requestmeta.RequestIDKey), "requestID"))). Done(), + NewUnaryMiddleware(). + WithName(DefaultMiddlewareNodeID). + WithInterceptor(nodeid.UnaryServerInterceptor("")). + Done(), + NewUnaryMiddleware(). WithName(DefaultMiddlewareOTelGRPC). WithInterceptor(otelgrpc.UnaryServerInterceptor()). // nolint: staticcheck @@ -369,6 +376,11 @@ func DefaultStreamingMiddleware(opts MiddlewareOption) (*MiddlewareChain[grpc.St WithInterceptor(logmw.StreamServerInterceptor(logmw.ExtractMetadataField(string(requestmeta.RequestIDKey), "requestID"))). Done(), + NewStreamMiddleware(). + WithName(DefaultMiddlewareNodeID). + WithInterceptor(nodeid.StreamServerInterceptor("")). + Done(), + NewStreamMiddleware(). WithName(DefaultMiddlewareOTelGRPC). WithInterceptor(otelgrpc.StreamServerInterceptor()). // nolint: staticcheck @@ -448,6 +460,7 @@ func DefaultDispatchMiddleware(logger zerolog.Logger, authFunc grpcauth.AuthFunc grpcMetricsUnaryInterceptor, grpcMetricsStreamingInterceptor := GRPCMetrics(disableGRPCLatencyHistogram) return []grpc.UnaryServerInterceptor{ requestid.UnaryServerInterceptor(requestid.GenerateIfMissing(true)), + nodeid.UnaryServerInterceptor(""), logmw.UnaryServerInterceptor(logmw.ExtractMetadataField(string(requestmeta.RequestIDKey), "requestID")), grpclog.UnaryServerInterceptor(InterceptorLogger(logger), dispatchDefaultCodeToLevel, durationFieldOption, traceIDFieldOption), grpcMetricsUnaryInterceptor, @@ -456,6 +469,7 @@ func DefaultDispatchMiddleware(logger zerolog.Logger, authFunc grpcauth.AuthFunc servicespecific.UnaryServerInterceptor, }, []grpc.StreamServerInterceptor{ requestid.StreamServerInterceptor(requestid.GenerateIfMissing(true)), + nodeid.StreamServerInterceptor(""), logmw.StreamServerInterceptor(logmw.ExtractMetadataField(string(requestmeta.RequestIDKey), "requestID")), grpclog.StreamServerInterceptor(InterceptorLogger(logger), dispatchDefaultCodeToLevel, durationFieldOption, traceIDFieldOption), grpcMetricsStreamingInterceptor, diff --git a/pkg/middleware/nodeid/nodeid.go b/pkg/middleware/nodeid/nodeid.go new file mode 100644 index 0000000000..665cd8c15b --- /dev/null +++ b/pkg/middleware/nodeid/nodeid.go @@ -0,0 +1,88 @@ +package nodeid + +import ( + "context" + "os" + + middleware "github.com/grpc-ecosystem/go-grpc-middleware/v2" + "google.golang.org/grpc" +) + +const spiceDBPrefix = "spicedb:" + +type ctxKeyType struct{} + +var nodeIDKey ctxKeyType = struct{}{} + +type nodeIDHandle struct { + nodeID string +} + +var defaultNodeID string + +// ContextWithHandle adds a placeholder to a context that will later be +// filled by the Node ID. +func ContextWithHandle(ctx context.Context) context.Context { + return context.WithValue(ctx, nodeIDKey, &nodeIDHandle{}) +} + +// FromContext reads the node's ID out of a context.Context. +func FromContext(ctx context.Context) (string, error) { + if c := ctx.Value(nodeIDKey); c != nil { + handle := c.(*nodeIDHandle) + return handle.nodeID, nil + } + + if defaultNodeID == "" { + hostname, err := os.Hostname() + if err != nil { + return "", err + } + defaultNodeID = spiceDBPrefix + hostname + } + + if err := setInContext(ctx, defaultNodeID); err != nil { + return "", err + } + + return defaultNodeID, nil +} + +// setInContext adds a node ID to the given context +func setInContext(ctx context.Context, nodeID string) error { + handle := ctx.Value(nodeIDKey) + if handle == nil { + return nil + } + handle.(*nodeIDHandle).nodeID = nodeID + return nil +} + +// UnaryServerInterceptor returns a new unary server interceptor that adds the +// node ID to the context. If empty, spicedb:$hostname is used. +func UnaryServerInterceptor(nodeID string) grpc.UnaryServerInterceptor { + return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { + newCtx := ContextWithHandle(ctx) + if nodeID != "" { + if err := setInContext(newCtx, nodeID); err != nil { + return nil, err + } + } + return handler(newCtx, req) + } +} + +// StreamServerInterceptor returns a new stream server interceptor that adds the +// node ID to the context. If empty, spicedb:$hostname is used. +func StreamServerInterceptor(nodeID string) grpc.StreamServerInterceptor { + return func(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { + wrapped := middleware.WrapServerStream(stream) + wrapped.WrappedContext = ContextWithHandle(wrapped.WrappedContext) + if nodeID != "" { + if err := setInContext(wrapped.WrappedContext, nodeID); err != nil { + return err + } + } + return handler(srv, wrapped) + } +} diff --git a/pkg/proto/dispatch/v1/dispatch.pb.go b/pkg/proto/dispatch/v1/dispatch.pb.go index 7fe55415f1..f8180138cc 100644 --- a/pkg/proto/dispatch/v1/dispatch.pb.go +++ b/pkg/proto/dispatch/v1/dispatch.pb.go @@ -1929,6 +1929,8 @@ type CheckDebugTrace struct { IsCachedResult bool `protobuf:"varint,4,opt,name=is_cached_result,json=isCachedResult,proto3" json:"is_cached_result,omitempty"` SubProblems []*CheckDebugTrace `protobuf:"bytes,5,rep,name=sub_problems,json=subProblems,proto3" json:"sub_problems,omitempty"` Duration *durationpb.Duration `protobuf:"bytes,6,opt,name=duration,proto3" json:"duration,omitempty"` + TraceId string `protobuf:"bytes,7,opt,name=trace_id,json=traceId,proto3" json:"trace_id,omitempty"` + SourceId string `protobuf:"bytes,8,opt,name=source_id,json=sourceId,proto3" json:"source_id,omitempty"` } func (x *CheckDebugTrace) Reset() { @@ -2005,6 +2007,20 @@ func (x *CheckDebugTrace) GetDuration() *durationpb.Duration { return nil } +func (x *CheckDebugTrace) GetTraceId() string { + if x != nil { + return x.TraceId + } + return "" +} + +func (x *CheckDebugTrace) GetSourceId() string { + if x != nil { + return x.SourceId + } + return "" +} + var File_dispatch_v1_dispatch_proto protoreflect.FileDescriptor var file_dispatch_v1_dispatch_proto_rawDesc = []byte{ @@ -2398,7 +2414,7 @@ var file_dispatch_v1_dispatch_proto_rawDesc = []byte{ 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x05, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x44, 0x65, 0x62, - 0x75, 0x67, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x05, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x22, 0xaf, + 0x75, 0x67, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x05, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x22, 0xe7, 0x04, 0x0a, 0x0f, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x44, 0x65, 0x62, 0x75, 0x67, 0x54, 0x72, 0x61, 0x63, 0x65, 0x12, 0x3b, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, @@ -2424,72 +2440,75 @@ var file_dispatch_v1_dispatch_proto_rawDesc = []byte{ 0x12, 0x35, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x64, - 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x5c, 0x0a, 0x0c, 0x52, 0x65, 0x73, 0x75, 0x6c, - 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x36, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, - 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, - 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x39, 0x0a, 0x0c, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, - 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x45, 0x4c, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x01, - 0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x02, - 0x32, 0xba, 0x05, 0x0a, 0x0f, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x12, 0x58, 0x0a, 0x0d, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, - 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x21, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, - 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x43, 0x68, 0x65, 0x63, - 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, - 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x43, - 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5b, - 0x0a, 0x0e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x45, 0x78, 0x70, 0x61, 0x6e, 0x64, - 0x12, 0x22, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, - 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x45, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, - 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x45, 0x78, 0x70, 0x61, 0x6e, - 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x81, 0x01, 0x0a, 0x1a, - 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, - 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x2e, 0x2e, 0x64, 0x69, 0x73, - 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, - 0x68, 0x52, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x64, 0x69, 0x73, - 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, - 0x68, 0x52, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, - 0x78, 0x0a, 0x17, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, - 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x2b, 0x2e, 0x64, 0x69, 0x73, - 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, - 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, - 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, - 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x75, 0x0a, 0x16, 0x44, 0x69, 0x73, - 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x73, 0x12, 0x2a, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, - 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, - 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x2b, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, - 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x6a, - 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, - 0x12, 0x7b, 0x0a, 0x18, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, - 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x32, 0x12, 0x2c, 0x2e, 0x64, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x72, 0x61, 0x63, 0x65, + 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x72, 0x61, 0x63, 0x65, + 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x1a, + 0x5c, 0x0a, 0x0c, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x36, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x20, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x39, 0x0a, + 0x0c, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, + 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x45, + 0x4c, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x45, 0x52, 0x4d, + 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x02, 0x32, 0xba, 0x05, 0x0a, 0x0f, 0x44, 0x69, 0x73, + 0x70, 0x61, 0x74, 0x63, 0x68, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x58, 0x0a, 0x0d, + 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x21, 0x2e, + 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, + 0x61, 0x74, 0x63, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x22, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, + 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5b, 0x0a, 0x0e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, + 0x63, 0x68, 0x45, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x12, 0x22, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, + 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x45, + 0x78, 0x70, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, + 0x74, 0x63, 0x68, 0x45, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x81, 0x01, 0x0a, 0x1a, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, + 0x52, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x12, 0x2e, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, + 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, + 0x6c, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, + 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, + 0x6c, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x78, 0x0a, 0x17, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x32, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x64, 0x69, 0x73, - 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, - 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, - 0x32, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x42, 0xaa, 0x01, - 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, - 0x31, 0x42, 0x0d, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x50, 0x72, 0x6f, 0x74, 0x6f, - 0x50, 0x01, 0x5a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, - 0x75, 0x74, 0x68, 0x7a, 0x65, 0x64, 0x2f, 0x73, 0x70, 0x69, 0x63, 0x65, 0x64, 0x62, 0x2f, 0x70, - 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, - 0x68, 0x2f, 0x76, 0x31, 0x3b, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x76, 0x31, 0xa2, - 0x02, 0x03, 0x44, 0x58, 0x58, 0xaa, 0x02, 0x0b, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, - 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0b, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5c, 0x56, - 0x31, 0xe2, 0x02, 0x17, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5c, 0x56, 0x31, 0x5c, - 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0c, 0x44, 0x69, - 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x65, 0x73, 0x12, 0x2b, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, + 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2c, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, + 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, + 0x01, 0x12, 0x75, 0x0a, 0x16, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, + 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x2a, 0x2e, 0x64, 0x69, + 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, + 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, + 0x63, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, + 0x6f, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x7b, 0x0a, 0x18, 0x44, 0x69, 0x73, 0x70, + 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x32, 0x12, 0x2c, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, + 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, + 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x32, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, + 0x2e, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x32, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x30, 0x01, 0x42, 0xaa, 0x01, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x2e, 0x64, 0x69, + 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x76, 0x31, 0x42, 0x0d, 0x44, 0x69, 0x73, 0x70, 0x61, + 0x74, 0x63, 0x68, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3b, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x7a, 0x65, 0x64, 0x2f, 0x73, + 0x70, 0x69, 0x63, 0x65, 0x64, 0x62, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2f, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2f, 0x76, 0x31, 0x3b, 0x64, 0x69, 0x73, + 0x70, 0x61, 0x74, 0x63, 0x68, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x44, 0x58, 0x58, 0xaa, 0x02, 0x0b, + 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0b, 0x44, 0x69, + 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x17, 0x44, 0x69, 0x73, 0x70, + 0x61, 0x74, 0x63, 0x68, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0xea, 0x02, 0x0c, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x3a, 0x3a, + 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/pkg/proto/dispatch/v1/dispatch.pb.validate.go b/pkg/proto/dispatch/v1/dispatch.pb.validate.go index 0b0a38ab6d..32cd1e72a5 100644 --- a/pkg/proto/dispatch/v1/dispatch.pb.validate.go +++ b/pkg/proto/dispatch/v1/dispatch.pb.validate.go @@ -4245,6 +4245,10 @@ func (m *CheckDebugTrace) validate(all bool) error { } } + // no validation rules for TraceId + + // no validation rules for SourceId + if len(errors) > 0 { return CheckDebugTraceMultiError(errors) } diff --git a/pkg/proto/dispatch/v1/dispatch_vtproto.pb.go b/pkg/proto/dispatch/v1/dispatch_vtproto.pb.go index 8df373560a..e2086aa914 100644 --- a/pkg/proto/dispatch/v1/dispatch_vtproto.pb.go +++ b/pkg/proto/dispatch/v1/dispatch_vtproto.pb.go @@ -659,6 +659,8 @@ func (m *CheckDebugTrace) CloneVT() *CheckDebugTrace { r.ResourceRelationType = m.ResourceRelationType r.IsCachedResult = m.IsCachedResult r.Duration = (*durationpb.Duration)((*durationpb1.Duration)(m.Duration).CloneVT()) + r.TraceId = m.TraceId + r.SourceId = m.SourceId if rhs := m.Results; rhs != nil { tmpContainer := make(map[string]*ResourceCheckResult, len(rhs)) for k, v := range rhs { @@ -1585,6 +1587,12 @@ func (this *CheckDebugTrace) EqualVT(that *CheckDebugTrace) bool { if !(*durationpb1.Duration)(this.Duration).EqualVT((*durationpb1.Duration)(that.Duration)) { return false } + if this.TraceId != that.TraceId { + return false + } + if this.SourceId != that.SourceId { + return false + } return string(this.unknownFields) == string(that.unknownFields) } @@ -3314,6 +3322,20 @@ func (m *CheckDebugTrace) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if len(m.SourceId) > 0 { + i -= len(m.SourceId) + copy(dAtA[i:], m.SourceId) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.SourceId))) + i-- + dAtA[i] = 0x42 + } + if len(m.TraceId) > 0 { + i -= len(m.TraceId) + copy(dAtA[i:], m.TraceId) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.TraceId))) + i-- + dAtA[i] = 0x3a + } if m.Duration != nil { size, err := (*durationpb1.Duration)(m.Duration).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { @@ -4107,6 +4129,14 @@ func (m *CheckDebugTrace) SizeVT() (n int) { l = (*durationpb1.Duration)(m.Duration).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } + l = len(m.TraceId) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } + l = len(m.SourceId) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } n += len(m.unknownFields) return n } @@ -8475,6 +8505,70 @@ func (m *CheckDebugTrace) UnmarshalVT(dAtA []byte) error { return err } iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TraceId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TraceId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SourceId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SourceId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) diff --git a/proto/internal/dispatch/v1/dispatch.proto b/proto/internal/dispatch/v1/dispatch.proto index fdcf3231d4..9c14752047 100644 --- a/proto/internal/dispatch/v1/dispatch.proto +++ b/proto/internal/dispatch/v1/dispatch.proto @@ -260,4 +260,6 @@ message CheckDebugTrace { bool is_cached_result = 4; repeated CheckDebugTrace sub_problems = 5; google.protobuf.Duration duration = 6; + string trace_id = 7; + string source_id = 8; }