diff --git a/MODULE.bazel b/MODULE.bazel index 0e5ace6c3..c29c54cf4 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -103,6 +103,12 @@ go_sdk.download( go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps") go_deps.from_file(go_mod = "//:go.mod") + +# Tango ships Bazel build files written for its own workspace, so regenerate them for this one. +go_deps.gazelle_override( + build_file_generation = "clean", + path = "github.com/uber/tango", +) go_deps.module( path = "google.golang.org/grpc/cmd/protoc-gen-go-grpc", sum = "h1:F29+wU6Ee6qgu9TddPgooOdaqsxTMunOoj8KA5yuS5A=", @@ -117,6 +123,7 @@ use_repo( "com_github_spf13_cobra", "com_github_stretchr_testify", "com_github_uber_go_tally", + "com_github_uber_tango", "in_gopkg_yaml_v3", "org_golang_google_grpc", "org_golang_google_grpc_cmd_protoc_gen_go_grpc", diff --git a/go.mod b/go.mod index 732fc8d8a..80d8e7aee 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,8 @@ require ( github.com/spf13/cobra v1.10.2 github.com/stretchr/testify v1.11.1 github.com/uber-go/tally v3.5.8+incompatible - go.uber.org/fx v1.22.0 + github.com/uber/tango v0.0.46 + go.uber.org/fx v1.24.0 go.uber.org/mock v0.6.0 go.uber.org/yarpc v1.81.0 go.uber.org/zap v1.27.1 @@ -26,6 +27,9 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/gogo/googleapis v1.4.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/gogo/status v1.1.0 // indirect github.com/golang/mock v1.7.0-rc.1 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -41,7 +45,7 @@ require ( github.com/twmb/murmur3 v1.1.8 // indirect github.com/uber/tchannel-go v1.34.4 // indirect go.uber.org/atomic v1.11.0 // indirect - go.uber.org/dig v1.17.1 // indirect + go.uber.org/dig v1.19.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/net/metrics v1.4.0 // indirect go.uber.org/thriftrw v1.32.0 // indirect @@ -52,8 +56,6 @@ require ( golang.org/x/sys v0.45.0 // indirect golang.org/x/text v0.37.0 // indirect golang.org/x/tools v0.44.0 // indirect - golang.org/x/tools/go/expect v0.1.1-deprecated // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect honnef.co/go/tools v0.4.3 // indirect ) diff --git a/go.sum b/go.sum index 7b4438147..430e6118c 100644 --- a/go.sum +++ b/go.sum @@ -41,9 +41,11 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0= github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/status v1.1.0 h1:+eIkrewn5q6b30y+g/BJINVVdi2xH7je5MPJ3ZPK3JA= @@ -85,6 +87,7 @@ github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/ github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/errcheck v1.7.0 h1:+SbscKmWJ5mOK/bO1zS60F5I9WwZDWOfRsC4RwfwRV0= github.com/kisielk/errcheck v1.7.0/go.mod h1:1kLL+jV4e+CFfueBmI1dSK2ADDyQnlrnrY/FqKluHJQ= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= @@ -92,7 +95,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -173,8 +175,12 @@ github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaO github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg= github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= +github.com/uber/tango v0.0.46 h1:EBmgNXKGzo9Nd7rjcpDd2n+qthsS/GsbvjTwe0vjkGg= +github.com/uber/tango v0.0.46/go.mod h1:ARewcVj7WUxbNW9Li4aAAvbNZqV9isaEFwhFeQMuVIA= github.com/uber/tchannel-go v1.34.4 h1:Wi7SSfUQbJ4lPqS4trP1uAZvWu46oK0qz0tOodcfcbY= github.com/uber/tchannel-go v1.34.4/go.mod h1:ERHDsQa50nNJxV8Mm6V4nxPWyGvGxiV+T/dUNRzmoC4= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= @@ -191,10 +197,10 @@ go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUS go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/dig v1.17.1 h1:Tga8Lz8PcYNsWsyHMZ1Vm0OQOUaJNDyvPImgbAu9YSc= -go.uber.org/dig v1.17.1/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= -go.uber.org/fx v1.22.0 h1:pApUK7yL0OUHMd8vkunWSlLxZVFFk70jR2nKde8X2NM= -go.uber.org/fx v1.22.0/go.mod h1:HT2M7d7RHo+ebKGh9NRcrsrHHfpZ60nW3QRubMRfv48= +go.uber.org/dig v1.19.0 h1:BACLhebsYdpQ7IROQ1AGPjrXcP5dF80U3gKoFzbaq/4= +go.uber.org/dig v1.19.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= +go.uber.org/fx v1.24.0 h1:wE8mruvpg2kiiL1Vqd0CC+tr0/24XIB10Iwp2lLWzkg= +go.uber.org/fx v1.24.0/go.mod h1:AmDeGyS+ZARGKM4tlH4FY2Jr63VjbEDJHtqXTGP5hbo= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= @@ -225,6 +231,8 @@ golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhp golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM= golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU= @@ -235,7 +243,9 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= @@ -244,8 +254,10 @@ golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= 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= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= @@ -261,6 +273,7 @@ golang.org/x/sys v0.0.0-20200117145432-59e60aa80a0c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -273,6 +286,7 @@ golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= @@ -285,6 +299,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200117215004-fe56e6335763/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c= golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI= @@ -297,8 +313,10 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk= google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.83.0 h1:JeNZEKJFbQxArAMl+hiytHauacDNqJUllNfmIMmpqnQ= google.golang.org/grpc v1.83.0/go.mod h1:kDyl6SKsiHKt0uylY5gtn5cEjkrIOhQOGDgIc4JGwzQ= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= diff --git a/submitqueue/extension/conflict/tango/tangoclient/BUILD.bazel b/submitqueue/extension/conflict/tango/tangoclient/BUILD.bazel new file mode 100644 index 000000000..03d900b79 --- /dev/null +++ b/submitqueue/extension/conflict/tango/tangoclient/BUILD.bazel @@ -0,0 +1,29 @@ +load("@rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = ["tangoclient.go"], + importpath = "github.com/uber/submitqueue/submitqueue/extension/conflict/tango/tangoclient", + visibility = [ + "//service/submitqueue/orchestrator:__subpackages__", + "//submitqueue/extension/conflict/tango:__subpackages__", + "//test/integration/submitqueue/extension/conflict/tango:__subpackages__", + ], + deps = [ + "//submitqueue/extension/conflict/tango:go_default_library", + "@com_github_uber_tango//tangopb:go_default_library", + ], +) + +go_test( + name = "go_default_test", + srcs = ["tangoclient_test.go"], + embed = [":go_default_library"], + deps = [ + "//submitqueue/extension/conflict/tango:go_default_library", + "@com_github_stretchr_testify//assert:go_default_library", + "@com_github_stretchr_testify//require:go_default_library", + "@com_github_uber_tango//tangopb:go_default_library", + "@org_uber_go_yarpc//:go_default_library", + ], +) diff --git a/submitqueue/extension/conflict/tango/tangoclient/tangoclient.go b/submitqueue/extension/conflict/tango/tangoclient/tangoclient.go new file mode 100644 index 000000000..7452d4a53 --- /dev/null +++ b/submitqueue/extension/conflict/tango/tangoclient/tangoclient.go @@ -0,0 +1,119 @@ +// Copyright (c) 2026 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package tangoclient implements tango.ChangedTargetsClient over the Tango +// service's YARPC client. It is the only package that imports Tango's +// generated types. +package tangoclient + +import ( + "context" + "errors" + "fmt" + "io" + + "github.com/uber/tango/tangopb" + + "github.com/uber/submitqueue/submitqueue/extension/conflict/tango" +) + +// Config holds what Tango needs that a changed-targets request does not carry. +type Config struct { + // Remote identifies the repository to Tango, in the form Tango is + // configured with. + Remote string + + // Strategy is how Tango computes the target graph of the base revision. + Strategy tangopb.ComputationStrategy +} + +type client struct { + tangoClient tangopb.TangoYARPCClient + config Config +} + +// New returns a ChangedTargetsClient that asks the Tango service behind +// tangoClient. It serves one repository, the one named by config.Remote. +func New(tangoClient tangopb.TangoYARPCClient, config Config) tango.ChangedTargetsClient { + return &client{tangoClient: tangoClient, config: config} +} + +type changedTarget struct { + localID int32 + distance int32 +} + +func (c *client) ChangedTargets(ctx context.Context, request tango.ChangedTargetsRequest) ([]tango.LabelDistance, error) { + stream, err := c.tangoClient.GetChangedTargets(ctx, &tangopb.GetChangedTargetsRequest{ + FirstRevision: c.description(request.BaseRevision, nil), + SecondRevision: c.description(request.BaseRevision, request.ChangeURIs), + // max_distance defaults to 0, which would keep only the seeds. + OutputConfig: &tangopb.OutputConfig{MaxDistance: -1}, + }) + if err != nil { + return nil, fmt.Errorf("failed to request changed targets: %w", err) + } + defer func() { _ = stream.CloseSend() }() + + // Only each target's local ID and distance are kept, so a response with + // millions of targets is not buffered whole. + var changed []changedTarget + var labels map[int32]string + for { + response, err := stream.Recv() + if errors.Is(err, io.EOF) { + break + } + if err != nil { + return nil, fmt.Errorf("failed to receive changed targets: %w", err) + } + for _, target := range response.GetChangedTargets().GetChangedTargets() { + // A deleted target exists only in the first revision. + node := target.GetNewTarget() + if target.GetChangeType() == tangopb.CHANGE_TYPE_DELETED { + node = target.GetOldTarget() + } + if node == nil { + return nil, fmt.Errorf("changed target of type %s has no target", target.GetChangeType()) + } + changed = append(changed, changedTarget{localID: node.GetId(), distance: target.GetDistance()}) + } + if metadata := response.GetMetadata(); metadata != nil { + labels = metadata.GetTargetIdMapping() + } + } + + targets := make([]tango.LabelDistance, 0, len(changed)) + for _, target := range changed { + label, ok := labels[target.localID] + if !ok { + return nil, fmt.Errorf("no label for changed target %d", target.localID) + } + targets = append(targets, tango.LabelDistance{Label: label, Distance: target.distance}) + } + return targets, nil +} + +func (c *client) description(baseRevision string, changeURIs []string) *tangopb.BuildDescription { + requests := make([]*tangopb.Request, len(changeURIs)) + for i, uri := range changeURIs { + requests[i] = &tangopb.Request{Url: uri} + } + return &tangopb.BuildDescription{ + Remote: c.config.Remote, + BaseSha: baseRevision, + Requests: requests, + Strategy: c.config.Strategy, + } +} diff --git a/submitqueue/extension/conflict/tango/tangoclient/tangoclient_test.go b/submitqueue/extension/conflict/tango/tangoclient/tangoclient_test.go new file mode 100644 index 000000000..a03dad746 --- /dev/null +++ b/submitqueue/extension/conflict/tango/tangoclient/tangoclient_test.go @@ -0,0 +1,224 @@ +// Copyright (c) 2026 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tangoclient + +import ( + "context" + "errors" + "io" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/uber/tango/tangopb" + "go.uber.org/yarpc" + + "github.com/uber/submitqueue/submitqueue/extension/conflict/tango" +) + +const testBaseRevision = "9f2c1d0e3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d" + +var errTestFailure = errors.New("dependency down") + +type fakeTango struct { + tangopb.TangoYARPCClient + stream *fakeStream + openErr error + request *tangopb.GetChangedTargetsRequest +} + +func (f *fakeTango) GetChangedTargets(_ context.Context, request *tangopb.GetChangedTargetsRequest, _ ...yarpc.CallOption) (tangopb.TangoServiceGetChangedTargetsYARPCClient, error) { + f.request = request + if f.openErr != nil { + return nil, f.openErr + } + return f.stream, nil +} + +type fakeStream struct { + responses []*tangopb.GetChangedTargetsResponse + endErr error + closed bool +} + +func (s *fakeStream) Context() context.Context { return context.Background() } + +func (s *fakeStream) Recv(...yarpc.StreamOption) (*tangopb.GetChangedTargetsResponse, error) { + if len(s.responses) == 0 { + if s.endErr != nil { + return nil, s.endErr + } + return nil, io.EOF + } + response := s.responses[0] + s.responses = s.responses[1:] + return response, nil +} + +func (s *fakeStream) CloseSend(...yarpc.StreamOption) error { + s.closed = true + return nil +} + +func changedTargets(targets ...*tangopb.ChangedTarget) *tangopb.GetChangedTargetsResponse { + return &tangopb.GetChangedTargetsResponse{Item: &tangopb.GetChangedTargetsResponse_ChangedTargets{ + ChangedTargets: &tangopb.ChangedTargets{ChangedTargets: targets}, + }} +} + +func metadata(labels map[int32]string) *tangopb.GetChangedTargetsResponse { + return &tangopb.GetChangedTargetsResponse{Item: &tangopb.GetChangedTargetsResponse_Metadata{ + Metadata: &tangopb.Metadata{TargetIdMapping: labels}, + }} +} + +func target(changeType tangopb.ChangeType, oldID, newID, distance int32) *tangopb.ChangedTarget { + changed := &tangopb.ChangedTarget{ChangeType: changeType, Distance: distance} + if oldID != 0 { + changed.OldTarget = &tangopb.OptimizedTarget{Id: oldID} + } + if newID != 0 { + changed.NewTarget = &tangopb.OptimizedTarget{Id: newID} + } + return changed +} + +func TestClient_ChangedTargets(t *testing.T) { + tests := []struct { + name string + stream *fakeStream + openErr error + want []tango.LabelDistance + wantError bool + }{ + { + name: "labels targets across stream chunks", + stream: &fakeStream{responses: []*tangopb.GetChangedTargetsResponse{ + changedTargets(target(tangopb.CHANGE_TYPE_CHANGED, 1, 1, 0), target(tangopb.CHANGE_TYPE_NEW, 0, 2, 0)), + changedTargets(target(tangopb.CHANGE_TYPE_CHANGED, 3, 3, 2)), + metadata(map[int32]string{1: "//app:lib", 2: "//app:new", 3: "//app:bin"}), + }}, + want: []tango.LabelDistance{ + {Label: "//app:lib", Distance: 0}, + {Label: "//app:new", Distance: 0}, + {Label: "//app:bin", Distance: 2}, + }, + }, + { + name: "deleted target is labeled by its old ID", + stream: &fakeStream{responses: []*tangopb.GetChangedTargetsResponse{ + changedTargets(target(tangopb.CHANGE_TYPE_DELETED, 7, 0, 0)), + metadata(map[int32]string{7: "//app:gone"}), + }}, + want: []tango.LabelDistance{{Label: "//app:gone", Distance: 0}}, + }, + { + name: "empty result is valid", + stream: &fakeStream{}, + want: []tango.LabelDistance{}, + }, + { + name: "metadata alone is an empty result", + stream: &fakeStream{responses: []*tangopb.GetChangedTargetsResponse{ + metadata(map[int32]string{}), + }}, + want: []tango.LabelDistance{}, + }, + { + name: "failure to open the stream fails", + openErr: errTestFailure, + wantError: true, + }, + { + name: "stream error fails", + stream: &fakeStream{ + responses: []*tangopb.GetChangedTargetsResponse{changedTargets(target(tangopb.CHANGE_TYPE_NEW, 0, 1, 0))}, + endErr: errTestFailure, + }, + wantError: true, + }, + { + name: "targets without metadata fail", + stream: &fakeStream{responses: []*tangopb.GetChangedTargetsResponse{ + changedTargets(target(tangopb.CHANGE_TYPE_NEW, 0, 1, 0)), + }}, + wantError: true, + }, + { + name: "target missing from the mapping fails", + stream: &fakeStream{responses: []*tangopb.GetChangedTargetsResponse{ + changedTargets(target(tangopb.CHANGE_TYPE_NEW, 0, 1, 0)), + metadata(map[int32]string{2: "//app:other"}), + }}, + wantError: true, + }, + { + name: "changed target without a target fails", + stream: &fakeStream{responses: []*tangopb.GetChangedTargetsResponse{ + changedTargets(target(tangopb.CHANGE_TYPE_NEW, 0, 0, 0)), + metadata(map[int32]string{}), + }}, + wantError: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + fake := &fakeTango{stream: tt.stream, openErr: tt.openErr} + c := New(fake, Config{Remote: "org/repo", Strategy: tangopb.COMPUTATION_STRATEGY_NATIVE}) + + got, err := c.ChangedTargets(context.Background(), tango.ChangedTargetsRequest{ + BaseRevision: testBaseRevision, + ChangeURIs: []string{"u1", "u2"}, + }) + + if tt.stream != nil { + assert.True(t, tt.stream.closed) + } + if tt.wantError { + require.Error(t, err) + return + } + require.NoError(t, err) + assert.Equal(t, tt.want, got) + }) + } +} + +func TestClient_ChangedTargets_Request(t *testing.T) { + fake := &fakeTango{stream: &fakeStream{}} + c := New(fake, Config{Remote: "org/repo", Strategy: tangopb.COMPUTATION_STRATEGY_NATIVE}) + + _, err := c.ChangedTargets(context.Background(), tango.ChangedTargetsRequest{ + BaseRevision: testBaseRevision, + ChangeURIs: []string{"u1", "u2"}, + }) + + require.NoError(t, err) + assert.Equal(t, &tangopb.GetChangedTargetsRequest{ + FirstRevision: &tangopb.BuildDescription{ + Remote: "org/repo", + BaseSha: testBaseRevision, + Requests: []*tangopb.Request{}, + Strategy: tangopb.COMPUTATION_STRATEGY_NATIVE, + }, + SecondRevision: &tangopb.BuildDescription{ + Remote: "org/repo", + BaseSha: testBaseRevision, + Requests: []*tangopb.Request{{Url: "u1"}, {Url: "u2"}}, + Strategy: tangopb.COMPUTATION_STRATEGY_NATIVE, + }, + OutputConfig: &tangopb.OutputConfig{MaxDistance: -1}, + }, fake.request) +}