diff --git a/Makefile b/Makefile index d3473f19784..49c3d59b93d 100644 --- a/Makefile +++ b/Makefile @@ -83,6 +83,9 @@ install-testing: build mkdir -p "$${PREFIX}/web/vtctld2" cp -R web/vtctld2/app "$${PREFIX}/web/vtctld2" +grpcvtctldclient: go/vt/proto/vtctlservice/vtctlservice.pb.go + make -C go/vt/vtctl/grpcvtctldclient + parser: make -C go/vt/sqlparser diff --git a/examples/local/scripts/vtctld-up.sh b/examples/local/scripts/vtctld-up.sh index 662a234ae48..853e5dbc544 100755 --- a/examples/local/scripts/vtctld-up.sh +++ b/examples/local/scripts/vtctld-up.sh @@ -28,7 +28,7 @@ vtctld \ -cell $cell \ -workflow_manager_init \ -workflow_manager_use_election \ - -service_map 'grpc-vtctl' \ + -service_map 'grpc-vtctl,grpc-vtctld' \ -backup_storage_implementation file \ -file_backup_storage_root $VTDATAROOT/backups \ -log_dir $VTDATAROOT/tmp \ diff --git a/go.mod b/go.mod index f1b2d8186d2..c4292f248d5 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,6 @@ require ( github.com/coreos/bbolt v1.3.2 // indirect github.com/coreos/etcd v3.3.10+incompatible github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f // indirect - github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect github.com/corpix/uarand v0.1.1 // indirect github.com/cyberdelia/go-metrics-graphite v0.0.0-20161219230853-39f87cc3b432 github.com/evanphx/json-patch v4.5.0+incompatible @@ -34,7 +33,7 @@ require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/google/uuid v1.1.1 github.com/googleapis/gnostic v0.2.0 // indirect - github.com/gorilla/websocket v1.4.0 + github.com/gorilla/websocket v1.4.2 github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 github.com/hashicorp/consul/api v1.5.0 @@ -80,15 +79,15 @@ require ( github.com/satori/go.uuid v1.2.0 // indirect github.com/sjmudd/stopwatch v0.0.0-20170613150411-f380bf8a9be1 github.com/smartystreets/goconvey v1.6.4 // indirect + github.com/spf13/cobra v0.0.5 + github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.4.0 github.com/tchap/go-patricia v0.0.0-20160729071656-dd168db6051b github.com/tebeka/selenium v0.9.9 github.com/tinylib/msgp v1.1.1 // indirect - github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 // indirect github.com/uber-go/atomic v1.4.0 // indirect github.com/uber/jaeger-client-go v2.16.0+incompatible github.com/uber/jaeger-lib v2.0.0+incompatible // indirect - github.com/ugorji/go v1.1.7 // indirect github.com/z-division/go-zookeeper v0.0.0-20190128072838-6d7457066b9b golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 golang.org/x/lint v0.0.0-20190409202823-959b441ac422 diff --git a/go.sum b/go.sum index c700b4a7578..9a187c01309 100644 --- a/go.sum +++ b/go.sum @@ -6,7 +6,11 @@ cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6A cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1 h1:lRi0CHyU+ytlvylOlFKKq0af6JncuyoRh1J+QJBqQx0= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3 h1:AVXDdKsrtX33oR9fbCMu/+c1o8Ofjq6Ku/MInaLVg5Y= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go/bigquery v1.0.1 h1:hL+ycaJpVE9M7nLoiXb/Pn10ENE2u+oddxbD8uu0ZVU= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0 h1:Kt+gOPPp2LEPWp8CSfxhsM8ik9CcyE/gYu+0r+RnZvM= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= github.com/Azure/azure-pipeline-go v0.2.1 h1:OLBdZJ3yvOn2MezlWvbrBMTEUQC72zAftRZOMdj5HYo= github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= @@ -47,6 +51,7 @@ github.com/Masterminds/glide v0.13.2/go.mod h1:STyF5vcenH/rUqTEv+/hBXlSTo7KYwg2o github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE= github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= @@ -89,11 +94,14 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/buger/jsonparser v0.0.0-20200322175846-f7e751efca13 h1:+qUNY4VRkEH46bLUwxCyUU+iOGJMQBVibAaYzWiwWcg= github.com/buger/jsonparser v0.0.0-20200322175846-f7e751efca13/go.mod h1:tgcrVJ81GPSF0mz+0nu1Xaz0fazGPrmmJfJtxjbHhUQ= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible h1:C29Ae4G5GtYyYMm1aztcyj/J5ckgJm2zwdDajFbx1NY= @@ -111,6 +119,8 @@ github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-oidc v2.1.0+incompatible h1:sdJrfw8akMnCuUlaZU3tE/uYXFgfqom8DBE9so9EBsM= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= @@ -128,6 +138,7 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc github.com/corpix/uarand v0.1.1 h1:RMr1TWc9F4n5jiPDzFHtmaUXLKLNUFK0SgCLo4BhX/U= github.com/corpix/uarand v0.1.1/go.mod h1:SFKZvkcRoLqVRFZ4u25xPmp6m9ktANfbpXZ7SJ0/FNU= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/cyberdelia/go-metrics-graphite v0.0.0-20161219230853-39f87cc3b432 h1:M5QgkYacWj0Xs8MhpIK/5uwU02icXpEoSo9sM2aRCps= github.com/cyberdelia/go-metrics-graphite v0.0.0-20161219230853-39f87cc3b432/go.mod h1:xwIwAxMvYnVrGJPe2FKx5prTrnAjGOD8zvDOnxnrrkM= @@ -139,6 +150,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= @@ -162,6 +174,7 @@ github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= @@ -224,6 +237,7 @@ github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -277,16 +291,22 @@ github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORR github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 h1:THDBEeQ9xZ8JEaCLyLQqXMMdRqNr0QAUJTIkQAUtFjg= github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5 h1:UImYN5qQ8tuGpGE16ZmjvcTtTw24zw1QAp/SlnNrZhI= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.5.0 h1:Yo2bneoGy68A7aNwmuETFnPhjyBEm7n3vzRacEVMjvI= github.com/hashicorp/consul/api v1.5.0/go.mod h1:LqwrLNW876eYSuUOo4ZLHBcdKc038txr/IMfbLPATa4= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.5.0 h1:WC4594Wp/LkEeML/OdQKEC1yqBmEYkRp6i7X5u0zDAs= github.com/hashicorp/consul/sdk v0.5.0/go.mod h1:fY08Y9z5SvJqevyZNy6WWPXiG3KwBPAvlcdx16zZ0fM= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= @@ -309,6 +329,7 @@ github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHh github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs= @@ -322,6 +343,7 @@ github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1 github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= @@ -329,11 +351,14 @@ github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uG github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/mdns v1.0.1 h1:XFSOubp8KWB+Jd2PDyaX5xUd5bhSP/+pTDZVDMzZJM8= github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/memberlist v0.2.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/memberlist v0.2.2 h1:5+RffWKwqJ71YPu9mWsF7ZOscZmwfasdA8kbdC7AO2g= github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hashicorp/serf v0.9.0/go.mod h1:YL0HO+FifKOW2u1ke99DGVu1zhcpZzNwrLIqBC7vbYU= github.com/hashicorp/serf v0.9.2 h1:yJoyfZXo4Pk2p/M/viW+YLibBFiIbKoP79gu7kDAFP0= github.com/hashicorp/serf v0.9.2/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= @@ -360,6 +385,7 @@ github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024 h1:rBMNdlhTLzJjJSDIjNEXX1Pz3Hmwmz91v+zycvx9PJc= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE= github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= @@ -439,6 +465,7 @@ github.com/minio/minio-go v0.0.0-20190131015406-c8a261de75c1/go.mod h1:vuvdOZLJu github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.0 h1:tEElEatulEHDeedTxwckzyYMA5c86fbmNIUL1hBIiTg= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= @@ -447,6 +474,8 @@ github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eI github.com/mitchellh/go-testing-interface v1.14.0 h1:/x0XQ6h+3U3nAyk1yx+bHPURrKa9sVVvYbuqZ7pIAtI= github.com/mitchellh/go-testing-interface v1.14.0/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= @@ -468,6 +497,7 @@ github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+ github.com/ngdinhtoan/glide-cleanup v0.2.0/go.mod h1:UQzsmiDOb8YV3nOsCxK/c9zPpCZVNoHScRE3EO9pVMM= github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229 h1:E2B8qYyeSgv5MXpmzZXRNp8IAQ4vjxIjhpAf5hv/tAg= github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5 h1:58+kh9C6jJVXYjt8IE48G2eWl6BjwU5Gj0gqY84fy78= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.5-0.20200416053754-163badb3bac6 h1:F721VBMijn0OBFZ5wUSuMVVLQj2IJiiupn6UNd7UbBE= @@ -514,6 +544,7 @@ github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prY github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.2 h1:awm861/B8OKDd2I/6o1dy3ra4BamzKhYOiGItCeZ740= github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.4.1 h1:FFSuS004yOQEtDdTq+TAOLP5xUq63KqAFYyOi8zA+Y8= github.com/prometheus/client_golang v1.4.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= @@ -522,14 +553,17 @@ github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1: github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181126121408-4724e9255275 h1:PnBWHBf+6L0jOqq0gIVUe6Yk0/QMZ640k6NvkxcBf+8= github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a h1:9a8MnZMP0X2nLJdBg+pBmGgkJlSaKC2KaQmTCk1XDtE= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= @@ -539,6 +573,7 @@ github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uY github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible h1:j1Wcmh8OrK4Q7GXY+V7SVSY8nUWQxHW5TkBe7YUl+2s= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= @@ -549,6 +584,7 @@ github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdh github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= @@ -563,6 +599,7 @@ github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIK github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= @@ -572,6 +609,8 @@ github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4= +github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -590,6 +629,7 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tchap/go-patricia v0.0.0-20160729071656-dd168db6051b h1:i3lm+BZX5fAaH95wJavMgsSYU95LhSxdNCMa8nLv2gk= github.com/tchap/go-patricia v0.0.0-20160729071656-dd168db6051b/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= github.com/tebeka/selenium v0.9.9 h1:cNziB+etNgyH/7KlNI7RMC1ua5aH1+5wUlFQyzeMh+w= @@ -608,11 +648,7 @@ github.com/uber/jaeger-client-go v2.16.0+incompatible h1:Q2Pp6v3QYiocMxomCaJuwQG github.com/uber/jaeger-client-go v2.16.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/uber/jaeger-lib v2.0.0+incompatible h1:iMSCV0rmXEogjNWPh2D0xk9YVKvrtGoHJNe9ebLu/pw= github.com/uber/jaeger-lib v2.0.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= -github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= -github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= -github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= @@ -624,6 +660,7 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/z-division/go-zookeeper v0.0.0-20190128072838-6d7457066b9b h1:Itr7GbuXoM1PK/eCeNNia4Qd3ib9IgX9g9SpXgo8BwQ= github.com/z-division/go-zookeeper v0.0.0-20190128072838-6d7457066b9b/go.mod h1:JNALoWa+nCXR8SmgLluHcBNVJgyejzpKPZk9pX2yXXE= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= @@ -661,16 +698,25 @@ golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947 golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 h1:A1gGSx58LAGVHUUsOf7IiR0u8Xb6W51gRwfDBhkdcaw= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422 h1:QzoH/1pFpZguR8NrRHLcO6jKqfv2zpuSqZLgdm7ZmjI= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 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/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -778,7 +824,9 @@ golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190624190245-7f2218787638/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191219041853-979b82bfef62 h1:vDaiisQl0rGVXqk3wT2yc43gSnwlj4haEG5J78IGZP4= golang.org/x/tools v0.0.0-20191219041853-979b82bfef62/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -794,6 +842,8 @@ google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0 h1:jbyannxz0XFD3zdjgrSUsaJbgpH4eTrkdhRChkHPfO8= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0 h1:Q3Ui3V3/CVinFWFiW39Iw0kMuVrRzYX0wN6OPFp0lTA= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 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= @@ -807,8 +857,11 @@ google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190626174449-989357319d63/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20190926190326-7ee9db18f195 h1:dWzgMaXfaHsnkRKZ1l3iJLDmTEB40JMl/dqRbJX4D/o= google.golang.org/genproto v0.0.0-20190926190326-7ee9db18f195/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a h1:Ob5/580gVHBJZgXnff1cZDbG+xLtMVE5mDRTe+nIsX4= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= diff --git a/go/cmd/vtctlclient/main.go b/go/cmd/vtctlclient/main.go index 4d2fcbee368..339be1ac84d 100644 --- a/go/cmd/vtctlclient/main.go +++ b/go/cmd/vtctlclient/main.go @@ -33,7 +33,7 @@ import ( ) // The default values used by these flags cannot be taken from wrangler and -// actionnode modules, as we do't want to depend on them at all. +// actionnode modules, as we don't want to depend on them at all. var ( actionTimeout = flag.Duration("action_timeout", time.Hour, "timeout for the total command") server = flag.String("server", "", "server to use for connection") diff --git a/go/cmd/vtctld/plugin_grpcvtctldserver.go b/go/cmd/vtctld/plugin_grpcvtctldserver.go new file mode 100644 index 00000000000..ee5d0aba22a --- /dev/null +++ b/go/cmd/vtctld/plugin_grpcvtctldserver.go @@ -0,0 +1,30 @@ +/* +Copyright 2019 The Vitess Authors. + +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 main + +import ( + "vitess.io/vitess/go/vt/servenv" + "vitess.io/vitess/go/vt/vtctl/grpcvtctldserver" +) + +func init() { + servenv.OnRun(func() { + if servenv.GRPCCheckServiceMap("vtctld") { + grpcvtctldserver.StartServer(servenv.GRPCServer, ts) + } + }) +} diff --git a/go/cmd/vtctldclient/cli/awk.go b/go/cmd/vtctldclient/cli/awk.go new file mode 100644 index 00000000000..2789fec2e6d --- /dev/null +++ b/go/cmd/vtctldclient/cli/awk.go @@ -0,0 +1,73 @@ +/* +Copyright 2021 The Vitess Authors. + +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 cli + +import ( + "fmt" + "sort" + "strings" + "time" + + "vitess.io/vitess/go/vt/logutil" + "vitess.io/vitess/go/vt/topo" + "vitess.io/vitess/go/vt/topo/topoproto" + + topodatapb "vitess.io/vitess/go/vt/proto/topodata" +) + +// MarshalMapAWK returns a string representation of a string->string map in an +// AWK-friendly format. +func MarshalMapAWK(m map[string]string) string { + pairs := make([]string, len(m)) + i := 0 + + for k, v := range m { + pairs[i] = fmt.Sprintf("%v: %q", k, v) + + i++ + } + + sort.Strings(pairs) + + return "[" + strings.Join(pairs, " ") + "]" +} + +// MarshalTabletAWK marshals a tablet into an AWK-friendly line. +func MarshalTabletAWK(t *topodatapb.Tablet) string { + ti := topo.TabletInfo{ + Tablet: t, + } + + keyspace := t.Keyspace + if keyspace == "" { + keyspace = "" + } + + shard := t.Shard + if shard == "" { + shard = "" + } + + mtst := "" + // special case for old primary that hasn't been updated in the topo + // yet. + if t.MasterTermStartTime != nil && t.MasterTermStartTime.Seconds > 0 { + mtst = logutil.ProtoToTime(t.MasterTermStartTime).Format(time.RFC3339) + } + + return fmt.Sprintf("%v %v %v %v %v %v %v %v", topoproto.TabletAliasString(t.Alias), keyspace, shard, topoproto.TabletTypeLString(t.Type), ti.Addr(), ti.MysqlAddr(), MarshalMapAWK(t.Tags), mtst) +} diff --git a/go/cmd/vtctldclient/cli/cobra.go b/go/cmd/vtctldclient/cli/cobra.go new file mode 100644 index 00000000000..d3f43bddbfb --- /dev/null +++ b/go/cmd/vtctldclient/cli/cobra.go @@ -0,0 +1,32 @@ +/* +Copyright 2021 The Vitess Authors. + +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 cli + +import "github.com/spf13/cobra" + +// FinishedParsing transitions a cobra.Command from treating RunE errors as +// usage errors to treating them just as normal runtime errors that should be +// propagated up to the root command's Execute method without also printing the +// subcommand's usage text on stderr. A subcommand should call this function +// from its RunE function when it has finished processing its flags and is +// moving into the pure "business logic" of its entrypoint. +// +// Package vitess.io/vitess/go/cmd/vtctldclient/internal/command has more +// details on why this exists. +func FinishedParsing(cmd *cobra.Command) { + cmd.SilenceUsage = true +} diff --git a/go/cmd/vtctldclient/cli/json.go b/go/cmd/vtctldclient/cli/json.go new file mode 100644 index 00000000000..903ca905b3e --- /dev/null +++ b/go/cmd/vtctldclient/cli/json.go @@ -0,0 +1,61 @@ +/* +Copyright 2021 The Vitess Authors. + +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 cli + +import ( + "bytes" + "encoding/json" + "fmt" + + "github.com/golang/protobuf/jsonpb" + "github.com/golang/protobuf/proto" +) + +// MarshalJSON marshals obj to a JSON string. It uses the jsonpb marshaler for +// proto.Message types, with some sensible defaults, and falls back to the +// standard Go marshaler otherwise. In both cases, the marshaled JSON is +// indented with two spaces for readability. +// +// Unfortunately jsonpb only works for types that implement proto.Message, +// either by being a proto message type or by anonymously embedding one, so for +// other types that may have nested struct fields, we still use the standard Go +// marshaler, which will result in different formattings. +func MarshalJSON(obj interface{}) ([]byte, error) { + switch obj := obj.(type) { + case proto.Message: + b := bytes.NewBuffer(nil) + m := jsonpb.Marshaler{ + EnumsAsInts: false, + EmitDefaults: true, + Indent: " ", + OrigName: true, + } + + if err := m.Marshal(b, obj); err != nil { + return nil, fmt.Errorf("jsonpb.Marshal = %v", err) + } + + return b.Bytes(), nil + default: + data, err := json.MarshalIndent(obj, "", " ") + if err != nil { + return nil, fmt.Errorf("json.Marshal = %v", err) + } + + return data, nil + } +} diff --git a/go/cmd/vtctldclient/cli/pflag.go b/go/cmd/vtctldclient/cli/pflag.go new file mode 100644 index 00000000000..8a364be8d86 --- /dev/null +++ b/go/cmd/vtctldclient/cli/pflag.go @@ -0,0 +1,93 @@ +/* +Copyright 2021 The Vitess Authors. + +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 cli + +import ( + "github.com/spf13/pflag" + + "vitess.io/vitess/go/flagutil" + "vitess.io/vitess/go/vt/key" + "vitess.io/vitess/go/vt/topo/topoproto" + + topodatapb "vitess.io/vitess/go/vt/proto/topodata" +) + +// StringMapValue augments flagutil.StringMapValue so it can be used as a +// pflag.Value. +type StringMapValue struct { + flagutil.StringMapValue +} + +// Type is part of the pflag.Value interface. +func (v *StringMapValue) Type() string { + return "cli.StringMapValue" +} + +// KeyspaceIDTypeFlag adds the pflag.Value interface to a +// topodatapb.KeyspaceIdType. +type KeyspaceIDTypeFlag topodatapb.KeyspaceIdType + +var _ pflag.Value = (*KeyspaceIDTypeFlag)(nil) + +// Set is part of the pflag.Value interface. +func (v *KeyspaceIDTypeFlag) Set(arg string) error { + t, err := key.ParseKeyspaceIDType(arg) + if err != nil { + return err + } + + *v = KeyspaceIDTypeFlag(t) + + return nil +} + +// String is part of the pflag.Value interface. +func (v *KeyspaceIDTypeFlag) String() string { + return key.KeyspaceIDTypeString(topodatapb.KeyspaceIdType(*v)) +} + +// Type is part of the pflag.Value interface. +func (v *KeyspaceIDTypeFlag) Type() string { + return "cli.KeyspaceIdTypeFlag" +} + +// KeyspaceTypeFlag adds the pflag.Value interface to a topodatapb.KeyspaceType. +type KeyspaceTypeFlag topodatapb.KeyspaceType + +var _ pflag.Value = (*KeyspaceTypeFlag)(nil) + +// Set is part of the pflag.Value interface. +func (v *KeyspaceTypeFlag) Set(arg string) error { + kt, err := topoproto.ParseKeyspaceType(arg) + if err != nil { + return err + } + + *v = KeyspaceTypeFlag(kt) + + return nil +} + +// String is part of the pflag.Value interface. +func (v *KeyspaceTypeFlag) String() string { + return topoproto.KeyspaceTypeString(topodatapb.KeyspaceType(*v)) +} + +// Type is part of the pflag.Value interface. +func (v *KeyspaceTypeFlag) Type() string { + return "cli.KeyspaceTypeFlag" +} diff --git a/go/cmd/vtctldclient/cli/shards.go b/go/cmd/vtctldclient/cli/shards.go new file mode 100644 index 00000000000..b71dc2741df --- /dev/null +++ b/go/cmd/vtctldclient/cli/shards.go @@ -0,0 +1,43 @@ +/* +Copyright 2021 The Vitess Authors. + +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 cli + +import ( + "vitess.io/vitess/go/vt/topo/topoproto" + + vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" +) + +// ParseKeyspaceShards takes a list of positional arguments and converts them to +// vtctldatapb.Shard objects. +func ParseKeyspaceShards(args []string) ([]*vtctldatapb.Shard, error) { + shards := make([]*vtctldatapb.Shard, 0, len(args)) + + for _, arg := range args { + keyspace, shard, err := topoproto.ParseKeyspaceShard(arg) + if err != nil { + return nil, err + } + + shards = append(shards, &vtctldatapb.Shard{ + Keyspace: keyspace, + Name: shard, + }) + } + + return shards, nil +} diff --git a/go/cmd/vtctldclient/cli/tablets.go b/go/cmd/vtctldclient/cli/tablets.go new file mode 100644 index 00000000000..a2962c42b45 --- /dev/null +++ b/go/cmd/vtctldclient/cli/tablets.go @@ -0,0 +1,40 @@ +/* +Copyright 2021 The Vitess Authors. + +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 cli + +import ( + "vitess.io/vitess/go/vt/topo/topoproto" + + topodatapb "vitess.io/vitess/go/vt/proto/topodata" +) + +// TabletAliasesFromPosArgs takes a list of positional (non-flag) arguments and +// converts them to tablet aliases. +func TabletAliasesFromPosArgs(args []string) ([]*topodatapb.TabletAlias, error) { + aliases := make([]*topodatapb.TabletAlias, 0, len(args)) + + for _, arg := range args { + alias, err := topoproto.ParseTabletAlias(arg) + if err != nil { + return nil, err + } + + aliases = append(aliases, alias) + } + + return aliases, nil +} diff --git a/go/cmd/vtctldclient/internal/command/backups.go b/go/cmd/vtctldclient/internal/command/backups.go new file mode 100644 index 00000000000..21d5673ef34 --- /dev/null +++ b/go/cmd/vtctldclient/internal/command/backups.go @@ -0,0 +1,62 @@ +/* +Copyright 2021 The Vitess Authors. + +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 command + +import ( + "fmt" + "strings" + + "github.com/spf13/cobra" + + "vitess.io/vitess/go/cmd/vtctldclient/cli" + vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" +) + +// GetBackups makes a GetBackups gRPC call to a vtctld. +var GetBackups = &cobra.Command{ + Use: "GetBackups keyspace shard", + Args: cobra.ExactArgs(2), + RunE: commandGetBackups, +} + +func commandGetBackups(cmd *cobra.Command, args []string) error { + cli.FinishedParsing(cmd) + + keyspace := cmd.Flags().Arg(0) + shard := cmd.Flags().Arg(1) + + resp, err := client.GetBackups(commandCtx, &vtctldatapb.GetBackupsRequest{ + Keyspace: keyspace, + Shard: shard, + }) + if err != nil { + return err + } + + names := make([]string, len(resp.Backups)) + for i, b := range resp.Backups { + names[i] = b.Name + } + + fmt.Printf("%s\n", strings.Join(names, "\n")) + + return nil +} + +func init() { + Root.AddCommand(GetBackups) +} diff --git a/go/cmd/vtctldclient/internal/command/cells.go b/go/cmd/vtctldclient/internal/command/cells.go new file mode 100644 index 00000000000..e04984f761b --- /dev/null +++ b/go/cmd/vtctldclient/internal/command/cells.go @@ -0,0 +1,106 @@ +/* +Copyright 2021 The Vitess Authors. + +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 command + +import ( + "fmt" + "strings" + + "github.com/spf13/cobra" + + "vitess.io/vitess/go/cmd/vtctldclient/cli" + + vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" +) + +var ( + // GetCellInfoNames makes a GetCellInfoNames gRPC call to a vtctld. + GetCellInfoNames = &cobra.Command{ + Use: "GetCellInfoNames", + Args: cobra.NoArgs, + RunE: commandGetCellInfoNames, + } + // GetCellInfo makes a GetCellInfo gRPC call to a vtctld. + GetCellInfo = &cobra.Command{ + Use: "GetCellInfo cell", + Args: cobra.ExactArgs(1), + RunE: commandGetCellInfo, + } + // GetCellsAliases makes a GetCellsAliases gRPC call to a vtctld. + GetCellsAliases = &cobra.Command{ + Use: "GetCellsAliases", + Args: cobra.NoArgs, + RunE: commandGetCellsAliases, + } +) + +func commandGetCellInfoNames(cmd *cobra.Command, args []string) error { + cli.FinishedParsing(cmd) + + resp, err := client.GetCellInfoNames(commandCtx, &vtctldatapb.GetCellInfoNamesRequest{}) + if err != nil { + return err + } + + fmt.Printf("%s\n", strings.Join(resp.Names, "\n")) + + return nil +} + +func commandGetCellInfo(cmd *cobra.Command, args []string) error { + cli.FinishedParsing(cmd) + + cell := cmd.Flags().Arg(0) + + resp, err := client.GetCellInfo(commandCtx, &vtctldatapb.GetCellInfoRequest{Cell: cell}) + if err != nil { + return err + } + + data, err := cli.MarshalJSON(resp.CellInfo) + if err != nil { + return err + } + + fmt.Printf("%s\n", data) + + return nil +} + +func commandGetCellsAliases(cmd *cobra.Command, args []string) error { + cli.FinishedParsing(cmd) + + resp, err := client.GetCellsAliases(commandCtx, &vtctldatapb.GetCellsAliasesRequest{}) + if err != nil { + return err + } + + data, err := cli.MarshalJSON(resp.Aliases) + if err != nil { + return err + } + + fmt.Printf("%s\n", data) + + return nil +} + +func init() { + Root.AddCommand(GetCellInfoNames) + Root.AddCommand(GetCellInfo) + Root.AddCommand(GetCellsAliases) +} diff --git a/go/cmd/vtctldclient/internal/command/doc.go b/go/cmd/vtctldclient/internal/command/doc.go new file mode 100644 index 00000000000..b83db0ef0a4 --- /dev/null +++ b/go/cmd/vtctldclient/internal/command/doc.go @@ -0,0 +1,144 @@ +/* +Copyright 2021 The Vitess Authors. + +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 command contains the commands used by vtctldclient. It is intended only +for use in vtctldclient's main package and entrypoint. The rest of this +documentation is intended for maintainers. + +Commands are grouped into files by the types of resources they interact with ( +e.g. GetTablet, CreateTablet, DeleteTablet, GetTablets) or by what they do (e.g. +PlannedReparentShard, EmergencyReparentShard, InitShardPrimary). Please add the +command to the appropriate existing file, alphabetically, or create a new +grouping if one does not exist. + +The root command lives in root.go, and commands must attach themselves to this +during an init function in order to be reachable from the CLI. root.go also +contains the global variables available to any subcommand that are managed by +the root command's pre- and post-run functions. Commands must not attempt to +manage these, as that may conflict with Root's post-run cleanup actions. All +commands should, at a minimum, use the commandCtx rather than creating their own +context.Background to start, as it contains root tracing spans that would be +lost. + +Commands should not keep their logic in an anonymous function on the +cobra.Command struct, but instead in a separate function that is assigned to +RunE. Commands should strive to keep declaration, function definition, and flag +initialization located as closely together as possible, to make the code easier +to follow and understand (the global variables declared near Root are the +exception here, not the rule). Commands should also prevent individual flag +names from polluting the package namespace. + +A good pattern we have found is to do the following: + package command + + // (imports ...) + + var ( + CreateTablet = &cobra.Command{ + Use: "CreateTablet [options] --keyspace= --shard= ", + Args: cobra.ExactArgs(2), + RunE: commandCreateTablet, + } + GetTablet = &cobra.Command{ + Use: "GetTablet ", + Args: cobra.ExactArgs(1), + RunE: commandGetTablet, + } + ) + + var createTabletOptions = struct { + Opt1 string + Opt2 bool + Keyspace string + Shard string + }{} + + func commandCreateTablet(cmd *cobra.Command, args []string) error { + aliasStr := cmd.Flags().Args(0) + tabletTypeStr := cmd.Flags().Args(1) + + // do stuff with: + // - client + // - commandCtx + // - createTabletOptions + // - aliasStr + // - tabletTypeStr + + return nil + } + + // GetTablet takes no flags, so it needs no anonymous struct to store them + func commandGetTablet(cmd *cobra.Command, args []string) error { + aliasStr := cmd.Flags().Arg(0) + + // do stuff with: + // - client + // - commandCtx + // - aliasStr + + return nil + } + + // finally, hook up all the commands in this file to Root, and add any flags + // to each of those commands + + func init() { + CreateTablet.Flags().StringVar(&createTabletOptions.Opt1, "opt1", "default", "help") + CreateTablet.Flags().BoolVar(&createTabletOptions.Opt2, "opt2", false, "help") + CreateTablet.Flags().StringVarP(&createTabletOptions.Keyspace, "keyspace", "k", "keyspace of tablet") + CreateTablet.MarkFlagRequired("keyspace") + CreateTablet.Flags().StringVarP(&createTabletOptions.Shard, "shard", "s", "shard range of tablet") + CreateTablet.MarkFlagRequired("shard") + Root.AddCommand(CreateTablet) + + Root.AddCommand(GetTablet) + } + +A note on RunE and SilenceUsage: + +We prefer using RunE over Run for the entrypoint to our subcommands, because it +allows us return errors back up to the vtctldclient main function and do error +handling, logging, and exit-code management once, in one place, rather than on a +per-command basis. However, cobra treats errors returned from a command's RunE +as usage errors, and therefore will print the command's full usage text to +stderr when RunE returns non-nil, in addition to propagating that error back up +to the result of the root command's Execute() method. This is decidedly not what +we want. There is no plan to address this in cobra v1. [1] + +The suggested workaround for this issue is to set SilenceUsage: true, either on +the root command or on every subcommand individually. This also does not work +for vtctldclient, because not every flag can be parsed during pflag.Parse time, +and for certain flags (mutually exclusive options, optional flags that require +other flags to be set with them, etc) we do additional parsing and validation of +flags in an individual subcommand. We want errors during this phase to be +treated as usage errors, so setting SilenceUsage=true before this point would +not cause usage text to be printed for us. + +So, for us, we want to individually set cmd.SilenceUsage = true at *particular +points* in each command, dependending on whether that command needs to do +an additional parse & validation pass. In most cases, the command does not need +to post-validate its options, and can set cmd.SilencUsage = true as their first +line. We feel, though, that a line that reads "SilenceUsage = true" to be +potentially confusing in how it reads. A maintainer without sufficient context +may read this and say "Silence usage? We don't want that" and remove the lines, +so we provide a wrapper function that communicates intent, cli.FinishedParsing, +that each subcommand should call when they have transitioned from the parsing & +validation phase of their entrypoint to the actual logic. + +[1]: https://github.com/spf13/cobra/issues/340 +*/ +package command diff --git a/go/cmd/vtctldclient/internal/command/keyspaces.go b/go/cmd/vtctldclient/internal/command/keyspaces.go new file mode 100644 index 00000000000..c9e779358a1 --- /dev/null +++ b/go/cmd/vtctldclient/internal/command/keyspaces.go @@ -0,0 +1,290 @@ +/* +Copyright 2021 The Vitess Authors. + +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 command + +import ( + "errors" + "fmt" + "time" + + "github.com/spf13/cobra" + + "vitess.io/vitess/go/cmd/vtctldclient/cli" + "vitess.io/vitess/go/vt/logutil" + "vitess.io/vitess/go/vt/topo" + + topodatapb "vitess.io/vitess/go/vt/proto/topodata" + vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" + "vitess.io/vitess/go/vt/proto/vttime" +) + +var ( + // CreateKeyspace makes a CreateKeyspace gRPC call to a vtctld. + CreateKeyspace = &cobra.Command{ + Use: "CreateKeyspace KEYSPACE_NAME [--force] [--sharding-column-name NAME --sharding-column-type TYPE] [--base-keyspace KEYSPACE --snapshot-timestamp TIME] [--served-from DB_TYPE:KEYSPACE ...]", + Args: cobra.ExactArgs(1), + RunE: commandCreateKeyspace, + } + // DeleteKeyspace makes a DeleteKeyspace gRPC call to a vtctld. + DeleteKeyspace = &cobra.Command{ + Use: "DeleteKeyspace KEYSPACE_NAME", + Args: cobra.ExactArgs(1), + RunE: commandDeleteKeyspace, + } + // FindAllShardsInKeyspace makes a FindAllShardsInKeyspace gRPC call to a vtctld. + FindAllShardsInKeyspace = &cobra.Command{ + Use: "FindAllShardsInKeyspace keyspace", + Aliases: []string{"findallshardsinkeyspace"}, + Args: cobra.ExactArgs(1), + RunE: commandFindAllShardsInKeyspace, + } + // GetKeyspace makes a GetKeyspace gRPC call to a vtctld. + GetKeyspace = &cobra.Command{ + Use: "GetKeyspace keyspace", + Aliases: []string{"getkeyspace"}, + Args: cobra.ExactArgs(1), + RunE: commandGetKeyspace, + } + // GetKeyspaces makes a GetKeyspaces gRPC call to a vtctld. + GetKeyspaces = &cobra.Command{ + Use: "GetKeyspaces", + Aliases: []string{"getkeyspaces"}, + Args: cobra.NoArgs, + RunE: commandGetKeyspaces, + } + // RemoveKeyspaceCell makes a RemoveKeyspaceCell gRPC call to a vtctld. + RemoveKeyspaceCell = &cobra.Command{ + Use: "RemoveKeyspaceCell ", + Args: cobra.ExactArgs(2), + RunE: commandRemoveKeyspaceCell, + } +) + +var createKeyspaceOptions = struct { + Force bool + AllowEmptyVSchema bool + + ShardingColumnName string + ShardingColumnType cli.KeyspaceIDTypeFlag + + ServedFromsMap cli.StringMapValue + + KeyspaceType cli.KeyspaceTypeFlag + BaseKeyspace string + SnapshotTimestamp string +}{ + KeyspaceType: cli.KeyspaceTypeFlag(topodatapb.KeyspaceType_NORMAL), +} + +func commandCreateKeyspace(cmd *cobra.Command, args []string) error { + name := cmd.Flags().Arg(0) + + switch topodatapb.KeyspaceType(createKeyspaceOptions.KeyspaceType) { + case topodatapb.KeyspaceType_NORMAL, topodatapb.KeyspaceType_SNAPSHOT: + default: + return fmt.Errorf("invalid keyspace type passed to --type: %v", createKeyspaceOptions.KeyspaceType) + } + + var snapshotTime *vttime.Time + if topodatapb.KeyspaceType(createKeyspaceOptions.KeyspaceType) == topodatapb.KeyspaceType_SNAPSHOT { + if createKeyspaceOptions.BaseKeyspace == "" { + return errors.New("--base-keyspace is required for a snapshot keyspace") + } + + if createKeyspaceOptions.SnapshotTimestamp == "" { + return errors.New("--snapshot-timestamp is required for a snapshot keyspace") + } + + t, err := time.Parse(time.RFC3339, createKeyspaceOptions.SnapshotTimestamp) + if err != nil { + return fmt.Errorf("cannot parse --snapshot-timestamp as RFC3339: %w", err) + } + + if now := time.Now(); t.After(now) { + return fmt.Errorf("--snapshot-time cannot be in the future; snapshot = %v, now = %v", t, now) + } + + snapshotTime = logutil.TimeToProto(t) + } + + cli.FinishedParsing(cmd) + + req := &vtctldatapb.CreateKeyspaceRequest{ + Name: name, + Force: createKeyspaceOptions.Force, + AllowEmptyVSchema: createKeyspaceOptions.AllowEmptyVSchema, + ShardingColumnName: createKeyspaceOptions.ShardingColumnName, + ShardingColumnType: topodatapb.KeyspaceIdType(createKeyspaceOptions.ShardingColumnType), + Type: topodatapb.KeyspaceType(createKeyspaceOptions.KeyspaceType), + BaseKeyspace: createKeyspaceOptions.BaseKeyspace, + SnapshotTime: snapshotTime, + } + + for n, v := range createKeyspaceOptions.ServedFromsMap.StringMapValue { + tt, err := topo.ParseServingTabletType(n) + if err != nil { + return err + } + + req.ServedFroms = append(req.ServedFroms, &topodatapb.Keyspace_ServedFrom{ + TabletType: tt, + Keyspace: v, + }) + } + + resp, err := client.CreateKeyspace(commandCtx, req) + if err != nil { + return err + } + + data, err := cli.MarshalJSON(resp.Keyspace) + if err != nil { + return err + } + + fmt.Printf("Successfully created keyspace %s. Result:\n%s\n", name, data) + + return nil +} + +var deleteKeyspaceOptions = struct { + Recursive bool +}{} + +func commandDeleteKeyspace(cmd *cobra.Command, args []string) error { + cli.FinishedParsing(cmd) + + ks := cmd.Flags().Arg(0) + _, err := client.DeleteKeyspace(commandCtx, &vtctldatapb.DeleteKeyspaceRequest{ + Keyspace: ks, + Recursive: deleteKeyspaceOptions.Recursive, + }) + + if err != nil { + return fmt.Errorf("DeleteKeyspace(%v) error: %w; please check the topo", ks, err) + } + + fmt.Printf("Successfully deleted keyspace %v.\n", ks) + + return nil +} + +func commandFindAllShardsInKeyspace(cmd *cobra.Command, args []string) error { + cli.FinishedParsing(cmd) + + ks := cmd.Flags().Arg(0) + resp, err := client.FindAllShardsInKeyspace(commandCtx, &vtctldatapb.FindAllShardsInKeyspaceRequest{ + Keyspace: ks, + }) + + if err != nil { + return err + } + + data, err := cli.MarshalJSON(resp) + if err != nil { + return err + } + + fmt.Printf("%s\n", data) + return nil +} + +func commandGetKeyspace(cmd *cobra.Command, args []string) error { + cli.FinishedParsing(cmd) + + ks := cmd.Flags().Arg(0) + resp, err := client.GetKeyspace(commandCtx, &vtctldatapb.GetKeyspaceRequest{ + Keyspace: ks, + }) + + if err != nil { + return err + } + + fmt.Printf("%+v\n", resp.Keyspace) + + return nil +} + +func commandGetKeyspaces(cmd *cobra.Command, args []string) error { + cli.FinishedParsing(cmd) + + resp, err := client.GetKeyspaces(commandCtx, &vtctldatapb.GetKeyspacesRequest{}) + if err != nil { + return err + } + + data, err := cli.MarshalJSON(resp.Keyspaces) + if err != nil { + return err + } + + fmt.Printf("%s\n", data) + + return nil +} + +var removeKeyspaceCellOptions = struct { + Force bool + Recursive bool +}{} + +func commandRemoveKeyspaceCell(cmd *cobra.Command, args []string) error { + cli.FinishedParsing(cmd) + + keyspace := cmd.Flags().Arg(0) + cell := cmd.Flags().Arg(1) + + _, err := client.RemoveKeyspaceCell(commandCtx, &vtctldatapb.RemoveKeyspaceCellRequest{ + Keyspace: keyspace, + Cell: cell, + Force: removeKeyspaceCellOptions.Force, + Recursive: removeKeyspaceCellOptions.Recursive, + }) + + if err != nil { + return err + } + + fmt.Printf("Successfully removed keyspace %s from cell %s\n", keyspace, cell) + + return nil +} + +func init() { + CreateKeyspace.Flags().BoolVarP(&createKeyspaceOptions.Force, "force", "f", false, "Proceeds even if the keyspace already exists. Does not overwrite the existing keyspace record") + CreateKeyspace.Flags().BoolVarP(&createKeyspaceOptions.AllowEmptyVSchema, "allow-empty-vschema", "e", false, "Allows a new keyspace to have no vschema") + CreateKeyspace.Flags().StringVar(&createKeyspaceOptions.ShardingColumnName, "sharding-column-name", "", "The column name to use for sharding operations") + CreateKeyspace.Flags().Var(&createKeyspaceOptions.ShardingColumnType, "sharding-column-type", "The type of the column to use for sharding operations") + CreateKeyspace.Flags().Var(&createKeyspaceOptions.ServedFromsMap, "served-from", "TODO") + CreateKeyspace.Flags().Var(&createKeyspaceOptions.KeyspaceType, "type", "The type of the keyspace") + CreateKeyspace.Flags().StringVar(&createKeyspaceOptions.BaseKeyspace, "base-keyspace", "", "The base keyspace for a snapshot keyspace.") + CreateKeyspace.Flags().StringVar(&createKeyspaceOptions.SnapshotTimestamp, "snapshot-timestamp", "", "The snapshot time for a snapshot keyspace, as a timestamp in RFC3339 format.") + Root.AddCommand(CreateKeyspace) + + DeleteKeyspace.Flags().BoolVarP(&deleteKeyspaceOptions.Recursive, "recursive", "r", false, "Recursively delete all shards in the keyspace, and all tablets in those shards.") + Root.AddCommand(DeleteKeyspace) + + Root.AddCommand(FindAllShardsInKeyspace) + Root.AddCommand(GetKeyspace) + Root.AddCommand(GetKeyspaces) + + RemoveKeyspaceCell.Flags().BoolVarP(&removeKeyspaceCellOptions.Force, "force", "f", false, "Proceed even if the cell's topology server cannot be reached. The assumption is that you turned down the entire cell, and just need to update the global topo data.") + RemoveKeyspaceCell.Flags().BoolVarP(&removeKeyspaceCellOptions.Recursive, "recursive", "r", false, "Also delete all tablets in that cell beloning to the specified keyspace.") + Root.AddCommand(RemoveKeyspaceCell) +} diff --git a/go/cmd/vtctldclient/internal/command/root.go b/go/cmd/vtctldclient/internal/command/root.go new file mode 100644 index 00000000000..7243c8836e5 --- /dev/null +++ b/go/cmd/vtctldclient/internal/command/root.go @@ -0,0 +1,81 @@ +/* +Copyright 2021 The Vitess Authors. + +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 command + +import ( + "context" + "errors" + "io" + "time" + + "github.com/spf13/cobra" + + "vitess.io/vitess/go/trace" + "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/vt/vtctl/vtctldclient" +) + +var ( + client vtctldclient.VtctldClient + traceCloser io.Closer + commandCtx context.Context + commandCancel func() + + server string + actionTimeout time.Duration + + // Root is the main entrypoint to the vtctldclient CLI. + Root = &cobra.Command{ + // We use PersistentPreRun to set up the tracer, grpc client, and + // command context for every command. + PersistentPreRunE: func(cmd *cobra.Command, args []string) (err error) { + traceCloser = trace.StartTracing("vtctldclient") + if server == "" { + err = errors.New("please specify -server to specify the vtctld server to connect to") + log.Error(err) + return err + } + + client, err = vtctldclient.New("grpc", server) + + commandCtx, commandCancel = context.WithTimeout(context.Background(), actionTimeout) + return err + }, + // Similarly, PersistentPostRun cleans up the resources spawned by + // PersistentPreRun. + PersistentPostRunE: func(cmd *cobra.Command, args []string) error { + commandCancel() + err := client.Close() + trace.LogErrorsWhenClosing(traceCloser) + return err + }, + TraverseChildren: true, + // By default, cobra will print any error returned by a child command to + // stderr, and then return that error back up the call chain. Since we + // use vitess's log package to log any error we get back from + // Root.Execute() (in ../../main.go) this actually results in duplicate + // stderr lines. So, somewhat counterintuitively, we actually "silence" + // all errors in cobra (just from being output, they still get + // propagated). + SilenceErrors: true, + } +) + +func init() { + Root.PersistentFlags().StringVar(&server, "server", "", "server to use for connection") + Root.PersistentFlags().DurationVar(&actionTimeout, "action_timeout", time.Hour, "timeout for the total command") +} diff --git a/go/cmd/vtctldclient/internal/command/schema.go b/go/cmd/vtctldclient/internal/command/schema.go new file mode 100644 index 00000000000..f1e438df308 --- /dev/null +++ b/go/cmd/vtctldclient/internal/command/schema.go @@ -0,0 +1,101 @@ +/* +Copyright 2021 The Vitess Authors. + +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 command + +import ( + "errors" + "fmt" + "strings" + + "github.com/spf13/cobra" + + "vitess.io/vitess/go/cmd/vtctldclient/cli" + "vitess.io/vitess/go/vt/topo/topoproto" + + vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" +) + +// GetSchema makes a GetSchema gRPC call to a vtctld. +var GetSchema = &cobra.Command{ + Use: "GetSchema [--tables TABLES ...] [--exclude-tables EXCLUDE_TABLES ...] [{--table-names-only | --table-sizes-only}] [--include-views] alias", + Args: cobra.ExactArgs(1), + RunE: commandGetSchema, +} + +var getSchemaOptions = struct { + Tables []string + ExcludeTables []string + IncludeViews bool + TableNamesOnly bool + TableSizesOnly bool +}{} + +func commandGetSchema(cmd *cobra.Command, args []string) error { + if getSchemaOptions.TableNamesOnly && getSchemaOptions.TableSizesOnly { + return errors.New("can only pass one of --table-names-only and --table-sizes-only") + } + + alias, err := topoproto.ParseTabletAlias(cmd.Flags().Arg(0)) + if err != nil { + return err + } + + cli.FinishedParsing(cmd) + + resp, err := client.GetSchema(commandCtx, &vtctldatapb.GetSchemaRequest{ + TabletAlias: alias, + Tables: getSchemaOptions.Tables, + ExcludeTables: getSchemaOptions.ExcludeTables, + IncludeViews: getSchemaOptions.IncludeViews, + TableNamesOnly: getSchemaOptions.TableNamesOnly, + TableSizesOnly: getSchemaOptions.TableSizesOnly, + }) + if err != nil { + return err + } + + if getSchemaOptions.TableNamesOnly { + names := make([]string, len(resp.Schema.TableDefinitions)) + + for i, td := range resp.Schema.TableDefinitions { + names[i] = td.Name + } + + fmt.Printf("%s\n", strings.Join(names, "\n")) + + return nil + } + + data, err := cli.MarshalJSON(resp.Schema) + if err != nil { + return err + } + + fmt.Printf("%s\n", data) + + return nil +} + +func init() { + GetSchema.Flags().StringSliceVar(&getSchemaOptions.Tables, "tables", nil, "TODO") + GetSchema.Flags().StringSliceVar(&getSchemaOptions.ExcludeTables, "exclude-tables", nil, "TODO") + GetSchema.Flags().BoolVar(&getSchemaOptions.IncludeViews, "include-views", false, "TODO") + GetSchema.Flags().BoolVarP(&getSchemaOptions.TableNamesOnly, "table-names-only", "n", false, "TODO") + GetSchema.Flags().BoolVarP(&getSchemaOptions.TableSizesOnly, "table-sizes-only", "s", false, "TODO") + + Root.AddCommand(GetSchema) +} diff --git a/go/cmd/vtctldclient/internal/command/shards.go b/go/cmd/vtctldclient/internal/command/shards.go new file mode 100644 index 00000000000..62d8205fb23 --- /dev/null +++ b/go/cmd/vtctldclient/internal/command/shards.go @@ -0,0 +1,190 @@ +/* +Copyright 2021 The Vitess Authors. + +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 command + +import ( + "fmt" + + "github.com/spf13/cobra" + + "vitess.io/vitess/go/cmd/vtctldclient/cli" + "vitess.io/vitess/go/vt/topo/topoproto" + + vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" +) + +var ( + // CreateShard makes a CreateShard gRPC request to a vtctld. + CreateShard = &cobra.Command{ + Use: "CreateShard ", + Args: cobra.ExactArgs(1), + RunE: commandCreateShard, + } + // DeleteShards makes a DeleteShards gRPC request to a vtctld. + DeleteShards = &cobra.Command{ + Use: "DeleteShards [ ...]", + Args: cobra.MinimumNArgs(1), + RunE: commandDeleteShards, + } + // GetShard makes a GetShard gRPC request to a vtctld. + GetShard = &cobra.Command{ + Use: "GetShard ", + Args: cobra.ExactArgs(1), + RunE: commandGetShard, + } + // RemoveShardCell makes a RemoveShardCell gRPC request to a vtctld. + RemoveShardCell = &cobra.Command{ + Use: "RemoveShardCell ", + Args: cobra.ExactArgs(2), + RunE: commandRemoveShardCell, + } +) + +var createShardOptions = struct { + Force bool + IncludeParent bool +}{} + +func commandCreateShard(cmd *cobra.Command, args []string) error { + keyspace, shard, err := topoproto.ParseKeyspaceShard(cmd.Flags().Arg(0)) + if err != nil { + return err + } + + cli.FinishedParsing(cmd) + + resp, err := client.CreateShard(commandCtx, &vtctldatapb.CreateShardRequest{ + Keyspace: keyspace, + ShardName: shard, + Force: createShardOptions.Force, + IncludeParent: createShardOptions.IncludeParent, + }) + if err != nil { + return err + } + + data, err := cli.MarshalJSON(resp) + if err != nil { + return err + } + + fmt.Printf("%s\n", data) + + return nil +} + +var deleteShardsOptions = struct { + Recursive bool + EvenIfServing bool +}{} + +func commandDeleteShards(cmd *cobra.Command, args []string) error { + shards, err := cli.ParseKeyspaceShards(cmd.Flags().Args()) + if err != nil { + return err + } + + cli.FinishedParsing(cmd) + + _, err = client.DeleteShards(commandCtx, &vtctldatapb.DeleteShardsRequest{ + Shards: shards, + EvenIfServing: deleteShardsOptions.EvenIfServing, + Recursive: deleteShardsOptions.Recursive, + }) + + if err != nil { + return fmt.Errorf("%w: while deleting %d shards; please inspect the topo", err, len(shards)) + } + + fmt.Printf("Successfully deleted %d shards\n", len(shards)) + + return nil +} + +func commandGetShard(cmd *cobra.Command, args []string) error { + keyspace, shard, err := topoproto.ParseKeyspaceShard(cmd.Flags().Arg(0)) + if err != nil { + return err + } + + cli.FinishedParsing(cmd) + + resp, err := client.GetShard(commandCtx, &vtctldatapb.GetShardRequest{ + Keyspace: keyspace, + ShardName: shard, + }) + if err != nil { + return err + } + + data, err := cli.MarshalJSON(resp.Shard) + if err != nil { + return err + } + + fmt.Printf("%s\n", data) + + return nil +} + +var removeShardCellOptions = struct { + Force bool + Recursive bool +}{} + +func commandRemoveShardCell(cmd *cobra.Command, args []string) error { + keyspace, shard, err := topoproto.ParseKeyspaceShard(cmd.Flags().Arg(0)) + if err != nil { + return err + } + + cli.FinishedParsing(cmd) + + cell := cmd.Flags().Arg(1) + + _, err = client.RemoveShardCell(commandCtx, &vtctldatapb.RemoveShardCellRequest{ + Keyspace: keyspace, + ShardName: shard, + Cell: cell, + Force: removeShardCellOptions.Force, + Recursive: removeShardCellOptions.Recursive, + }) + + if err != nil { + return err + } + + fmt.Printf("Successfully removed cell %v from shard %s/%s\n", cell, keyspace, shard) + + return nil +} + +func init() { + CreateShard.Flags().BoolVarP(&createShardOptions.Force, "force", "f", false, "") + CreateShard.Flags().BoolVarP(&createShardOptions.IncludeParent, "include-parent", "p", false, "") + Root.AddCommand(CreateShard) + + DeleteShards.Flags().BoolVarP(&deleteShardsOptions.Recursive, "recursive", "r", false, "Also delete all tablets belonging to the shard. This is required to delete a non-empty shard.") + DeleteShards.Flags().BoolVarP(&deleteShardsOptions.EvenIfServing, "even-if-serving", "f", false, "Remove the shard even if it is serving. Use with caution.") + Root.AddCommand(DeleteShards) + + Root.AddCommand(GetShard) + + RemoveShardCell.Flags().BoolVarP(&removeShardCellOptions.Force, "force", "f", false, "Proceed even if the cell's topology server cannot be reached. The assumption is that you turned down the entire cell, and just need to update the global topo data.") + RemoveShardCell.Flags().BoolVarP(&removeShardCellOptions.Recursive, "recursive", "r", false, "Also delete all tablets in that cell beloning to the specified shard.") + Root.AddCommand(RemoveShardCell) +} diff --git a/go/cmd/vtctldclient/internal/command/tablets.go b/go/cmd/vtctldclient/internal/command/tablets.go new file mode 100644 index 00000000000..dcf7e832d54 --- /dev/null +++ b/go/cmd/vtctldclient/internal/command/tablets.go @@ -0,0 +1,210 @@ +/* +Copyright 2021 The Vitess Authors. + +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 command + +import ( + "fmt" + "strings" + + "github.com/spf13/cobra" + + "vitess.io/vitess/go/cmd/vtctldclient/cli" + "vitess.io/vitess/go/vt/topo/topoproto" + + vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" +) + +var ( + // ChangeTabletType makes a ChangeTabletType gRPC call to a vtctld. + ChangeTabletType = &cobra.Command{ + Use: "ChangeTabletType [--dry-run] TABLET_ALIAS TABLET_TYPE", + Args: cobra.ExactArgs(2), + RunE: commandChangeTabletType, + } + // DeleteTablets makes a DeleteTablets gRPC call to a vtctld. + DeleteTablets = &cobra.Command{ + Use: "DeleteTablets TABLET_ALIAS [ TABLET_ALIAS ... ]", + Args: cobra.MinimumNArgs(1), + RunE: commandDeleteTablets, + } + // GetTablet makes a GetTablet gRPC call to a vtctld. + GetTablet = &cobra.Command{ + Use: "GetTablet alias", + Args: cobra.ExactArgs(1), + RunE: commandGetTablet, + } + // GetTablets makes a GetTablets gRPC call to a vtctld. + GetTablets = &cobra.Command{ + Use: "GetTablets [--cell $c1, ...] [--keyspace $ks [--shard $shard]]", + Args: cobra.NoArgs, + RunE: commandGetTablets, + } +) + +var changeTabletTypeOptions = struct { + DryRun bool +}{} + +func commandChangeTabletType(cmd *cobra.Command, args []string) error { + aliasStr := cmd.Flags().Arg(0) + typeStr := cmd.Flags().Arg(1) + + alias, err := topoproto.ParseTabletAlias(aliasStr) + if err != nil { + return err + } + + newType, err := topoproto.ParseTabletType(typeStr) + if err != nil { + return err + } + + cli.FinishedParsing(cmd) + + resp, err := client.ChangeTabletType(commandCtx, &vtctldatapb.ChangeTabletTypeRequest{ + TabletAlias: alias, + DbType: newType, + DryRun: changeTabletTypeOptions.DryRun, + }) + if err != nil { + return err + } + + if resp.WasDryRun { + fmt.Println("--- DRY RUN ---") + } + + fmt.Printf("- %v\n", cli.MarshalTabletAWK(resp.BeforeTablet)) + fmt.Printf("+ %v\n", cli.MarshalTabletAWK(resp.AfterTablet)) + + return nil +} + +var deleteTabletsOptions = struct { + AllowPrimary bool +}{} + +func commandDeleteTablets(cmd *cobra.Command, args []string) error { + aliases, err := cli.TabletAliasesFromPosArgs(cmd.Flags().Args()) + if err != nil { + return err + } + + cli.FinishedParsing(cmd) + + _, err = client.DeleteTablets(commandCtx, &vtctldatapb.DeleteTabletsRequest{ + TabletAliases: aliases, + AllowPrimary: deleteTabletsOptions.AllowPrimary, + }) + + if err != nil { + return fmt.Errorf("%w: while deleting %d tablets; please inspect the topo", err, len(aliases)) + } + + fmt.Printf("Successfully deleted %d tablets\n", len(aliases)) + + return nil +} + +func commandGetTablet(cmd *cobra.Command, args []string) error { + aliasStr := cmd.Flags().Arg(0) + alias, err := topoproto.ParseTabletAlias(aliasStr) + if err != nil { + return err + } + + cli.FinishedParsing(cmd) + + resp, err := client.GetTablet(commandCtx, &vtctldatapb.GetTabletRequest{TabletAlias: alias}) + if err != nil { + return err + } + + data, err := cli.MarshalJSON(resp.Tablet) + if err != nil { + return err + } + + fmt.Printf("%s\n", data) + + return nil +} + +var getTabletsOptions = struct { + Cells []string + Keyspace string + Shard string + + Format string +}{} + +func commandGetTablets(cmd *cobra.Command, args []string) error { + format := strings.ToLower(getTabletsOptions.Format) + + switch format { + case "awk", "json": + default: + return fmt.Errorf("invalid output format, got %s", getTabletsOptions.Format) + } + + if getTabletsOptions.Keyspace == "" && getTabletsOptions.Shard != "" { + return fmt.Errorf("--shard (= %s) cannot be passed without also passing --keyspace", getTabletsOptions.Shard) + } + + cli.FinishedParsing(cmd) + + resp, err := client.GetTablets(commandCtx, &vtctldatapb.GetTabletsRequest{ + Cells: getTabletsOptions.Cells, + Keyspace: getTabletsOptions.Keyspace, + Shard: getTabletsOptions.Shard, + }) + if err != nil { + return err + } + + switch format { + case "awk": + for _, t := range resp.Tablets { + fmt.Println(cli.MarshalTabletAWK(t)) + } + case "json": + data, err := cli.MarshalJSON(resp.Tablets) + if err != nil { + return err + } + + fmt.Printf("%s\n", data) + } + + return nil +} + +func init() { + ChangeTabletType.Flags().BoolVarP(&changeTabletTypeOptions.DryRun, "dry-run", "d", false, "Shows the proposed change without actually executing it") + Root.AddCommand(ChangeTabletType) + + DeleteTablets.Flags().BoolVarP(&deleteTabletsOptions.AllowPrimary, "allow-primary", "p", false, "Allow the primary tablet of a shard to be deleted. Use with caution.") + Root.AddCommand(DeleteTablets) + + Root.AddCommand(GetTablet) + + GetTablets.Flags().StringSliceVarP(&getTabletsOptions.Cells, "cell", "c", nil, "list of cells to filter tablets by") + GetTablets.Flags().StringVarP(&getTabletsOptions.Keyspace, "keyspace", "k", "", "keyspace to filter tablets by") + GetTablets.Flags().StringVarP(&getTabletsOptions.Shard, "shard", "s", "", "shard to filter tablets by") + GetTablets.Flags().StringVar(&getTabletsOptions.Format, "format", "awk", "Output format to use; valid choices are (json, awk)") + Root.AddCommand(GetTablets) +} diff --git a/go/cmd/vtctldclient/internal/command/vschemas.go b/go/cmd/vtctldclient/internal/command/vschemas.go new file mode 100644 index 00000000000..0184b9b50ac --- /dev/null +++ b/go/cmd/vtctldclient/internal/command/vschemas.go @@ -0,0 +1,91 @@ +/* +Copyright 2021 The Vitess Authors. + +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 command + +import ( + "fmt" + + "github.com/spf13/cobra" + + "vitess.io/vitess/go/cmd/vtctldclient/cli" + + vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" +) + +var ( + // GetSrvVSchema makes a GetSrvVSchema gRPC call to a vtctld. + GetSrvVSchema = &cobra.Command{ + Use: "GetSrvVSchema cell", + Args: cobra.ExactArgs(1), + RunE: commandGetSrvVSchema, + } + // GetVSchema makes a GetVSchema gRPC call to a vtctld. + GetVSchema = &cobra.Command{ + Use: "GetVSchema keyspace", + Args: cobra.ExactArgs(1), + RunE: commandGetVSchema, + } +) + +func commandGetSrvVSchema(cmd *cobra.Command, args []string) error { + cli.FinishedParsing(cmd) + + cell := cmd.Flags().Arg(0) + + resp, err := client.GetSrvVSchema(commandCtx, &vtctldatapb.GetSrvVSchemaRequest{ + Cell: cell, + }) + if err != nil { + return err + } + + data, err := cli.MarshalJSON(resp.SrvVSchema) + if err != nil { + return err + } + + fmt.Printf("%s\n", data) + + return nil +} + +func commandGetVSchema(cmd *cobra.Command, args []string) error { + cli.FinishedParsing(cmd) + + keyspace := cmd.Flags().Arg(0) + + resp, err := client.GetVSchema(commandCtx, &vtctldatapb.GetVSchemaRequest{ + Keyspace: keyspace, + }) + if err != nil { + return err + } + + data, err := cli.MarshalJSON(resp.VSchema) + if err != nil { + return err + } + + fmt.Printf("%s\n", data) + + return nil +} + +func init() { + Root.AddCommand(GetSrvVSchema) + Root.AddCommand(GetVSchema) +} diff --git a/go/cmd/vtctldclient/internal/command/workflows.go b/go/cmd/vtctldclient/internal/command/workflows.go new file mode 100644 index 00000000000..760139a9be9 --- /dev/null +++ b/go/cmd/vtctldclient/internal/command/workflows.go @@ -0,0 +1,69 @@ +/* +Copyright 2021 The Vitess Authors. + +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 command + +import ( + "fmt" + + "github.com/spf13/cobra" + + "vitess.io/vitess/go/cmd/vtctldclient/cli" + + vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" +) + +var ( + // GetWorkflows makes a GetWorkflows gRPC call to a vtctld. + GetWorkflows = &cobra.Command{ + Use: "GetWorkflows ", + Args: cobra.ExactArgs(1), + RunE: commandGetWorkflows, + } +) + +var getWorkflowsOptions = struct { + ShowAll bool +}{} + +func commandGetWorkflows(cmd *cobra.Command, args []string) error { + cli.FinishedParsing(cmd) + + ks := cmd.Flags().Arg(0) + + resp, err := client.GetWorkflows(commandCtx, &vtctldatapb.GetWorkflowsRequest{ + Keyspace: ks, + ActiveOnly: !getWorkflowsOptions.ShowAll, + }) + + if err != nil { + return err + } + + data, err := cli.MarshalJSON(resp) + if err != nil { + return err + } + + fmt.Printf("%s\n", data) + + return nil +} + +func init() { + GetWorkflows.Flags().BoolVarP(&getWorkflowsOptions.ShowAll, "show-all", "a", false, "Show all workflows instead of just active workflows") + Root.AddCommand(GetWorkflows) +} diff --git a/go/cmd/vtctldclient/main.go b/go/cmd/vtctldclient/main.go new file mode 100644 index 00000000000..9086b289b8c --- /dev/null +++ b/go/cmd/vtctldclient/main.go @@ -0,0 +1,45 @@ +/* +Copyright 2020 The Vitess Authors. + +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 main + +import ( + "flag" + "os" + + "vitess.io/vitess/go/cmd/vtctldclient/internal/command" + "vitess.io/vitess/go/exit" + "vitess.io/vitess/go/vt/log" +) + +func main() { + defer exit.Recover() + + // Grab all those global flags across the codebase and shove 'em on in. + command.Root.PersistentFlags().AddGoFlagSet(flag.CommandLine) + + // hack to get rid of an "ERROR: logging before flag.Parse" + args := os.Args[:] + os.Args = os.Args[:1] + flag.Parse() + os.Args = args + + // back to your regularly scheduled cobra programming + if err := command.Root.Execute(); err != nil { + log.Error(err) + exit.Return(1) + } +} diff --git a/go/cmd/vtctldclient/plugin_grpcvtctldclient.go b/go/cmd/vtctldclient/plugin_grpcvtctldclient.go new file mode 100644 index 00000000000..e93c2a6e700 --- /dev/null +++ b/go/cmd/vtctldclient/plugin_grpcvtctldclient.go @@ -0,0 +1,23 @@ +/* +Copyright 2020 The Vitess Authors. + +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 main + +// Imports and register the gRPC vtctld client. + +import ( + _ "vitess.io/vitess/go/vt/vtctl/grpcvtctldclient" +) diff --git a/go/vt/key/key.go b/go/vt/key/key.go index 98143a1ff13..858e64406cf 100644 --- a/go/vt/key/key.go +++ b/go/vt/key/key.go @@ -62,6 +62,16 @@ func ParseKeyspaceIDType(param string) (topodatapb.KeyspaceIdType, error) { return topodatapb.KeyspaceIdType(value), nil } +// KeyspaceIDTypeString returns the string representation of a keyspace id type. +func KeyspaceIDTypeString(id topodatapb.KeyspaceIdType) string { + s, ok := topodatapb.KeyspaceIdType_name[int32(id)] + if !ok { + return KeyspaceIDTypeString(topodatapb.KeyspaceIdType_UNSET) + } + + return s +} + // // KeyRange helper methods // @@ -185,7 +195,7 @@ func KeyRangeEqual(left, right *topodatapb.KeyRange) bool { bytes.Equal(left.End, right.End) } -// KeyRangeStartEqual returns true if right's keyrange start is _after_ left's start +// KeyRangeStartSmaller returns true if right's keyrange start is _after_ left's start func KeyRangeStartSmaller(left, right *topodatapb.KeyRange) bool { if left == nil { return right != nil diff --git a/go/vt/mysqlctl/mysqlctlproto/backup.go b/go/vt/mysqlctl/mysqlctlproto/backup.go new file mode 100644 index 00000000000..6fa2755b441 --- /dev/null +++ b/go/vt/mysqlctl/mysqlctlproto/backup.go @@ -0,0 +1,31 @@ +/* +Copyright 2021 The Vitess Authors. + +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 mysqlctlproto + +import ( + "vitess.io/vitess/go/vt/mysqlctl/backupstorage" + + mysqlctlpb "vitess.io/vitess/go/vt/proto/mysqlctl" +) + +// BackupHandleToProto returns a BackupInfo proto from a BackupHandle. +func BackupHandleToProto(bh backupstorage.BackupHandle) *mysqlctlpb.BackupInfo { + return &mysqlctlpb.BackupInfo{ + Name: bh.Name(), + Directory: bh.Directory(), + } +} diff --git a/go/vt/mysqlctl/mysqlctlproto/doc.go b/go/vt/mysqlctl/mysqlctlproto/doc.go new file mode 100644 index 00000000000..cf77ea853ff --- /dev/null +++ b/go/vt/mysqlctl/mysqlctlproto/doc.go @@ -0,0 +1,21 @@ +/* +Copyright 2021 The Vitess Authors. + +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 mysqlctlproto provides utility functions for working with data +structures in mysqlctl.proto. +*/ +package mysqlctlproto diff --git a/go/vt/proto/binlogdata/binlogdata.pb.go b/go/vt/proto/binlogdata/binlogdata.pb.go index 5df50ab9dc0..75a2108d406 100644 --- a/go/vt/proto/binlogdata/binlogdata.pb.go +++ b/go/vt/proto/binlogdata/binlogdata.pb.go @@ -1383,9 +1383,6 @@ func (m *VEvent) GetJournal() *Journal { func (m *VEvent) GetDml() string { if m != nil { - if m.Statement != "" { - return m.Statement - } return m.Dml } return "" diff --git a/go/vt/proto/mysqlctl/mysqlctl.pb.go b/go/vt/proto/mysqlctl/mysqlctl.pb.go index 2b80a9971d9..f7c1b3af765 100644 --- a/go/vt/proto/mysqlctl/mysqlctl.pb.go +++ b/go/vt/proto/mysqlctl/mysqlctl.pb.go @@ -351,6 +351,54 @@ func (m *RefreshConfigResponse) XXX_DiscardUnknown() { var xxx_messageInfo_RefreshConfigResponse proto.InternalMessageInfo +// BackupInfo is the read-only attributes of a mysqlctl/backupstorage.BackupHandle. +type BackupInfo struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Directory string `protobuf:"bytes,2,opt,name=directory,proto3" json:"directory,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *BackupInfo) Reset() { *m = BackupInfo{} } +func (m *BackupInfo) String() string { return proto.CompactTextString(m) } +func (*BackupInfo) ProtoMessage() {} +func (*BackupInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_cd8c110e42f9cbb9, []int{10} +} + +func (m *BackupInfo) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_BackupInfo.Unmarshal(m, b) +} +func (m *BackupInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_BackupInfo.Marshal(b, m, deterministic) +} +func (m *BackupInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_BackupInfo.Merge(m, src) +} +func (m *BackupInfo) XXX_Size() int { + return xxx_messageInfo_BackupInfo.Size(m) +} +func (m *BackupInfo) XXX_DiscardUnknown() { + xxx_messageInfo_BackupInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_BackupInfo proto.InternalMessageInfo + +func (m *BackupInfo) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *BackupInfo) GetDirectory() string { + if m != nil { + return m.Directory + } + return "" +} + func init() { proto.RegisterType((*StartRequest)(nil), "mysqlctl.StartRequest") proto.RegisterType((*StartResponse)(nil), "mysqlctl.StartResponse") @@ -362,34 +410,37 @@ func init() { proto.RegisterType((*ReinitConfigResponse)(nil), "mysqlctl.ReinitConfigResponse") proto.RegisterType((*RefreshConfigRequest)(nil), "mysqlctl.RefreshConfigRequest") proto.RegisterType((*RefreshConfigResponse)(nil), "mysqlctl.RefreshConfigResponse") + proto.RegisterType((*BackupInfo)(nil), "mysqlctl.BackupInfo") } func init() { proto.RegisterFile("mysqlctl.proto", fileDescriptor_cd8c110e42f9cbb9) } var fileDescriptor_cd8c110e42f9cbb9 = []byte{ - // 339 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x92, 0x4d, 0x4f, 0xfa, 0x30, - 0x1c, 0xc7, 0xff, 0x84, 0xfc, 0xcd, 0xfc, 0x09, 0xce, 0x54, 0x79, 0x6a, 0xa2, 0xe0, 0x12, 0x95, - 0x13, 0x4d, 0xf4, 0xa4, 0x37, 0x25, 0xf1, 0x66, 0x4c, 0x4a, 0x4c, 0x8c, 0x17, 0x32, 0xa5, 0x8c, - 0x26, 0xb8, 0x42, 0x5b, 0x20, 0xbe, 0x05, 0x5f, 0xb5, 0xb1, 0x6b, 0xc7, 0xc6, 0xc0, 0xdb, 0xfa, - 0x7d, 0x6a, 0xf6, 0xd9, 0xe0, 0xf0, 0xf3, 0x4b, 0xcd, 0xa7, 0x1f, 0x7a, 0xda, 0x9b, 0x49, 0xa1, - 0x05, 0xf2, 0xdc, 0x39, 0x20, 0x50, 0x19, 0xe8, 0x50, 0x6a, 0xca, 0xe6, 0x0b, 0xa6, 0x34, 0x6a, - 0xc3, 0x81, 0xf1, 0x46, 0xc3, 0x50, 0x46, 0xaa, 0x59, 0xea, 0x94, 0xbb, 0xfb, 0x14, 0x12, 0xe9, - 0x5e, 0x46, 0x2a, 0xf0, 0xa1, 0x6a, 0x0b, 0x6a, 0x26, 0x62, 0xc5, 0x82, 0x5b, 0xf0, 0x07, 0x93, - 0x85, 0x1e, 0x89, 0x55, 0xec, 0x46, 0x2e, 0xc1, 0x5f, 0x85, 0x5c, 0x0f, 0xc7, 0x42, 0x0e, 0x93, - 0x6a, 0xb3, 0xd4, 0x29, 0x75, 0x3d, 0x5a, 0xfd, 0x95, 0x1f, 0x85, 0x7c, 0x32, 0x62, 0x80, 0xe0, - 0x68, 0x5d, 0xb5, 0x73, 0x4d, 0xa8, 0xd3, 0x45, 0x6c, 0x02, 0x2f, 0xb3, 0x48, 0x86, 0x23, 0x66, - 0x57, 0x83, 0x16, 0x34, 0x0a, 0x8e, 0x2d, 0xd5, 0xe0, 0x98, 0x32, 0x1e, 0x73, 0xdd, 0x17, 0xf1, - 0x98, 0x47, 0xae, 0x51, 0x87, 0x93, 0xbc, 0x6c, 0xe3, 0x46, 0x1f, 0x4b, 0xa6, 0x26, 0xf9, 0x7c, - 0x03, 0x6a, 0x1b, 0x7a, 0x52, 0xb8, 0xfe, 0x2e, 0x83, 0x67, 0x2e, 0xee, 0xeb, 0x29, 0xba, 0x83, - 0xff, 0x86, 0x00, 0xaa, 0xf7, 0x52, 0xac, 0x59, 0x86, 0xb8, 0x51, 0xd0, 0xed, 0xbd, 0xff, 0x50, - 0x1f, 0x3c, 0xf7, 0xc6, 0xa8, 0x95, 0x89, 0xe5, 0x01, 0x62, 0xbc, 0xcd, 0x4a, 0x47, 0x5e, 0xc1, - 0xdf, 0x00, 0x81, 0x3a, 0xeb, 0xc2, 0x76, 0x7a, 0xf8, 0xfc, 0x8f, 0x44, 0xba, 0xfc, 0x0c, 0x95, - 0x2c, 0x30, 0x74, 0x9a, 0x29, 0x15, 0xf9, 0xe2, 0xb3, 0x5d, 0x76, 0x3a, 0x48, 0xa1, 0x9a, 0x23, - 0x8a, 0x72, 0x95, 0xe2, 0x27, 0xc0, 0xed, 0x9d, 0xbe, 0xdb, 0x7c, 0xb8, 0x7a, 0xbb, 0x58, 0x72, - 0xcd, 0x94, 0xea, 0x71, 0x41, 0x92, 0x27, 0x12, 0x09, 0xb2, 0xd4, 0xc4, 0xfc, 0xdc, 0xc4, 0x0d, - 0xbc, 0xef, 0x99, 0xf3, 0xcd, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xb8, 0xe2, 0x15, 0x86, 0xfe, - 0x02, 0x00, 0x00, + // 376 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x53, 0xdf, 0x4b, 0xc2, 0x40, + 0x1c, 0xcf, 0xac, 0x98, 0xdf, 0xb4, 0xc5, 0x95, 0x3a, 0x47, 0xa5, 0x0d, 0x2a, 0x9f, 0x1c, 0xd4, + 0x53, 0x3d, 0x04, 0x29, 0x04, 0x3d, 0x44, 0x30, 0x09, 0xa2, 0x17, 0x59, 0xee, 0x36, 0x47, 0xba, + 0x9b, 0x77, 0x37, 0xc5, 0x7f, 0xa1, 0xbf, 0x3a, 0x3a, 0x6f, 0x73, 0x73, 0xda, 0xdb, 0xdd, 0xe7, + 0x17, 0xb7, 0xcf, 0x87, 0xc1, 0xd1, 0x64, 0xc1, 0xa6, 0xe3, 0x21, 0x1f, 0x77, 0x42, 0x4a, 0x38, + 0x41, 0x4a, 0x7c, 0x37, 0x4c, 0x28, 0xf7, 0xb9, 0x4d, 0xb9, 0x85, 0xa7, 0x11, 0x66, 0x1c, 0x35, + 0xe1, 0x50, 0x70, 0xce, 0xc0, 0xa6, 0x1e, 0xd3, 0x0a, 0xad, 0x62, 0xbb, 0x64, 0xc1, 0x12, 0x7a, + 0xa2, 0x1e, 0x33, 0x54, 0xa8, 0x48, 0x03, 0x0b, 0x49, 0xc0, 0xb0, 0x71, 0x0f, 0x6a, 0x7f, 0x14, + 0x71, 0x87, 0xcc, 0x83, 0x38, 0xe4, 0x1a, 0xd4, 0xb9, 0xed, 0xf3, 0x81, 0x4b, 0xe8, 0x60, 0x69, + 0xd5, 0x0a, 0xad, 0x42, 0x5b, 0xb1, 0x2a, 0x7f, 0xf0, 0x33, 0xa1, 0xaf, 0x02, 0x34, 0x10, 0x1c, + 0xaf, 0xac, 0x32, 0x4e, 0x83, 0x9a, 0x15, 0x05, 0x42, 0xf0, 0x1e, 0x7a, 0xd4, 0x76, 0xb0, 0x4c, + 0x35, 0x1a, 0x50, 0xcf, 0x31, 0xd2, 0x54, 0x85, 0x13, 0x0b, 0xfb, 0x81, 0xcf, 0x7b, 0x24, 0x70, + 0x7d, 0x2f, 0x76, 0xd4, 0xe0, 0x34, 0x0b, 0x4b, 0xb9, 0xc0, 0x5d, 0x8a, 0xd9, 0x28, 0xab, 0xaf, + 0x43, 0x75, 0x0d, 0x97, 0x86, 0x47, 0x80, 0xae, 0x3d, 0xfc, 0x8e, 0xc2, 0x97, 0xc0, 0x25, 0x08, + 0xc1, 0x5e, 0x60, 0x4f, 0xb0, 0xf8, 0xa6, 0x92, 0x25, 0xce, 0xe8, 0x0c, 0x4a, 0x8e, 0x4f, 0xf1, + 0x90, 0x13, 0xba, 0xd0, 0x76, 0x05, 0xb1, 0x02, 0x6e, 0x7f, 0x8a, 0xa0, 0x88, 0x87, 0xf7, 0xf8, + 0x18, 0x3d, 0xc0, 0xbe, 0x68, 0x10, 0xd5, 0x3a, 0xc9, 0x2c, 0xe9, 0x0d, 0xf4, 0x7a, 0x0e, 0x97, + 0xcf, 0xd8, 0x41, 0x3d, 0x50, 0xe2, 0xc6, 0x50, 0x23, 0x25, 0xcb, 0x0e, 0xa0, 0xeb, 0x9b, 0xa8, + 0x24, 0xe4, 0x03, 0xd4, 0xb5, 0x22, 0x51, 0x6b, 0x65, 0xd8, 0xdc, 0xbe, 0x7e, 0xf9, 0x8f, 0x22, + 0x49, 0x7e, 0x83, 0x72, 0xba, 0x70, 0x74, 0x9e, 0x32, 0xe5, 0xf7, 0xd1, 0x2f, 0xb6, 0xd1, 0x49, + 0xa0, 0x05, 0x95, 0xcc, 0x22, 0x28, 0x63, 0xc9, 0x4f, 0xa8, 0x37, 0xb7, 0xf2, 0x71, 0x66, 0xf7, + 0xe6, 0xf3, 0x6a, 0xe6, 0x73, 0xcc, 0x58, 0xc7, 0x27, 0xe6, 0xf2, 0x64, 0x7a, 0xc4, 0x9c, 0x71, + 0x53, 0xfc, 0x1c, 0x66, 0x1c, 0xf0, 0x75, 0x20, 0xee, 0x77, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, + 0xd3, 0x4f, 0x5c, 0x33, 0x3e, 0x03, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/go/vt/proto/vtctldata/vtctldata.pb.go b/go/vt/proto/vtctldata/vtctldata.pb.go index 8734dc87f02..54deb2c00e7 100644 --- a/go/vt/proto/vtctldata/vtctldata.pb.go +++ b/go/vt/proto/vtctldata/vtctldata.pb.go @@ -8,7 +8,13 @@ import ( math "math" proto "github.com/golang/protobuf/proto" + binlogdata "vitess.io/vitess/go/vt/proto/binlogdata" logutil "vitess.io/vitess/go/vt/proto/logutil" + mysqlctl "vitess.io/vitess/go/vt/proto/mysqlctl" + tabletmanagerdata "vitess.io/vitess/go/vt/proto/tabletmanagerdata" + topodata "vitess.io/vitess/go/vt/proto/topodata" + vschema "vitess.io/vitess/go/vt/proto/vschema" + vttime "vitess.io/vitess/go/vt/proto/vttime" ) // Reference imports to suppress errors if they are not otherwise used. @@ -181,8 +187,11 @@ type MaterializeSettings struct { StopAfterCopy bool `protobuf:"varint,4,opt,name=stop_after_copy,json=stopAfterCopy,proto3" json:"stop_after_copy,omitempty"` TableSettings []*TableMaterializeSettings `protobuf:"bytes,5,rep,name=table_settings,json=tableSettings,proto3" json:"table_settings,omitempty"` // optional parameters. - Cell string `protobuf:"bytes,6,opt,name=cell,proto3" json:"cell,omitempty"` - TabletTypes string `protobuf:"bytes,7,opt,name=tablet_types,json=tabletTypes,proto3" json:"tablet_types,omitempty"` + Cell string `protobuf:"bytes,6,opt,name=cell,proto3" json:"cell,omitempty"` + TabletTypes string `protobuf:"bytes,7,opt,name=tablet_types,json=tabletTypes,proto3" json:"tablet_types,omitempty"` + // ExternalCluster is the name of the mounted cluster which has the source keyspace/db for this workflow + // it is of the type + ExternalCluster string `protobuf:"bytes,8,opt,name=external_cluster,json=externalCluster,proto3" json:"external_cluster,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -262,42 +271,2897 @@ func (m *MaterializeSettings) GetTabletTypes() string { return "" } +func (m *MaterializeSettings) GetExternalCluster() string { + if m != nil { + return m.ExternalCluster + } + return "" +} + +type Keyspace struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Keyspace *topodata.Keyspace `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Keyspace) Reset() { *m = Keyspace{} } +func (m *Keyspace) String() string { return proto.CompactTextString(m) } +func (*Keyspace) ProtoMessage() {} +func (*Keyspace) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{4} +} + +func (m *Keyspace) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Keyspace.Unmarshal(m, b) +} +func (m *Keyspace) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Keyspace.Marshal(b, m, deterministic) +} +func (m *Keyspace) XXX_Merge(src proto.Message) { + xxx_messageInfo_Keyspace.Merge(m, src) +} +func (m *Keyspace) XXX_Size() int { + return xxx_messageInfo_Keyspace.Size(m) +} +func (m *Keyspace) XXX_DiscardUnknown() { + xxx_messageInfo_Keyspace.DiscardUnknown(m) +} + +var xxx_messageInfo_Keyspace proto.InternalMessageInfo + +func (m *Keyspace) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Keyspace) GetKeyspace() *topodata.Keyspace { + if m != nil { + return m.Keyspace + } + return nil +} + +type Shard struct { + Keyspace string `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Shard *topodata.Shard `protobuf:"bytes,3,opt,name=shard,proto3" json:"shard,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Shard) Reset() { *m = Shard{} } +func (m *Shard) String() string { return proto.CompactTextString(m) } +func (*Shard) ProtoMessage() {} +func (*Shard) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{5} +} + +func (m *Shard) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Shard.Unmarshal(m, b) +} +func (m *Shard) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Shard.Marshal(b, m, deterministic) +} +func (m *Shard) XXX_Merge(src proto.Message) { + xxx_messageInfo_Shard.Merge(m, src) +} +func (m *Shard) XXX_Size() int { + return xxx_messageInfo_Shard.Size(m) +} +func (m *Shard) XXX_DiscardUnknown() { + xxx_messageInfo_Shard.DiscardUnknown(m) +} + +var xxx_messageInfo_Shard proto.InternalMessageInfo + +func (m *Shard) GetKeyspace() string { + if m != nil { + return m.Keyspace + } + return "" +} + +func (m *Shard) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Shard) GetShard() *topodata.Shard { + if m != nil { + return m.Shard + } + return nil +} + +// TODO: comment the hell out of this. +type Workflow struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Source *Workflow_ReplicationLocation `protobuf:"bytes,2,opt,name=source,proto3" json:"source,omitempty"` + Target *Workflow_ReplicationLocation `protobuf:"bytes,3,opt,name=target,proto3" json:"target,omitempty"` + MaxVReplicationLag int64 `protobuf:"varint,4,opt,name=max_v_replication_lag,json=maxVReplicationLag,proto3" json:"max_v_replication_lag,omitempty"` + ShardStreams map[string]*Workflow_ShardStream `protobuf:"bytes,5,rep,name=shard_streams,json=shardStreams,proto3" json:"shard_streams,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Workflow) Reset() { *m = Workflow{} } +func (m *Workflow) String() string { return proto.CompactTextString(m) } +func (*Workflow) ProtoMessage() {} +func (*Workflow) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{6} +} + +func (m *Workflow) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Workflow.Unmarshal(m, b) +} +func (m *Workflow) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Workflow.Marshal(b, m, deterministic) +} +func (m *Workflow) XXX_Merge(src proto.Message) { + xxx_messageInfo_Workflow.Merge(m, src) +} +func (m *Workflow) XXX_Size() int { + return xxx_messageInfo_Workflow.Size(m) +} +func (m *Workflow) XXX_DiscardUnknown() { + xxx_messageInfo_Workflow.DiscardUnknown(m) +} + +var xxx_messageInfo_Workflow proto.InternalMessageInfo + +func (m *Workflow) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Workflow) GetSource() *Workflow_ReplicationLocation { + if m != nil { + return m.Source + } + return nil +} + +func (m *Workflow) GetTarget() *Workflow_ReplicationLocation { + if m != nil { + return m.Target + } + return nil +} + +func (m *Workflow) GetMaxVReplicationLag() int64 { + if m != nil { + return m.MaxVReplicationLag + } + return 0 +} + +func (m *Workflow) GetShardStreams() map[string]*Workflow_ShardStream { + if m != nil { + return m.ShardStreams + } + return nil +} + +type Workflow_ReplicationLocation struct { + Keyspace string `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + Shards []string `protobuf:"bytes,2,rep,name=shards,proto3" json:"shards,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Workflow_ReplicationLocation) Reset() { *m = Workflow_ReplicationLocation{} } +func (m *Workflow_ReplicationLocation) String() string { return proto.CompactTextString(m) } +func (*Workflow_ReplicationLocation) ProtoMessage() {} +func (*Workflow_ReplicationLocation) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{6, 1} +} + +func (m *Workflow_ReplicationLocation) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Workflow_ReplicationLocation.Unmarshal(m, b) +} +func (m *Workflow_ReplicationLocation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Workflow_ReplicationLocation.Marshal(b, m, deterministic) +} +func (m *Workflow_ReplicationLocation) XXX_Merge(src proto.Message) { + xxx_messageInfo_Workflow_ReplicationLocation.Merge(m, src) +} +func (m *Workflow_ReplicationLocation) XXX_Size() int { + return xxx_messageInfo_Workflow_ReplicationLocation.Size(m) +} +func (m *Workflow_ReplicationLocation) XXX_DiscardUnknown() { + xxx_messageInfo_Workflow_ReplicationLocation.DiscardUnknown(m) +} + +var xxx_messageInfo_Workflow_ReplicationLocation proto.InternalMessageInfo + +func (m *Workflow_ReplicationLocation) GetKeyspace() string { + if m != nil { + return m.Keyspace + } + return "" +} + +func (m *Workflow_ReplicationLocation) GetShards() []string { + if m != nil { + return m.Shards + } + return nil +} + +type Workflow_ShardStream struct { + Streams []*Workflow_Stream `protobuf:"bytes,1,rep,name=streams,proto3" json:"streams,omitempty"` + TabletControls []*topodata.Shard_TabletControl `protobuf:"bytes,2,rep,name=tablet_controls,json=tabletControls,proto3" json:"tablet_controls,omitempty"` + IsPrimaryServing bool `protobuf:"varint,3,opt,name=is_primary_serving,json=isPrimaryServing,proto3" json:"is_primary_serving,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Workflow_ShardStream) Reset() { *m = Workflow_ShardStream{} } +func (m *Workflow_ShardStream) String() string { return proto.CompactTextString(m) } +func (*Workflow_ShardStream) ProtoMessage() {} +func (*Workflow_ShardStream) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{6, 2} +} + +func (m *Workflow_ShardStream) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Workflow_ShardStream.Unmarshal(m, b) +} +func (m *Workflow_ShardStream) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Workflow_ShardStream.Marshal(b, m, deterministic) +} +func (m *Workflow_ShardStream) XXX_Merge(src proto.Message) { + xxx_messageInfo_Workflow_ShardStream.Merge(m, src) +} +func (m *Workflow_ShardStream) XXX_Size() int { + return xxx_messageInfo_Workflow_ShardStream.Size(m) +} +func (m *Workflow_ShardStream) XXX_DiscardUnknown() { + xxx_messageInfo_Workflow_ShardStream.DiscardUnknown(m) +} + +var xxx_messageInfo_Workflow_ShardStream proto.InternalMessageInfo + +func (m *Workflow_ShardStream) GetStreams() []*Workflow_Stream { + if m != nil { + return m.Streams + } + return nil +} + +func (m *Workflow_ShardStream) GetTabletControls() []*topodata.Shard_TabletControl { + if m != nil { + return m.TabletControls + } + return nil +} + +func (m *Workflow_ShardStream) GetIsPrimaryServing() bool { + if m != nil { + return m.IsPrimaryServing + } + return false +} + +type Workflow_Stream struct { + Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Shard string `protobuf:"bytes,2,opt,name=shard,proto3" json:"shard,omitempty"` + Tablet *topodata.TabletAlias `protobuf:"bytes,3,opt,name=tablet,proto3" json:"tablet,omitempty"` + BinlogSource *binlogdata.BinlogSource `protobuf:"bytes,4,opt,name=binlog_source,json=binlogSource,proto3" json:"binlog_source,omitempty"` + Position string `protobuf:"bytes,5,opt,name=position,proto3" json:"position,omitempty"` + StopPosition string `protobuf:"bytes,6,opt,name=stop_position,json=stopPosition,proto3" json:"stop_position,omitempty"` + State string `protobuf:"bytes,7,opt,name=state,proto3" json:"state,omitempty"` + DbName string `protobuf:"bytes,8,opt,name=db_name,json=dbName,proto3" json:"db_name,omitempty"` + TransactionTimestamp *vttime.Time `protobuf:"bytes,9,opt,name=transaction_timestamp,json=transactionTimestamp,proto3" json:"transaction_timestamp,omitempty"` + TimeUpdated *vttime.Time `protobuf:"bytes,10,opt,name=time_updated,json=timeUpdated,proto3" json:"time_updated,omitempty"` + Message string `protobuf:"bytes,11,opt,name=message,proto3" json:"message,omitempty"` + CopyStates []*Workflow_Stream_CopyState `protobuf:"bytes,12,rep,name=copy_states,json=copyStates,proto3" json:"copy_states,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Workflow_Stream) Reset() { *m = Workflow_Stream{} } +func (m *Workflow_Stream) String() string { return proto.CompactTextString(m) } +func (*Workflow_Stream) ProtoMessage() {} +func (*Workflow_Stream) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{6, 3} +} + +func (m *Workflow_Stream) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Workflow_Stream.Unmarshal(m, b) +} +func (m *Workflow_Stream) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Workflow_Stream.Marshal(b, m, deterministic) +} +func (m *Workflow_Stream) XXX_Merge(src proto.Message) { + xxx_messageInfo_Workflow_Stream.Merge(m, src) +} +func (m *Workflow_Stream) XXX_Size() int { + return xxx_messageInfo_Workflow_Stream.Size(m) +} +func (m *Workflow_Stream) XXX_DiscardUnknown() { + xxx_messageInfo_Workflow_Stream.DiscardUnknown(m) +} + +var xxx_messageInfo_Workflow_Stream proto.InternalMessageInfo + +func (m *Workflow_Stream) GetId() int64 { + if m != nil { + return m.Id + } + return 0 +} + +func (m *Workflow_Stream) GetShard() string { + if m != nil { + return m.Shard + } + return "" +} + +func (m *Workflow_Stream) GetTablet() *topodata.TabletAlias { + if m != nil { + return m.Tablet + } + return nil +} + +func (m *Workflow_Stream) GetBinlogSource() *binlogdata.BinlogSource { + if m != nil { + return m.BinlogSource + } + return nil +} + +func (m *Workflow_Stream) GetPosition() string { + if m != nil { + return m.Position + } + return "" +} + +func (m *Workflow_Stream) GetStopPosition() string { + if m != nil { + return m.StopPosition + } + return "" +} + +func (m *Workflow_Stream) GetState() string { + if m != nil { + return m.State + } + return "" +} + +func (m *Workflow_Stream) GetDbName() string { + if m != nil { + return m.DbName + } + return "" +} + +func (m *Workflow_Stream) GetTransactionTimestamp() *vttime.Time { + if m != nil { + return m.TransactionTimestamp + } + return nil +} + +func (m *Workflow_Stream) GetTimeUpdated() *vttime.Time { + if m != nil { + return m.TimeUpdated + } + return nil +} + +func (m *Workflow_Stream) GetMessage() string { + if m != nil { + return m.Message + } + return "" +} + +func (m *Workflow_Stream) GetCopyStates() []*Workflow_Stream_CopyState { + if m != nil { + return m.CopyStates + } + return nil +} + +type Workflow_Stream_CopyState struct { + Table string `protobuf:"bytes,1,opt,name=table,proto3" json:"table,omitempty"` + LastPk string `protobuf:"bytes,2,opt,name=last_pk,json=lastPk,proto3" json:"last_pk,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Workflow_Stream_CopyState) Reset() { *m = Workflow_Stream_CopyState{} } +func (m *Workflow_Stream_CopyState) String() string { return proto.CompactTextString(m) } +func (*Workflow_Stream_CopyState) ProtoMessage() {} +func (*Workflow_Stream_CopyState) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{6, 3, 0} +} + +func (m *Workflow_Stream_CopyState) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Workflow_Stream_CopyState.Unmarshal(m, b) +} +func (m *Workflow_Stream_CopyState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Workflow_Stream_CopyState.Marshal(b, m, deterministic) +} +func (m *Workflow_Stream_CopyState) XXX_Merge(src proto.Message) { + xxx_messageInfo_Workflow_Stream_CopyState.Merge(m, src) +} +func (m *Workflow_Stream_CopyState) XXX_Size() int { + return xxx_messageInfo_Workflow_Stream_CopyState.Size(m) +} +func (m *Workflow_Stream_CopyState) XXX_DiscardUnknown() { + xxx_messageInfo_Workflow_Stream_CopyState.DiscardUnknown(m) +} + +var xxx_messageInfo_Workflow_Stream_CopyState proto.InternalMessageInfo + +func (m *Workflow_Stream_CopyState) GetTable() string { + if m != nil { + return m.Table + } + return "" +} + +func (m *Workflow_Stream_CopyState) GetLastPk() string { + if m != nil { + return m.LastPk + } + return "" +} + +type ChangeTabletTypeRequest struct { + TabletAlias *topodata.TabletAlias `protobuf:"bytes,1,opt,name=tablet_alias,json=tabletAlias,proto3" json:"tablet_alias,omitempty"` + DbType topodata.TabletType `protobuf:"varint,2,opt,name=db_type,json=dbType,proto3,enum=topodata.TabletType" json:"db_type,omitempty"` + DryRun bool `protobuf:"varint,3,opt,name=dry_run,json=dryRun,proto3" json:"dry_run,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ChangeTabletTypeRequest) Reset() { *m = ChangeTabletTypeRequest{} } +func (m *ChangeTabletTypeRequest) String() string { return proto.CompactTextString(m) } +func (*ChangeTabletTypeRequest) ProtoMessage() {} +func (*ChangeTabletTypeRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{7} +} + +func (m *ChangeTabletTypeRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ChangeTabletTypeRequest.Unmarshal(m, b) +} +func (m *ChangeTabletTypeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ChangeTabletTypeRequest.Marshal(b, m, deterministic) +} +func (m *ChangeTabletTypeRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ChangeTabletTypeRequest.Merge(m, src) +} +func (m *ChangeTabletTypeRequest) XXX_Size() int { + return xxx_messageInfo_ChangeTabletTypeRequest.Size(m) +} +func (m *ChangeTabletTypeRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ChangeTabletTypeRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ChangeTabletTypeRequest proto.InternalMessageInfo + +func (m *ChangeTabletTypeRequest) GetTabletAlias() *topodata.TabletAlias { + if m != nil { + return m.TabletAlias + } + return nil +} + +func (m *ChangeTabletTypeRequest) GetDbType() topodata.TabletType { + if m != nil { + return m.DbType + } + return topodata.TabletType_UNKNOWN +} + +func (m *ChangeTabletTypeRequest) GetDryRun() bool { + if m != nil { + return m.DryRun + } + return false +} + +type ChangeTabletTypeResponse struct { + BeforeTablet *topodata.Tablet `protobuf:"bytes,1,opt,name=before_tablet,json=beforeTablet,proto3" json:"before_tablet,omitempty"` + AfterTablet *topodata.Tablet `protobuf:"bytes,2,opt,name=after_tablet,json=afterTablet,proto3" json:"after_tablet,omitempty"` + WasDryRun bool `protobuf:"varint,3,opt,name=was_dry_run,json=wasDryRun,proto3" json:"was_dry_run,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ChangeTabletTypeResponse) Reset() { *m = ChangeTabletTypeResponse{} } +func (m *ChangeTabletTypeResponse) String() string { return proto.CompactTextString(m) } +func (*ChangeTabletTypeResponse) ProtoMessage() {} +func (*ChangeTabletTypeResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{8} +} + +func (m *ChangeTabletTypeResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ChangeTabletTypeResponse.Unmarshal(m, b) +} +func (m *ChangeTabletTypeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ChangeTabletTypeResponse.Marshal(b, m, deterministic) +} +func (m *ChangeTabletTypeResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ChangeTabletTypeResponse.Merge(m, src) +} +func (m *ChangeTabletTypeResponse) XXX_Size() int { + return xxx_messageInfo_ChangeTabletTypeResponse.Size(m) +} +func (m *ChangeTabletTypeResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ChangeTabletTypeResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ChangeTabletTypeResponse proto.InternalMessageInfo + +func (m *ChangeTabletTypeResponse) GetBeforeTablet() *topodata.Tablet { + if m != nil { + return m.BeforeTablet + } + return nil +} + +func (m *ChangeTabletTypeResponse) GetAfterTablet() *topodata.Tablet { + if m != nil { + return m.AfterTablet + } + return nil +} + +func (m *ChangeTabletTypeResponse) GetWasDryRun() bool { + if m != nil { + return m.WasDryRun + } + return false +} + +type CreateKeyspaceRequest struct { + // Name is the name of the keyspace. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // Force proceeds with the request even if the keyspace already exists. + Force bool `protobuf:"varint,2,opt,name=force,proto3" json:"force,omitempty"` + // AllowEmptyVSchema allows a keyspace to be created with no vschema. + AllowEmptyVSchema bool `protobuf:"varint,3,opt,name=allow_empty_v_schema,json=allowEmptyVSchema,proto3" json:"allow_empty_v_schema,omitempty"` + // ShardingColumnName specifies the column to use for sharding operations. + ShardingColumnName string `protobuf:"bytes,4,opt,name=sharding_column_name,json=shardingColumnName,proto3" json:"sharding_column_name,omitempty"` + // ShardingColumnType specifies the type of the column to use for sharding + // operations. + ShardingColumnType topodata.KeyspaceIdType `protobuf:"varint,5,opt,name=sharding_column_type,json=shardingColumnType,proto3,enum=topodata.KeyspaceIdType" json:"sharding_column_type,omitempty"` + // ServedFroms specifies a set of db_type:keyspace pairs used to serve + // traffic for the keyspace. + ServedFroms []*topodata.Keyspace_ServedFrom `protobuf:"bytes,6,rep,name=served_froms,json=servedFroms,proto3" json:"served_froms,omitempty"` + // Type is the type of the keyspace to create. + Type topodata.KeyspaceType `protobuf:"varint,7,opt,name=type,proto3,enum=topodata.KeyspaceType" json:"type,omitempty"` + // BaseKeyspace specifies the base keyspace for SNAPSHOT keyspaces. It is + // required to create a SNAPSHOT keyspace. + BaseKeyspace string `protobuf:"bytes,8,opt,name=base_keyspace,json=baseKeyspace,proto3" json:"base_keyspace,omitempty"` + // SnapshotTime specifies the snapshot time for this keyspace. It is required + // to create a SNAPSHOT keyspace. + SnapshotTime *vttime.Time `protobuf:"bytes,9,opt,name=snapshot_time,json=snapshotTime,proto3" json:"snapshot_time,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CreateKeyspaceRequest) Reset() { *m = CreateKeyspaceRequest{} } +func (m *CreateKeyspaceRequest) String() string { return proto.CompactTextString(m) } +func (*CreateKeyspaceRequest) ProtoMessage() {} +func (*CreateKeyspaceRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{9} +} + +func (m *CreateKeyspaceRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_CreateKeyspaceRequest.Unmarshal(m, b) +} +func (m *CreateKeyspaceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_CreateKeyspaceRequest.Marshal(b, m, deterministic) +} +func (m *CreateKeyspaceRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_CreateKeyspaceRequest.Merge(m, src) +} +func (m *CreateKeyspaceRequest) XXX_Size() int { + return xxx_messageInfo_CreateKeyspaceRequest.Size(m) +} +func (m *CreateKeyspaceRequest) XXX_DiscardUnknown() { + xxx_messageInfo_CreateKeyspaceRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_CreateKeyspaceRequest proto.InternalMessageInfo + +func (m *CreateKeyspaceRequest) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *CreateKeyspaceRequest) GetForce() bool { + if m != nil { + return m.Force + } + return false +} + +func (m *CreateKeyspaceRequest) GetAllowEmptyVSchema() bool { + if m != nil { + return m.AllowEmptyVSchema + } + return false +} + +func (m *CreateKeyspaceRequest) GetShardingColumnName() string { + if m != nil { + return m.ShardingColumnName + } + return "" +} + +func (m *CreateKeyspaceRequest) GetShardingColumnType() topodata.KeyspaceIdType { + if m != nil { + return m.ShardingColumnType + } + return topodata.KeyspaceIdType_UNSET +} + +func (m *CreateKeyspaceRequest) GetServedFroms() []*topodata.Keyspace_ServedFrom { + if m != nil { + return m.ServedFroms + } + return nil +} + +func (m *CreateKeyspaceRequest) GetType() topodata.KeyspaceType { + if m != nil { + return m.Type + } + return topodata.KeyspaceType_NORMAL +} + +func (m *CreateKeyspaceRequest) GetBaseKeyspace() string { + if m != nil { + return m.BaseKeyspace + } + return "" +} + +func (m *CreateKeyspaceRequest) GetSnapshotTime() *vttime.Time { + if m != nil { + return m.SnapshotTime + } + return nil +} + +type CreateKeyspaceResponse struct { + // Keyspace is the newly-created keyspace. + Keyspace *Keyspace `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CreateKeyspaceResponse) Reset() { *m = CreateKeyspaceResponse{} } +func (m *CreateKeyspaceResponse) String() string { return proto.CompactTextString(m) } +func (*CreateKeyspaceResponse) ProtoMessage() {} +func (*CreateKeyspaceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{10} +} + +func (m *CreateKeyspaceResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_CreateKeyspaceResponse.Unmarshal(m, b) +} +func (m *CreateKeyspaceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_CreateKeyspaceResponse.Marshal(b, m, deterministic) +} +func (m *CreateKeyspaceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_CreateKeyspaceResponse.Merge(m, src) +} +func (m *CreateKeyspaceResponse) XXX_Size() int { + return xxx_messageInfo_CreateKeyspaceResponse.Size(m) +} +func (m *CreateKeyspaceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_CreateKeyspaceResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_CreateKeyspaceResponse proto.InternalMessageInfo + +func (m *CreateKeyspaceResponse) GetKeyspace() *Keyspace { + if m != nil { + return m.Keyspace + } + return nil +} + +type CreateShardRequest struct { + // Keyspace is the name of the keyspace to create the shard in. + Keyspace string `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + // ShardName is the name of the shard to create. E.g. "-" or "-80". + ShardName string `protobuf:"bytes,2,opt,name=shard_name,json=shardName,proto3" json:"shard_name,omitempty"` + // Force treats an attempt to create a shard that already exists as a + // non-error. + Force bool `protobuf:"varint,3,opt,name=force,proto3" json:"force,omitempty"` + // IncludeParent creates the parent keyspace as an empty BASE keyspace, if it + // doesn't already exist. + IncludeParent bool `protobuf:"varint,4,opt,name=include_parent,json=includeParent,proto3" json:"include_parent,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CreateShardRequest) Reset() { *m = CreateShardRequest{} } +func (m *CreateShardRequest) String() string { return proto.CompactTextString(m) } +func (*CreateShardRequest) ProtoMessage() {} +func (*CreateShardRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{11} +} + +func (m *CreateShardRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_CreateShardRequest.Unmarshal(m, b) +} +func (m *CreateShardRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_CreateShardRequest.Marshal(b, m, deterministic) +} +func (m *CreateShardRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_CreateShardRequest.Merge(m, src) +} +func (m *CreateShardRequest) XXX_Size() int { + return xxx_messageInfo_CreateShardRequest.Size(m) +} +func (m *CreateShardRequest) XXX_DiscardUnknown() { + xxx_messageInfo_CreateShardRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_CreateShardRequest proto.InternalMessageInfo + +func (m *CreateShardRequest) GetKeyspace() string { + if m != nil { + return m.Keyspace + } + return "" +} + +func (m *CreateShardRequest) GetShardName() string { + if m != nil { + return m.ShardName + } + return "" +} + +func (m *CreateShardRequest) GetForce() bool { + if m != nil { + return m.Force + } + return false +} + +func (m *CreateShardRequest) GetIncludeParent() bool { + if m != nil { + return m.IncludeParent + } + return false +} + +type CreateShardResponse struct { + // Keyspace is the created keyspace. It is set only if IncludeParent was + // specified in the request and the parent keyspace needed to be created. + Keyspace *Keyspace `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + // Shard is the newly-created shard object. + Shard *Shard `protobuf:"bytes,2,opt,name=shard,proto3" json:"shard,omitempty"` + // ShardAlreadyExists is set if Force was specified in the request and the + // shard already existed. + ShardAlreadyExists bool `protobuf:"varint,3,opt,name=shard_already_exists,json=shardAlreadyExists,proto3" json:"shard_already_exists,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CreateShardResponse) Reset() { *m = CreateShardResponse{} } +func (m *CreateShardResponse) String() string { return proto.CompactTextString(m) } +func (*CreateShardResponse) ProtoMessage() {} +func (*CreateShardResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{12} +} + +func (m *CreateShardResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_CreateShardResponse.Unmarshal(m, b) +} +func (m *CreateShardResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_CreateShardResponse.Marshal(b, m, deterministic) +} +func (m *CreateShardResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_CreateShardResponse.Merge(m, src) +} +func (m *CreateShardResponse) XXX_Size() int { + return xxx_messageInfo_CreateShardResponse.Size(m) +} +func (m *CreateShardResponse) XXX_DiscardUnknown() { + xxx_messageInfo_CreateShardResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_CreateShardResponse proto.InternalMessageInfo + +func (m *CreateShardResponse) GetKeyspace() *Keyspace { + if m != nil { + return m.Keyspace + } + return nil +} + +func (m *CreateShardResponse) GetShard() *Shard { + if m != nil { + return m.Shard + } + return nil +} + +func (m *CreateShardResponse) GetShardAlreadyExists() bool { + if m != nil { + return m.ShardAlreadyExists + } + return false +} + +type DeleteKeyspaceRequest struct { + // Keyspace is the name of the keyspace to delete. + Keyspace string `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + // Recursive causes all shards in the keyspace to be recursively deleted + // before deleting the keyspace. It is an error to call DeleteKeyspace on a + // non-empty keyspace without also specifying Recursive. + Recursive bool `protobuf:"varint,2,opt,name=recursive,proto3" json:"recursive,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DeleteKeyspaceRequest) Reset() { *m = DeleteKeyspaceRequest{} } +func (m *DeleteKeyspaceRequest) String() string { return proto.CompactTextString(m) } +func (*DeleteKeyspaceRequest) ProtoMessage() {} +func (*DeleteKeyspaceRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{13} +} + +func (m *DeleteKeyspaceRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DeleteKeyspaceRequest.Unmarshal(m, b) +} +func (m *DeleteKeyspaceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DeleteKeyspaceRequest.Marshal(b, m, deterministic) +} +func (m *DeleteKeyspaceRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_DeleteKeyspaceRequest.Merge(m, src) +} +func (m *DeleteKeyspaceRequest) XXX_Size() int { + return xxx_messageInfo_DeleteKeyspaceRequest.Size(m) +} +func (m *DeleteKeyspaceRequest) XXX_DiscardUnknown() { + xxx_messageInfo_DeleteKeyspaceRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_DeleteKeyspaceRequest proto.InternalMessageInfo + +func (m *DeleteKeyspaceRequest) GetKeyspace() string { + if m != nil { + return m.Keyspace + } + return "" +} + +func (m *DeleteKeyspaceRequest) GetRecursive() bool { + if m != nil { + return m.Recursive + } + return false +} + +type DeleteKeyspaceResponse struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DeleteKeyspaceResponse) Reset() { *m = DeleteKeyspaceResponse{} } +func (m *DeleteKeyspaceResponse) String() string { return proto.CompactTextString(m) } +func (*DeleteKeyspaceResponse) ProtoMessage() {} +func (*DeleteKeyspaceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{14} +} + +func (m *DeleteKeyspaceResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DeleteKeyspaceResponse.Unmarshal(m, b) +} +func (m *DeleteKeyspaceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DeleteKeyspaceResponse.Marshal(b, m, deterministic) +} +func (m *DeleteKeyspaceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_DeleteKeyspaceResponse.Merge(m, src) +} +func (m *DeleteKeyspaceResponse) XXX_Size() int { + return xxx_messageInfo_DeleteKeyspaceResponse.Size(m) +} +func (m *DeleteKeyspaceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_DeleteKeyspaceResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_DeleteKeyspaceResponse proto.InternalMessageInfo + +type DeleteShardsRequest struct { + // Shards is the list of shards to delete. The nested topodatapb.Shard field + // is not required for DeleteShard, but the Keyspace and Shard fields are. + Shards []*Shard `protobuf:"bytes,1,rep,name=shards,proto3" json:"shards,omitempty"` + // Recursive also deletes all tablets belonging to the shard(s). It is an + // error to call DeleteShard on a non-empty shard without also specificying + // Recursive. + Recursive bool `protobuf:"varint,2,opt,name=recursive,proto3" json:"recursive,omitempty"` + // EvenIfServing allows a shard to be deleted even if it is serving, which is + // normally an error. Use with caution. + EvenIfServing bool `protobuf:"varint,4,opt,name=even_if_serving,json=evenIfServing,proto3" json:"even_if_serving,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DeleteShardsRequest) Reset() { *m = DeleteShardsRequest{} } +func (m *DeleteShardsRequest) String() string { return proto.CompactTextString(m) } +func (*DeleteShardsRequest) ProtoMessage() {} +func (*DeleteShardsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{15} +} + +func (m *DeleteShardsRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DeleteShardsRequest.Unmarshal(m, b) +} +func (m *DeleteShardsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DeleteShardsRequest.Marshal(b, m, deterministic) +} +func (m *DeleteShardsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_DeleteShardsRequest.Merge(m, src) +} +func (m *DeleteShardsRequest) XXX_Size() int { + return xxx_messageInfo_DeleteShardsRequest.Size(m) +} +func (m *DeleteShardsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_DeleteShardsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_DeleteShardsRequest proto.InternalMessageInfo + +func (m *DeleteShardsRequest) GetShards() []*Shard { + if m != nil { + return m.Shards + } + return nil +} + +func (m *DeleteShardsRequest) GetRecursive() bool { + if m != nil { + return m.Recursive + } + return false +} + +func (m *DeleteShardsRequest) GetEvenIfServing() bool { + if m != nil { + return m.EvenIfServing + } + return false +} + +type DeleteShardsResponse struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DeleteShardsResponse) Reset() { *m = DeleteShardsResponse{} } +func (m *DeleteShardsResponse) String() string { return proto.CompactTextString(m) } +func (*DeleteShardsResponse) ProtoMessage() {} +func (*DeleteShardsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{16} +} + +func (m *DeleteShardsResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DeleteShardsResponse.Unmarshal(m, b) +} +func (m *DeleteShardsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DeleteShardsResponse.Marshal(b, m, deterministic) +} +func (m *DeleteShardsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_DeleteShardsResponse.Merge(m, src) +} +func (m *DeleteShardsResponse) XXX_Size() int { + return xxx_messageInfo_DeleteShardsResponse.Size(m) +} +func (m *DeleteShardsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_DeleteShardsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_DeleteShardsResponse proto.InternalMessageInfo + +type DeleteTabletsRequest struct { + // TabletAliases is the list of tablets to delete. + TabletAliases []*topodata.TabletAlias `protobuf:"bytes,1,rep,name=tablet_aliases,json=tabletAliases,proto3" json:"tablet_aliases,omitempty"` + // AllowPrimary allows for the master/primary tablet of a shard to be deleted. + // Use with caution. + AllowPrimary bool `protobuf:"varint,2,opt,name=allow_primary,json=allowPrimary,proto3" json:"allow_primary,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DeleteTabletsRequest) Reset() { *m = DeleteTabletsRequest{} } +func (m *DeleteTabletsRequest) String() string { return proto.CompactTextString(m) } +func (*DeleteTabletsRequest) ProtoMessage() {} +func (*DeleteTabletsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{17} +} + +func (m *DeleteTabletsRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DeleteTabletsRequest.Unmarshal(m, b) +} +func (m *DeleteTabletsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DeleteTabletsRequest.Marshal(b, m, deterministic) +} +func (m *DeleteTabletsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_DeleteTabletsRequest.Merge(m, src) +} +func (m *DeleteTabletsRequest) XXX_Size() int { + return xxx_messageInfo_DeleteTabletsRequest.Size(m) +} +func (m *DeleteTabletsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_DeleteTabletsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_DeleteTabletsRequest proto.InternalMessageInfo + +func (m *DeleteTabletsRequest) GetTabletAliases() []*topodata.TabletAlias { + if m != nil { + return m.TabletAliases + } + return nil +} + +func (m *DeleteTabletsRequest) GetAllowPrimary() bool { + if m != nil { + return m.AllowPrimary + } + return false +} + +type DeleteTabletsResponse struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DeleteTabletsResponse) Reset() { *m = DeleteTabletsResponse{} } +func (m *DeleteTabletsResponse) String() string { return proto.CompactTextString(m) } +func (*DeleteTabletsResponse) ProtoMessage() {} +func (*DeleteTabletsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{18} +} + +func (m *DeleteTabletsResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DeleteTabletsResponse.Unmarshal(m, b) +} +func (m *DeleteTabletsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DeleteTabletsResponse.Marshal(b, m, deterministic) +} +func (m *DeleteTabletsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_DeleteTabletsResponse.Merge(m, src) +} +func (m *DeleteTabletsResponse) XXX_Size() int { + return xxx_messageInfo_DeleteTabletsResponse.Size(m) +} +func (m *DeleteTabletsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_DeleteTabletsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_DeleteTabletsResponse proto.InternalMessageInfo + +type FindAllShardsInKeyspaceRequest struct { + Keyspace string `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *FindAllShardsInKeyspaceRequest) Reset() { *m = FindAllShardsInKeyspaceRequest{} } +func (m *FindAllShardsInKeyspaceRequest) String() string { return proto.CompactTextString(m) } +func (*FindAllShardsInKeyspaceRequest) ProtoMessage() {} +func (*FindAllShardsInKeyspaceRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{19} +} + +func (m *FindAllShardsInKeyspaceRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_FindAllShardsInKeyspaceRequest.Unmarshal(m, b) +} +func (m *FindAllShardsInKeyspaceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_FindAllShardsInKeyspaceRequest.Marshal(b, m, deterministic) +} +func (m *FindAllShardsInKeyspaceRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_FindAllShardsInKeyspaceRequest.Merge(m, src) +} +func (m *FindAllShardsInKeyspaceRequest) XXX_Size() int { + return xxx_messageInfo_FindAllShardsInKeyspaceRequest.Size(m) +} +func (m *FindAllShardsInKeyspaceRequest) XXX_DiscardUnknown() { + xxx_messageInfo_FindAllShardsInKeyspaceRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_FindAllShardsInKeyspaceRequest proto.InternalMessageInfo + +func (m *FindAllShardsInKeyspaceRequest) GetKeyspace() string { + if m != nil { + return m.Keyspace + } + return "" +} + +type FindAllShardsInKeyspaceResponse struct { + Shards map[string]*Shard `protobuf:"bytes,1,rep,name=shards,proto3" json:"shards,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *FindAllShardsInKeyspaceResponse) Reset() { *m = FindAllShardsInKeyspaceResponse{} } +func (m *FindAllShardsInKeyspaceResponse) String() string { return proto.CompactTextString(m) } +func (*FindAllShardsInKeyspaceResponse) ProtoMessage() {} +func (*FindAllShardsInKeyspaceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{20} +} + +func (m *FindAllShardsInKeyspaceResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_FindAllShardsInKeyspaceResponse.Unmarshal(m, b) +} +func (m *FindAllShardsInKeyspaceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_FindAllShardsInKeyspaceResponse.Marshal(b, m, deterministic) +} +func (m *FindAllShardsInKeyspaceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_FindAllShardsInKeyspaceResponse.Merge(m, src) +} +func (m *FindAllShardsInKeyspaceResponse) XXX_Size() int { + return xxx_messageInfo_FindAllShardsInKeyspaceResponse.Size(m) +} +func (m *FindAllShardsInKeyspaceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_FindAllShardsInKeyspaceResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_FindAllShardsInKeyspaceResponse proto.InternalMessageInfo + +func (m *FindAllShardsInKeyspaceResponse) GetShards() map[string]*Shard { + if m != nil { + return m.Shards + } + return nil +} + +type GetBackupsRequest struct { + Keyspace string `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + Shard string `protobuf:"bytes,2,opt,name=shard,proto3" json:"shard,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetBackupsRequest) Reset() { *m = GetBackupsRequest{} } +func (m *GetBackupsRequest) String() string { return proto.CompactTextString(m) } +func (*GetBackupsRequest) ProtoMessage() {} +func (*GetBackupsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{21} +} + +func (m *GetBackupsRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetBackupsRequest.Unmarshal(m, b) +} +func (m *GetBackupsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetBackupsRequest.Marshal(b, m, deterministic) +} +func (m *GetBackupsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetBackupsRequest.Merge(m, src) +} +func (m *GetBackupsRequest) XXX_Size() int { + return xxx_messageInfo_GetBackupsRequest.Size(m) +} +func (m *GetBackupsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetBackupsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetBackupsRequest proto.InternalMessageInfo + +func (m *GetBackupsRequest) GetKeyspace() string { + if m != nil { + return m.Keyspace + } + return "" +} + +func (m *GetBackupsRequest) GetShard() string { + if m != nil { + return m.Shard + } + return "" +} + +type GetBackupsResponse struct { + Backups []*mysqlctl.BackupInfo `protobuf:"bytes,1,rep,name=backups,proto3" json:"backups,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetBackupsResponse) Reset() { *m = GetBackupsResponse{} } +func (m *GetBackupsResponse) String() string { return proto.CompactTextString(m) } +func (*GetBackupsResponse) ProtoMessage() {} +func (*GetBackupsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{22} +} + +func (m *GetBackupsResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetBackupsResponse.Unmarshal(m, b) +} +func (m *GetBackupsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetBackupsResponse.Marshal(b, m, deterministic) +} +func (m *GetBackupsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetBackupsResponse.Merge(m, src) +} +func (m *GetBackupsResponse) XXX_Size() int { + return xxx_messageInfo_GetBackupsResponse.Size(m) +} +func (m *GetBackupsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetBackupsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetBackupsResponse proto.InternalMessageInfo + +func (m *GetBackupsResponse) GetBackups() []*mysqlctl.BackupInfo { + if m != nil { + return m.Backups + } + return nil +} + +type GetCellInfoNamesRequest struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetCellInfoNamesRequest) Reset() { *m = GetCellInfoNamesRequest{} } +func (m *GetCellInfoNamesRequest) String() string { return proto.CompactTextString(m) } +func (*GetCellInfoNamesRequest) ProtoMessage() {} +func (*GetCellInfoNamesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{23} +} + +func (m *GetCellInfoNamesRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetCellInfoNamesRequest.Unmarshal(m, b) +} +func (m *GetCellInfoNamesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetCellInfoNamesRequest.Marshal(b, m, deterministic) +} +func (m *GetCellInfoNamesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetCellInfoNamesRequest.Merge(m, src) +} +func (m *GetCellInfoNamesRequest) XXX_Size() int { + return xxx_messageInfo_GetCellInfoNamesRequest.Size(m) +} +func (m *GetCellInfoNamesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetCellInfoNamesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetCellInfoNamesRequest proto.InternalMessageInfo + +type GetCellInfoNamesResponse struct { + Names []string `protobuf:"bytes,1,rep,name=names,proto3" json:"names,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetCellInfoNamesResponse) Reset() { *m = GetCellInfoNamesResponse{} } +func (m *GetCellInfoNamesResponse) String() string { return proto.CompactTextString(m) } +func (*GetCellInfoNamesResponse) ProtoMessage() {} +func (*GetCellInfoNamesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{24} +} + +func (m *GetCellInfoNamesResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetCellInfoNamesResponse.Unmarshal(m, b) +} +func (m *GetCellInfoNamesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetCellInfoNamesResponse.Marshal(b, m, deterministic) +} +func (m *GetCellInfoNamesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetCellInfoNamesResponse.Merge(m, src) +} +func (m *GetCellInfoNamesResponse) XXX_Size() int { + return xxx_messageInfo_GetCellInfoNamesResponse.Size(m) +} +func (m *GetCellInfoNamesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetCellInfoNamesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetCellInfoNamesResponse proto.InternalMessageInfo + +func (m *GetCellInfoNamesResponse) GetNames() []string { + if m != nil { + return m.Names + } + return nil +} + +type GetCellInfoRequest struct { + Cell string `protobuf:"bytes,1,opt,name=cell,proto3" json:"cell,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetCellInfoRequest) Reset() { *m = GetCellInfoRequest{} } +func (m *GetCellInfoRequest) String() string { return proto.CompactTextString(m) } +func (*GetCellInfoRequest) ProtoMessage() {} +func (*GetCellInfoRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{25} +} + +func (m *GetCellInfoRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetCellInfoRequest.Unmarshal(m, b) +} +func (m *GetCellInfoRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetCellInfoRequest.Marshal(b, m, deterministic) +} +func (m *GetCellInfoRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetCellInfoRequest.Merge(m, src) +} +func (m *GetCellInfoRequest) XXX_Size() int { + return xxx_messageInfo_GetCellInfoRequest.Size(m) +} +func (m *GetCellInfoRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetCellInfoRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetCellInfoRequest proto.InternalMessageInfo + +func (m *GetCellInfoRequest) GetCell() string { + if m != nil { + return m.Cell + } + return "" +} + +type GetCellInfoResponse struct { + CellInfo *topodata.CellInfo `protobuf:"bytes,1,opt,name=cell_info,json=cellInfo,proto3" json:"cell_info,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetCellInfoResponse) Reset() { *m = GetCellInfoResponse{} } +func (m *GetCellInfoResponse) String() string { return proto.CompactTextString(m) } +func (*GetCellInfoResponse) ProtoMessage() {} +func (*GetCellInfoResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{26} +} + +func (m *GetCellInfoResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetCellInfoResponse.Unmarshal(m, b) +} +func (m *GetCellInfoResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetCellInfoResponse.Marshal(b, m, deterministic) +} +func (m *GetCellInfoResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetCellInfoResponse.Merge(m, src) +} +func (m *GetCellInfoResponse) XXX_Size() int { + return xxx_messageInfo_GetCellInfoResponse.Size(m) +} +func (m *GetCellInfoResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetCellInfoResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetCellInfoResponse proto.InternalMessageInfo + +func (m *GetCellInfoResponse) GetCellInfo() *topodata.CellInfo { + if m != nil { + return m.CellInfo + } + return nil +} + +type GetCellsAliasesRequest struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetCellsAliasesRequest) Reset() { *m = GetCellsAliasesRequest{} } +func (m *GetCellsAliasesRequest) String() string { return proto.CompactTextString(m) } +func (*GetCellsAliasesRequest) ProtoMessage() {} +func (*GetCellsAliasesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{27} +} + +func (m *GetCellsAliasesRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetCellsAliasesRequest.Unmarshal(m, b) +} +func (m *GetCellsAliasesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetCellsAliasesRequest.Marshal(b, m, deterministic) +} +func (m *GetCellsAliasesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetCellsAliasesRequest.Merge(m, src) +} +func (m *GetCellsAliasesRequest) XXX_Size() int { + return xxx_messageInfo_GetCellsAliasesRequest.Size(m) +} +func (m *GetCellsAliasesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetCellsAliasesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetCellsAliasesRequest proto.InternalMessageInfo + +type GetCellsAliasesResponse struct { + Aliases map[string]*topodata.CellsAlias `protobuf:"bytes,1,rep,name=aliases,proto3" json:"aliases,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetCellsAliasesResponse) Reset() { *m = GetCellsAliasesResponse{} } +func (m *GetCellsAliasesResponse) String() string { return proto.CompactTextString(m) } +func (*GetCellsAliasesResponse) ProtoMessage() {} +func (*GetCellsAliasesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{28} +} + +func (m *GetCellsAliasesResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetCellsAliasesResponse.Unmarshal(m, b) +} +func (m *GetCellsAliasesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetCellsAliasesResponse.Marshal(b, m, deterministic) +} +func (m *GetCellsAliasesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetCellsAliasesResponse.Merge(m, src) +} +func (m *GetCellsAliasesResponse) XXX_Size() int { + return xxx_messageInfo_GetCellsAliasesResponse.Size(m) +} +func (m *GetCellsAliasesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetCellsAliasesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetCellsAliasesResponse proto.InternalMessageInfo + +func (m *GetCellsAliasesResponse) GetAliases() map[string]*topodata.CellsAlias { + if m != nil { + return m.Aliases + } + return nil +} + +type GetKeyspacesRequest struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetKeyspacesRequest) Reset() { *m = GetKeyspacesRequest{} } +func (m *GetKeyspacesRequest) String() string { return proto.CompactTextString(m) } +func (*GetKeyspacesRequest) ProtoMessage() {} +func (*GetKeyspacesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{29} +} + +func (m *GetKeyspacesRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetKeyspacesRequest.Unmarshal(m, b) +} +func (m *GetKeyspacesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetKeyspacesRequest.Marshal(b, m, deterministic) +} +func (m *GetKeyspacesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetKeyspacesRequest.Merge(m, src) +} +func (m *GetKeyspacesRequest) XXX_Size() int { + return xxx_messageInfo_GetKeyspacesRequest.Size(m) +} +func (m *GetKeyspacesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetKeyspacesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetKeyspacesRequest proto.InternalMessageInfo + +type GetKeyspacesResponse struct { + Keyspaces []*Keyspace `protobuf:"bytes,1,rep,name=keyspaces,proto3" json:"keyspaces,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetKeyspacesResponse) Reset() { *m = GetKeyspacesResponse{} } +func (m *GetKeyspacesResponse) String() string { return proto.CompactTextString(m) } +func (*GetKeyspacesResponse) ProtoMessage() {} +func (*GetKeyspacesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{30} +} + +func (m *GetKeyspacesResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetKeyspacesResponse.Unmarshal(m, b) +} +func (m *GetKeyspacesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetKeyspacesResponse.Marshal(b, m, deterministic) +} +func (m *GetKeyspacesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetKeyspacesResponse.Merge(m, src) +} +func (m *GetKeyspacesResponse) XXX_Size() int { + return xxx_messageInfo_GetKeyspacesResponse.Size(m) +} +func (m *GetKeyspacesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetKeyspacesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetKeyspacesResponse proto.InternalMessageInfo + +func (m *GetKeyspacesResponse) GetKeyspaces() []*Keyspace { + if m != nil { + return m.Keyspaces + } + return nil +} + +type GetKeyspaceRequest struct { + Keyspace string `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetKeyspaceRequest) Reset() { *m = GetKeyspaceRequest{} } +func (m *GetKeyspaceRequest) String() string { return proto.CompactTextString(m) } +func (*GetKeyspaceRequest) ProtoMessage() {} +func (*GetKeyspaceRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{31} +} + +func (m *GetKeyspaceRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetKeyspaceRequest.Unmarshal(m, b) +} +func (m *GetKeyspaceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetKeyspaceRequest.Marshal(b, m, deterministic) +} +func (m *GetKeyspaceRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetKeyspaceRequest.Merge(m, src) +} +func (m *GetKeyspaceRequest) XXX_Size() int { + return xxx_messageInfo_GetKeyspaceRequest.Size(m) +} +func (m *GetKeyspaceRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetKeyspaceRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetKeyspaceRequest proto.InternalMessageInfo + +func (m *GetKeyspaceRequest) GetKeyspace() string { + if m != nil { + return m.Keyspace + } + return "" +} + +type GetKeyspaceResponse struct { + Keyspace *Keyspace `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetKeyspaceResponse) Reset() { *m = GetKeyspaceResponse{} } +func (m *GetKeyspaceResponse) String() string { return proto.CompactTextString(m) } +func (*GetKeyspaceResponse) ProtoMessage() {} +func (*GetKeyspaceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{32} +} + +func (m *GetKeyspaceResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetKeyspaceResponse.Unmarshal(m, b) +} +func (m *GetKeyspaceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetKeyspaceResponse.Marshal(b, m, deterministic) +} +func (m *GetKeyspaceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetKeyspaceResponse.Merge(m, src) +} +func (m *GetKeyspaceResponse) XXX_Size() int { + return xxx_messageInfo_GetKeyspaceResponse.Size(m) +} +func (m *GetKeyspaceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetKeyspaceResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetKeyspaceResponse proto.InternalMessageInfo + +func (m *GetKeyspaceResponse) GetKeyspace() *Keyspace { + if m != nil { + return m.Keyspace + } + return nil +} + +type GetSchemaRequest struct { + TabletAlias *topodata.TabletAlias `protobuf:"bytes,1,opt,name=tablet_alias,json=tabletAlias,proto3" json:"tablet_alias,omitempty"` + // Tables is a list of tables for which we should gather information. Each is + // either an exact match, or a regular expression of the form /regexp/. + Tables []string `protobuf:"bytes,2,rep,name=tables,proto3" json:"tables,omitempty"` + // ExcludeTables is a list of tables to exclude from the result. Each is + // either an exact match, or a regular expression of the form /regexp/. + ExcludeTables []string `protobuf:"bytes,3,rep,name=exclude_tables,json=excludeTables,proto3" json:"exclude_tables,omitempty"` + // IncludeViews specifies whether to include views in the result. + IncludeViews bool `protobuf:"varint,4,opt,name=include_views,json=includeViews,proto3" json:"include_views,omitempty"` + // TableNamesOnly specifies whether to limit the results to just table names, + // rather than full schema information for each table. + TableNamesOnly bool `protobuf:"varint,5,opt,name=table_names_only,json=tableNamesOnly,proto3" json:"table_names_only,omitempty"` + // TableSizesOnly specifies whether to limit the results to just table sizes, + // rather than full schema information for each table. It is ignored if + // TableNamesOnly is set to true. + TableSizesOnly bool `protobuf:"varint,6,opt,name=table_sizes_only,json=tableSizesOnly,proto3" json:"table_sizes_only,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetSchemaRequest) Reset() { *m = GetSchemaRequest{} } +func (m *GetSchemaRequest) String() string { return proto.CompactTextString(m) } +func (*GetSchemaRequest) ProtoMessage() {} +func (*GetSchemaRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{33} +} + +func (m *GetSchemaRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetSchemaRequest.Unmarshal(m, b) +} +func (m *GetSchemaRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetSchemaRequest.Marshal(b, m, deterministic) +} +func (m *GetSchemaRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetSchemaRequest.Merge(m, src) +} +func (m *GetSchemaRequest) XXX_Size() int { + return xxx_messageInfo_GetSchemaRequest.Size(m) +} +func (m *GetSchemaRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetSchemaRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetSchemaRequest proto.InternalMessageInfo + +func (m *GetSchemaRequest) GetTabletAlias() *topodata.TabletAlias { + if m != nil { + return m.TabletAlias + } + return nil +} + +func (m *GetSchemaRequest) GetTables() []string { + if m != nil { + return m.Tables + } + return nil +} + +func (m *GetSchemaRequest) GetExcludeTables() []string { + if m != nil { + return m.ExcludeTables + } + return nil +} + +func (m *GetSchemaRequest) GetIncludeViews() bool { + if m != nil { + return m.IncludeViews + } + return false +} + +func (m *GetSchemaRequest) GetTableNamesOnly() bool { + if m != nil { + return m.TableNamesOnly + } + return false +} + +func (m *GetSchemaRequest) GetTableSizesOnly() bool { + if m != nil { + return m.TableSizesOnly + } + return false +} + +type GetSchemaResponse struct { + Schema *tabletmanagerdata.SchemaDefinition `protobuf:"bytes,1,opt,name=schema,proto3" json:"schema,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetSchemaResponse) Reset() { *m = GetSchemaResponse{} } +func (m *GetSchemaResponse) String() string { return proto.CompactTextString(m) } +func (*GetSchemaResponse) ProtoMessage() {} +func (*GetSchemaResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{34} +} + +func (m *GetSchemaResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetSchemaResponse.Unmarshal(m, b) +} +func (m *GetSchemaResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetSchemaResponse.Marshal(b, m, deterministic) +} +func (m *GetSchemaResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetSchemaResponse.Merge(m, src) +} +func (m *GetSchemaResponse) XXX_Size() int { + return xxx_messageInfo_GetSchemaResponse.Size(m) +} +func (m *GetSchemaResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetSchemaResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetSchemaResponse proto.InternalMessageInfo + +func (m *GetSchemaResponse) GetSchema() *tabletmanagerdata.SchemaDefinition { + if m != nil { + return m.Schema + } + return nil +} + +type GetShardRequest struct { + Keyspace string `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + ShardName string `protobuf:"bytes,2,opt,name=shard_name,json=shardName,proto3" json:"shard_name,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetShardRequest) Reset() { *m = GetShardRequest{} } +func (m *GetShardRequest) String() string { return proto.CompactTextString(m) } +func (*GetShardRequest) ProtoMessage() {} +func (*GetShardRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{35} +} + +func (m *GetShardRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetShardRequest.Unmarshal(m, b) +} +func (m *GetShardRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetShardRequest.Marshal(b, m, deterministic) +} +func (m *GetShardRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetShardRequest.Merge(m, src) +} +func (m *GetShardRequest) XXX_Size() int { + return xxx_messageInfo_GetShardRequest.Size(m) +} +func (m *GetShardRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetShardRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetShardRequest proto.InternalMessageInfo + +func (m *GetShardRequest) GetKeyspace() string { + if m != nil { + return m.Keyspace + } + return "" +} + +func (m *GetShardRequest) GetShardName() string { + if m != nil { + return m.ShardName + } + return "" +} + +type GetShardResponse struct { + Shard *Shard `protobuf:"bytes,1,opt,name=shard,proto3" json:"shard,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetShardResponse) Reset() { *m = GetShardResponse{} } +func (m *GetShardResponse) String() string { return proto.CompactTextString(m) } +func (*GetShardResponse) ProtoMessage() {} +func (*GetShardResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{36} +} + +func (m *GetShardResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetShardResponse.Unmarshal(m, b) +} +func (m *GetShardResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetShardResponse.Marshal(b, m, deterministic) +} +func (m *GetShardResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetShardResponse.Merge(m, src) +} +func (m *GetShardResponse) XXX_Size() int { + return xxx_messageInfo_GetShardResponse.Size(m) +} +func (m *GetShardResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetShardResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetShardResponse proto.InternalMessageInfo + +func (m *GetShardResponse) GetShard() *Shard { + if m != nil { + return m.Shard + } + return nil +} + +type GetSrvVSchemaRequest struct { + Cell string `protobuf:"bytes,1,opt,name=cell,proto3" json:"cell,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetSrvVSchemaRequest) Reset() { *m = GetSrvVSchemaRequest{} } +func (m *GetSrvVSchemaRequest) String() string { return proto.CompactTextString(m) } +func (*GetSrvVSchemaRequest) ProtoMessage() {} +func (*GetSrvVSchemaRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{37} +} + +func (m *GetSrvVSchemaRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetSrvVSchemaRequest.Unmarshal(m, b) +} +func (m *GetSrvVSchemaRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetSrvVSchemaRequest.Marshal(b, m, deterministic) +} +func (m *GetSrvVSchemaRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetSrvVSchemaRequest.Merge(m, src) +} +func (m *GetSrvVSchemaRequest) XXX_Size() int { + return xxx_messageInfo_GetSrvVSchemaRequest.Size(m) +} +func (m *GetSrvVSchemaRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetSrvVSchemaRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetSrvVSchemaRequest proto.InternalMessageInfo + +func (m *GetSrvVSchemaRequest) GetCell() string { + if m != nil { + return m.Cell + } + return "" +} + +type GetSrvVSchemaResponse struct { + SrvVSchema *vschema.SrvVSchema `protobuf:"bytes,1,opt,name=srv_v_schema,json=srvVSchema,proto3" json:"srv_v_schema,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetSrvVSchemaResponse) Reset() { *m = GetSrvVSchemaResponse{} } +func (m *GetSrvVSchemaResponse) String() string { return proto.CompactTextString(m) } +func (*GetSrvVSchemaResponse) ProtoMessage() {} +func (*GetSrvVSchemaResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{38} +} + +func (m *GetSrvVSchemaResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetSrvVSchemaResponse.Unmarshal(m, b) +} +func (m *GetSrvVSchemaResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetSrvVSchemaResponse.Marshal(b, m, deterministic) +} +func (m *GetSrvVSchemaResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetSrvVSchemaResponse.Merge(m, src) +} +func (m *GetSrvVSchemaResponse) XXX_Size() int { + return xxx_messageInfo_GetSrvVSchemaResponse.Size(m) +} +func (m *GetSrvVSchemaResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetSrvVSchemaResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetSrvVSchemaResponse proto.InternalMessageInfo + +func (m *GetSrvVSchemaResponse) GetSrvVSchema() *vschema.SrvVSchema { + if m != nil { + return m.SrvVSchema + } + return nil +} + +type GetTabletRequest struct { + TabletAlias *topodata.TabletAlias `protobuf:"bytes,1,opt,name=tablet_alias,json=tabletAlias,proto3" json:"tablet_alias,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetTabletRequest) Reset() { *m = GetTabletRequest{} } +func (m *GetTabletRequest) String() string { return proto.CompactTextString(m) } +func (*GetTabletRequest) ProtoMessage() {} +func (*GetTabletRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{39} +} + +func (m *GetTabletRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetTabletRequest.Unmarshal(m, b) +} +func (m *GetTabletRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetTabletRequest.Marshal(b, m, deterministic) +} +func (m *GetTabletRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetTabletRequest.Merge(m, src) +} +func (m *GetTabletRequest) XXX_Size() int { + return xxx_messageInfo_GetTabletRequest.Size(m) +} +func (m *GetTabletRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetTabletRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetTabletRequest proto.InternalMessageInfo + +func (m *GetTabletRequest) GetTabletAlias() *topodata.TabletAlias { + if m != nil { + return m.TabletAlias + } + return nil +} + +type GetTabletResponse struct { + Tablet *topodata.Tablet `protobuf:"bytes,1,opt,name=tablet,proto3" json:"tablet,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetTabletResponse) Reset() { *m = GetTabletResponse{} } +func (m *GetTabletResponse) String() string { return proto.CompactTextString(m) } +func (*GetTabletResponse) ProtoMessage() {} +func (*GetTabletResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{40} +} + +func (m *GetTabletResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetTabletResponse.Unmarshal(m, b) +} +func (m *GetTabletResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetTabletResponse.Marshal(b, m, deterministic) +} +func (m *GetTabletResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetTabletResponse.Merge(m, src) +} +func (m *GetTabletResponse) XXX_Size() int { + return xxx_messageInfo_GetTabletResponse.Size(m) +} +func (m *GetTabletResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetTabletResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetTabletResponse proto.InternalMessageInfo + +func (m *GetTabletResponse) GetTablet() *topodata.Tablet { + if m != nil { + return m.Tablet + } + return nil +} + +type GetTabletsRequest struct { + // Keyspace is the name of the keyspace to return tablets for. Omit to return + // all tablets. + Keyspace string `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + // Shard is the name of the shard to return tablets for. This field is ignored + // if Keyspace is not set. + Shard string `protobuf:"bytes,2,opt,name=shard,proto3" json:"shard,omitempty"` + // Cells is an optional set of cells to return tablets for. + Cells []string `protobuf:"bytes,3,rep,name=cells,proto3" json:"cells,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetTabletsRequest) Reset() { *m = GetTabletsRequest{} } +func (m *GetTabletsRequest) String() string { return proto.CompactTextString(m) } +func (*GetTabletsRequest) ProtoMessage() {} +func (*GetTabletsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{41} +} + +func (m *GetTabletsRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetTabletsRequest.Unmarshal(m, b) +} +func (m *GetTabletsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetTabletsRequest.Marshal(b, m, deterministic) +} +func (m *GetTabletsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetTabletsRequest.Merge(m, src) +} +func (m *GetTabletsRequest) XXX_Size() int { + return xxx_messageInfo_GetTabletsRequest.Size(m) +} +func (m *GetTabletsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetTabletsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetTabletsRequest proto.InternalMessageInfo + +func (m *GetTabletsRequest) GetKeyspace() string { + if m != nil { + return m.Keyspace + } + return "" +} + +func (m *GetTabletsRequest) GetShard() string { + if m != nil { + return m.Shard + } + return "" +} + +func (m *GetTabletsRequest) GetCells() []string { + if m != nil { + return m.Cells + } + return nil +} + +type GetTabletsResponse struct { + Tablets []*topodata.Tablet `protobuf:"bytes,1,rep,name=tablets,proto3" json:"tablets,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetTabletsResponse) Reset() { *m = GetTabletsResponse{} } +func (m *GetTabletsResponse) String() string { return proto.CompactTextString(m) } +func (*GetTabletsResponse) ProtoMessage() {} +func (*GetTabletsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{42} +} + +func (m *GetTabletsResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetTabletsResponse.Unmarshal(m, b) +} +func (m *GetTabletsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetTabletsResponse.Marshal(b, m, deterministic) +} +func (m *GetTabletsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetTabletsResponse.Merge(m, src) +} +func (m *GetTabletsResponse) XXX_Size() int { + return xxx_messageInfo_GetTabletsResponse.Size(m) +} +func (m *GetTabletsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetTabletsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetTabletsResponse proto.InternalMessageInfo + +func (m *GetTabletsResponse) GetTablets() []*topodata.Tablet { + if m != nil { + return m.Tablets + } + return nil +} + +type GetVSchemaRequest struct { + Keyspace string `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetVSchemaRequest) Reset() { *m = GetVSchemaRequest{} } +func (m *GetVSchemaRequest) String() string { return proto.CompactTextString(m) } +func (*GetVSchemaRequest) ProtoMessage() {} +func (*GetVSchemaRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{43} +} + +func (m *GetVSchemaRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetVSchemaRequest.Unmarshal(m, b) +} +func (m *GetVSchemaRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetVSchemaRequest.Marshal(b, m, deterministic) +} +func (m *GetVSchemaRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetVSchemaRequest.Merge(m, src) +} +func (m *GetVSchemaRequest) XXX_Size() int { + return xxx_messageInfo_GetVSchemaRequest.Size(m) +} +func (m *GetVSchemaRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetVSchemaRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetVSchemaRequest proto.InternalMessageInfo + +func (m *GetVSchemaRequest) GetKeyspace() string { + if m != nil { + return m.Keyspace + } + return "" +} + +type GetVSchemaResponse struct { + VSchema *vschema.Keyspace `protobuf:"bytes,1,opt,name=v_schema,json=vSchema,proto3" json:"v_schema,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetVSchemaResponse) Reset() { *m = GetVSchemaResponse{} } +func (m *GetVSchemaResponse) String() string { return proto.CompactTextString(m) } +func (*GetVSchemaResponse) ProtoMessage() {} +func (*GetVSchemaResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{44} +} + +func (m *GetVSchemaResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetVSchemaResponse.Unmarshal(m, b) +} +func (m *GetVSchemaResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetVSchemaResponse.Marshal(b, m, deterministic) +} +func (m *GetVSchemaResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetVSchemaResponse.Merge(m, src) +} +func (m *GetVSchemaResponse) XXX_Size() int { + return xxx_messageInfo_GetVSchemaResponse.Size(m) +} +func (m *GetVSchemaResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetVSchemaResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetVSchemaResponse proto.InternalMessageInfo + +func (m *GetVSchemaResponse) GetVSchema() *vschema.Keyspace { + if m != nil { + return m.VSchema + } + return nil +} + +type GetWorkflowsRequest struct { + Keyspace string `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + ActiveOnly bool `protobuf:"varint,2,opt,name=active_only,json=activeOnly,proto3" json:"active_only,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetWorkflowsRequest) Reset() { *m = GetWorkflowsRequest{} } +func (m *GetWorkflowsRequest) String() string { return proto.CompactTextString(m) } +func (*GetWorkflowsRequest) ProtoMessage() {} +func (*GetWorkflowsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{45} +} + +func (m *GetWorkflowsRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetWorkflowsRequest.Unmarshal(m, b) +} +func (m *GetWorkflowsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetWorkflowsRequest.Marshal(b, m, deterministic) +} +func (m *GetWorkflowsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetWorkflowsRequest.Merge(m, src) +} +func (m *GetWorkflowsRequest) XXX_Size() int { + return xxx_messageInfo_GetWorkflowsRequest.Size(m) +} +func (m *GetWorkflowsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetWorkflowsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetWorkflowsRequest proto.InternalMessageInfo + +func (m *GetWorkflowsRequest) GetKeyspace() string { + if m != nil { + return m.Keyspace + } + return "" +} + +func (m *GetWorkflowsRequest) GetActiveOnly() bool { + if m != nil { + return m.ActiveOnly + } + return false +} + +type GetWorkflowsResponse struct { + Workflows []*Workflow `protobuf:"bytes,1,rep,name=workflows,proto3" json:"workflows,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetWorkflowsResponse) Reset() { *m = GetWorkflowsResponse{} } +func (m *GetWorkflowsResponse) String() string { return proto.CompactTextString(m) } +func (*GetWorkflowsResponse) ProtoMessage() {} +func (*GetWorkflowsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{46} +} + +func (m *GetWorkflowsResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetWorkflowsResponse.Unmarshal(m, b) +} +func (m *GetWorkflowsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetWorkflowsResponse.Marshal(b, m, deterministic) +} +func (m *GetWorkflowsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetWorkflowsResponse.Merge(m, src) +} +func (m *GetWorkflowsResponse) XXX_Size() int { + return xxx_messageInfo_GetWorkflowsResponse.Size(m) +} +func (m *GetWorkflowsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetWorkflowsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetWorkflowsResponse proto.InternalMessageInfo + +func (m *GetWorkflowsResponse) GetWorkflows() []*Workflow { + if m != nil { + return m.Workflows + } + return nil +} + +type RemoveKeyspaceCellRequest struct { + Keyspace string `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + Cell string `protobuf:"bytes,2,opt,name=cell,proto3" json:"cell,omitempty"` + // Force proceeds even if the cell's topology server cannot be reached. This + // should only be set if a cell has been shut down entirely, and the global + // topology data just needs to be updated. + Force bool `protobuf:"varint,3,opt,name=force,proto3" json:"force,omitempty"` + // Recursive also deletes all tablets in that cell belonging to the specified + // keyspace. + Recursive bool `protobuf:"varint,4,opt,name=recursive,proto3" json:"recursive,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RemoveKeyspaceCellRequest) Reset() { *m = RemoveKeyspaceCellRequest{} } +func (m *RemoveKeyspaceCellRequest) String() string { return proto.CompactTextString(m) } +func (*RemoveKeyspaceCellRequest) ProtoMessage() {} +func (*RemoveKeyspaceCellRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{47} +} + +func (m *RemoveKeyspaceCellRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_RemoveKeyspaceCellRequest.Unmarshal(m, b) +} +func (m *RemoveKeyspaceCellRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_RemoveKeyspaceCellRequest.Marshal(b, m, deterministic) +} +func (m *RemoveKeyspaceCellRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_RemoveKeyspaceCellRequest.Merge(m, src) +} +func (m *RemoveKeyspaceCellRequest) XXX_Size() int { + return xxx_messageInfo_RemoveKeyspaceCellRequest.Size(m) +} +func (m *RemoveKeyspaceCellRequest) XXX_DiscardUnknown() { + xxx_messageInfo_RemoveKeyspaceCellRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_RemoveKeyspaceCellRequest proto.InternalMessageInfo + +func (m *RemoveKeyspaceCellRequest) GetKeyspace() string { + if m != nil { + return m.Keyspace + } + return "" +} + +func (m *RemoveKeyspaceCellRequest) GetCell() string { + if m != nil { + return m.Cell + } + return "" +} + +func (m *RemoveKeyspaceCellRequest) GetForce() bool { + if m != nil { + return m.Force + } + return false +} + +func (m *RemoveKeyspaceCellRequest) GetRecursive() bool { + if m != nil { + return m.Recursive + } + return false +} + +type RemoveKeyspaceCellResponse struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RemoveKeyspaceCellResponse) Reset() { *m = RemoveKeyspaceCellResponse{} } +func (m *RemoveKeyspaceCellResponse) String() string { return proto.CompactTextString(m) } +func (*RemoveKeyspaceCellResponse) ProtoMessage() {} +func (*RemoveKeyspaceCellResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{48} +} + +func (m *RemoveKeyspaceCellResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_RemoveKeyspaceCellResponse.Unmarshal(m, b) +} +func (m *RemoveKeyspaceCellResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_RemoveKeyspaceCellResponse.Marshal(b, m, deterministic) +} +func (m *RemoveKeyspaceCellResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_RemoveKeyspaceCellResponse.Merge(m, src) +} +func (m *RemoveKeyspaceCellResponse) XXX_Size() int { + return xxx_messageInfo_RemoveKeyspaceCellResponse.Size(m) +} +func (m *RemoveKeyspaceCellResponse) XXX_DiscardUnknown() { + xxx_messageInfo_RemoveKeyspaceCellResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_RemoveKeyspaceCellResponse proto.InternalMessageInfo + +type RemoveShardCellRequest struct { + Keyspace string `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + ShardName string `protobuf:"bytes,2,opt,name=shard_name,json=shardName,proto3" json:"shard_name,omitempty"` + Cell string `protobuf:"bytes,3,opt,name=cell,proto3" json:"cell,omitempty"` + // Force proceeds even if the cell's topology server cannot be reached. This + // should only be set if a cell has been shut down entirely, and the global + // topology data just needs to be updated. + Force bool `protobuf:"varint,4,opt,name=force,proto3" json:"force,omitempty"` + // Recursive also deletes all tablets in that cell belonging to the specified + // keyspace and shard. + Recursive bool `protobuf:"varint,5,opt,name=recursive,proto3" json:"recursive,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RemoveShardCellRequest) Reset() { *m = RemoveShardCellRequest{} } +func (m *RemoveShardCellRequest) String() string { return proto.CompactTextString(m) } +func (*RemoveShardCellRequest) ProtoMessage() {} +func (*RemoveShardCellRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{49} +} + +func (m *RemoveShardCellRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_RemoveShardCellRequest.Unmarshal(m, b) +} +func (m *RemoveShardCellRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_RemoveShardCellRequest.Marshal(b, m, deterministic) +} +func (m *RemoveShardCellRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_RemoveShardCellRequest.Merge(m, src) +} +func (m *RemoveShardCellRequest) XXX_Size() int { + return xxx_messageInfo_RemoveShardCellRequest.Size(m) +} +func (m *RemoveShardCellRequest) XXX_DiscardUnknown() { + xxx_messageInfo_RemoveShardCellRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_RemoveShardCellRequest proto.InternalMessageInfo + +func (m *RemoveShardCellRequest) GetKeyspace() string { + if m != nil { + return m.Keyspace + } + return "" +} + +func (m *RemoveShardCellRequest) GetShardName() string { + if m != nil { + return m.ShardName + } + return "" +} + +func (m *RemoveShardCellRequest) GetCell() string { + if m != nil { + return m.Cell + } + return "" +} + +func (m *RemoveShardCellRequest) GetForce() bool { + if m != nil { + return m.Force + } + return false +} + +func (m *RemoveShardCellRequest) GetRecursive() bool { + if m != nil { + return m.Recursive + } + return false +} + +type RemoveShardCellResponse struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RemoveShardCellResponse) Reset() { *m = RemoveShardCellResponse{} } +func (m *RemoveShardCellResponse) String() string { return proto.CompactTextString(m) } +func (*RemoveShardCellResponse) ProtoMessage() {} +func (*RemoveShardCellResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{50} +} + +func (m *RemoveShardCellResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_RemoveShardCellResponse.Unmarshal(m, b) +} +func (m *RemoveShardCellResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_RemoveShardCellResponse.Marshal(b, m, deterministic) +} +func (m *RemoveShardCellResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_RemoveShardCellResponse.Merge(m, src) +} +func (m *RemoveShardCellResponse) XXX_Size() int { + return xxx_messageInfo_RemoveShardCellResponse.Size(m) +} +func (m *RemoveShardCellResponse) XXX_DiscardUnknown() { + xxx_messageInfo_RemoveShardCellResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_RemoveShardCellResponse proto.InternalMessageInfo + +type ReparentTabletRequest struct { + // Tablet is the alias of the tablet that should be reparented under the + // current shard primary. + Tablet *topodata.TabletAlias `protobuf:"bytes,1,opt,name=tablet,proto3" json:"tablet,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ReparentTabletRequest) Reset() { *m = ReparentTabletRequest{} } +func (m *ReparentTabletRequest) String() string { return proto.CompactTextString(m) } +func (*ReparentTabletRequest) ProtoMessage() {} +func (*ReparentTabletRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{51} +} + +func (m *ReparentTabletRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ReparentTabletRequest.Unmarshal(m, b) +} +func (m *ReparentTabletRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ReparentTabletRequest.Marshal(b, m, deterministic) +} +func (m *ReparentTabletRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ReparentTabletRequest.Merge(m, src) +} +func (m *ReparentTabletRequest) XXX_Size() int { + return xxx_messageInfo_ReparentTabletRequest.Size(m) +} +func (m *ReparentTabletRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ReparentTabletRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ReparentTabletRequest proto.InternalMessageInfo + +func (m *ReparentTabletRequest) GetTablet() *topodata.TabletAlias { + if m != nil { + return m.Tablet + } + return nil +} + +type ReparentTabletResponse struct { + // Keyspace is the name of the keyspace the tablet was reparented in. + Keyspace string `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + // Shard is the name of the shard the tablet was reparented in. + Shard string `protobuf:"bytes,2,opt,name=shard,proto3" json:"shard,omitempty"` + // Primary is the alias of the tablet that the tablet was reparented under. + Primary *topodata.TabletAlias `protobuf:"bytes,3,opt,name=primary,proto3" json:"primary,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ReparentTabletResponse) Reset() { *m = ReparentTabletResponse{} } +func (m *ReparentTabletResponse) String() string { return proto.CompactTextString(m) } +func (*ReparentTabletResponse) ProtoMessage() {} +func (*ReparentTabletResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{52} +} + +func (m *ReparentTabletResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ReparentTabletResponse.Unmarshal(m, b) +} +func (m *ReparentTabletResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ReparentTabletResponse.Marshal(b, m, deterministic) +} +func (m *ReparentTabletResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ReparentTabletResponse.Merge(m, src) +} +func (m *ReparentTabletResponse) XXX_Size() int { + return xxx_messageInfo_ReparentTabletResponse.Size(m) +} +func (m *ReparentTabletResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ReparentTabletResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ReparentTabletResponse proto.InternalMessageInfo + +func (m *ReparentTabletResponse) GetKeyspace() string { + if m != nil { + return m.Keyspace + } + return "" +} + +func (m *ReparentTabletResponse) GetShard() string { + if m != nil { + return m.Shard + } + return "" +} + +func (m *ReparentTabletResponse) GetPrimary() *topodata.TabletAlias { + if m != nil { + return m.Primary + } + return nil +} + +type TabletExternallyReparentedRequest struct { + // Tablet is the alias of the tablet that was promoted externally and should + // be updated to the shard primary in the topo. + Tablet *topodata.TabletAlias `protobuf:"bytes,1,opt,name=tablet,proto3" json:"tablet,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *TabletExternallyReparentedRequest) Reset() { *m = TabletExternallyReparentedRequest{} } +func (m *TabletExternallyReparentedRequest) String() string { return proto.CompactTextString(m) } +func (*TabletExternallyReparentedRequest) ProtoMessage() {} +func (*TabletExternallyReparentedRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{53} +} + +func (m *TabletExternallyReparentedRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_TabletExternallyReparentedRequest.Unmarshal(m, b) +} +func (m *TabletExternallyReparentedRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_TabletExternallyReparentedRequest.Marshal(b, m, deterministic) +} +func (m *TabletExternallyReparentedRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_TabletExternallyReparentedRequest.Merge(m, src) +} +func (m *TabletExternallyReparentedRequest) XXX_Size() int { + return xxx_messageInfo_TabletExternallyReparentedRequest.Size(m) +} +func (m *TabletExternallyReparentedRequest) XXX_DiscardUnknown() { + xxx_messageInfo_TabletExternallyReparentedRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_TabletExternallyReparentedRequest proto.InternalMessageInfo + +func (m *TabletExternallyReparentedRequest) GetTablet() *topodata.TabletAlias { + if m != nil { + return m.Tablet + } + return nil +} + +type TabletExternallyReparentedResponse struct { + Keyspace string `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` + Shard string `protobuf:"bytes,2,opt,name=shard,proto3" json:"shard,omitempty"` + NewPrimary *topodata.TabletAlias `protobuf:"bytes,3,opt,name=new_primary,json=newPrimary,proto3" json:"new_primary,omitempty"` + OldPrimary *topodata.TabletAlias `protobuf:"bytes,4,opt,name=old_primary,json=oldPrimary,proto3" json:"old_primary,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *TabletExternallyReparentedResponse) Reset() { *m = TabletExternallyReparentedResponse{} } +func (m *TabletExternallyReparentedResponse) String() string { return proto.CompactTextString(m) } +func (*TabletExternallyReparentedResponse) ProtoMessage() {} +func (*TabletExternallyReparentedResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f41247b323a1ab2e, []int{54} +} + +func (m *TabletExternallyReparentedResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_TabletExternallyReparentedResponse.Unmarshal(m, b) +} +func (m *TabletExternallyReparentedResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_TabletExternallyReparentedResponse.Marshal(b, m, deterministic) +} +func (m *TabletExternallyReparentedResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_TabletExternallyReparentedResponse.Merge(m, src) +} +func (m *TabletExternallyReparentedResponse) XXX_Size() int { + return xxx_messageInfo_TabletExternallyReparentedResponse.Size(m) +} +func (m *TabletExternallyReparentedResponse) XXX_DiscardUnknown() { + xxx_messageInfo_TabletExternallyReparentedResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_TabletExternallyReparentedResponse proto.InternalMessageInfo + +func (m *TabletExternallyReparentedResponse) GetKeyspace() string { + if m != nil { + return m.Keyspace + } + return "" +} + +func (m *TabletExternallyReparentedResponse) GetShard() string { + if m != nil { + return m.Shard + } + return "" +} + +func (m *TabletExternallyReparentedResponse) GetNewPrimary() *topodata.TabletAlias { + if m != nil { + return m.NewPrimary + } + return nil +} + +func (m *TabletExternallyReparentedResponse) GetOldPrimary() *topodata.TabletAlias { + if m != nil { + return m.OldPrimary + } + return nil +} + func init() { proto.RegisterType((*ExecuteVtctlCommandRequest)(nil), "vtctldata.ExecuteVtctlCommandRequest") proto.RegisterType((*ExecuteVtctlCommandResponse)(nil), "vtctldata.ExecuteVtctlCommandResponse") proto.RegisterType((*TableMaterializeSettings)(nil), "vtctldata.TableMaterializeSettings") proto.RegisterType((*MaterializeSettings)(nil), "vtctldata.MaterializeSettings") + proto.RegisterType((*Keyspace)(nil), "vtctldata.Keyspace") + proto.RegisterType((*Shard)(nil), "vtctldata.Shard") + proto.RegisterType((*Workflow)(nil), "vtctldata.Workflow") + proto.RegisterMapType((map[string]*Workflow_ShardStream)(nil), "vtctldata.Workflow.ShardStreamsEntry") + proto.RegisterType((*Workflow_ReplicationLocation)(nil), "vtctldata.Workflow.ReplicationLocation") + proto.RegisterType((*Workflow_ShardStream)(nil), "vtctldata.Workflow.ShardStream") + proto.RegisterType((*Workflow_Stream)(nil), "vtctldata.Workflow.Stream") + proto.RegisterType((*Workflow_Stream_CopyState)(nil), "vtctldata.Workflow.Stream.CopyState") + proto.RegisterType((*ChangeTabletTypeRequest)(nil), "vtctldata.ChangeTabletTypeRequest") + proto.RegisterType((*ChangeTabletTypeResponse)(nil), "vtctldata.ChangeTabletTypeResponse") + proto.RegisterType((*CreateKeyspaceRequest)(nil), "vtctldata.CreateKeyspaceRequest") + proto.RegisterType((*CreateKeyspaceResponse)(nil), "vtctldata.CreateKeyspaceResponse") + proto.RegisterType((*CreateShardRequest)(nil), "vtctldata.CreateShardRequest") + proto.RegisterType((*CreateShardResponse)(nil), "vtctldata.CreateShardResponse") + proto.RegisterType((*DeleteKeyspaceRequest)(nil), "vtctldata.DeleteKeyspaceRequest") + proto.RegisterType((*DeleteKeyspaceResponse)(nil), "vtctldata.DeleteKeyspaceResponse") + proto.RegisterType((*DeleteShardsRequest)(nil), "vtctldata.DeleteShardsRequest") + proto.RegisterType((*DeleteShardsResponse)(nil), "vtctldata.DeleteShardsResponse") + proto.RegisterType((*DeleteTabletsRequest)(nil), "vtctldata.DeleteTabletsRequest") + proto.RegisterType((*DeleteTabletsResponse)(nil), "vtctldata.DeleteTabletsResponse") + proto.RegisterType((*FindAllShardsInKeyspaceRequest)(nil), "vtctldata.FindAllShardsInKeyspaceRequest") + proto.RegisterType((*FindAllShardsInKeyspaceResponse)(nil), "vtctldata.FindAllShardsInKeyspaceResponse") + proto.RegisterMapType((map[string]*Shard)(nil), "vtctldata.FindAllShardsInKeyspaceResponse.ShardsEntry") + proto.RegisterType((*GetBackupsRequest)(nil), "vtctldata.GetBackupsRequest") + proto.RegisterType((*GetBackupsResponse)(nil), "vtctldata.GetBackupsResponse") + proto.RegisterType((*GetCellInfoNamesRequest)(nil), "vtctldata.GetCellInfoNamesRequest") + proto.RegisterType((*GetCellInfoNamesResponse)(nil), "vtctldata.GetCellInfoNamesResponse") + proto.RegisterType((*GetCellInfoRequest)(nil), "vtctldata.GetCellInfoRequest") + proto.RegisterType((*GetCellInfoResponse)(nil), "vtctldata.GetCellInfoResponse") + proto.RegisterType((*GetCellsAliasesRequest)(nil), "vtctldata.GetCellsAliasesRequest") + proto.RegisterType((*GetCellsAliasesResponse)(nil), "vtctldata.GetCellsAliasesResponse") + proto.RegisterMapType((map[string]*topodata.CellsAlias)(nil), "vtctldata.GetCellsAliasesResponse.AliasesEntry") + proto.RegisterType((*GetKeyspacesRequest)(nil), "vtctldata.GetKeyspacesRequest") + proto.RegisterType((*GetKeyspacesResponse)(nil), "vtctldata.GetKeyspacesResponse") + proto.RegisterType((*GetKeyspaceRequest)(nil), "vtctldata.GetKeyspaceRequest") + proto.RegisterType((*GetKeyspaceResponse)(nil), "vtctldata.GetKeyspaceResponse") + proto.RegisterType((*GetSchemaRequest)(nil), "vtctldata.GetSchemaRequest") + proto.RegisterType((*GetSchemaResponse)(nil), "vtctldata.GetSchemaResponse") + proto.RegisterType((*GetShardRequest)(nil), "vtctldata.GetShardRequest") + proto.RegisterType((*GetShardResponse)(nil), "vtctldata.GetShardResponse") + proto.RegisterType((*GetSrvVSchemaRequest)(nil), "vtctldata.GetSrvVSchemaRequest") + proto.RegisterType((*GetSrvVSchemaResponse)(nil), "vtctldata.GetSrvVSchemaResponse") + proto.RegisterType((*GetTabletRequest)(nil), "vtctldata.GetTabletRequest") + proto.RegisterType((*GetTabletResponse)(nil), "vtctldata.GetTabletResponse") + proto.RegisterType((*GetTabletsRequest)(nil), "vtctldata.GetTabletsRequest") + proto.RegisterType((*GetTabletsResponse)(nil), "vtctldata.GetTabletsResponse") + proto.RegisterType((*GetVSchemaRequest)(nil), "vtctldata.GetVSchemaRequest") + proto.RegisterType((*GetVSchemaResponse)(nil), "vtctldata.GetVSchemaResponse") + proto.RegisterType((*GetWorkflowsRequest)(nil), "vtctldata.GetWorkflowsRequest") + proto.RegisterType((*GetWorkflowsResponse)(nil), "vtctldata.GetWorkflowsResponse") + proto.RegisterType((*RemoveKeyspaceCellRequest)(nil), "vtctldata.RemoveKeyspaceCellRequest") + proto.RegisterType((*RemoveKeyspaceCellResponse)(nil), "vtctldata.RemoveKeyspaceCellResponse") + proto.RegisterType((*RemoveShardCellRequest)(nil), "vtctldata.RemoveShardCellRequest") + proto.RegisterType((*RemoveShardCellResponse)(nil), "vtctldata.RemoveShardCellResponse") + proto.RegisterType((*ReparentTabletRequest)(nil), "vtctldata.ReparentTabletRequest") + proto.RegisterType((*ReparentTabletResponse)(nil), "vtctldata.ReparentTabletResponse") + proto.RegisterType((*TabletExternallyReparentedRequest)(nil), "vtctldata.TabletExternallyReparentedRequest") + proto.RegisterType((*TabletExternallyReparentedResponse)(nil), "vtctldata.TabletExternallyReparentedResponse") } func init() { proto.RegisterFile("vtctldata.proto", fileDescriptor_f41247b323a1ab2e) } var fileDescriptor_f41247b323a1ab2e = []byte{ - // 422 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0xd1, 0x6e, 0xd3, 0x30, - 0x14, 0x86, 0x95, 0xb5, 0x1b, 0xeb, 0x29, 0x4d, 0xc1, 0xdc, 0x58, 0x45, 0x48, 0xa1, 0xc0, 0x88, - 0x84, 0xd4, 0x48, 0xe3, 0x09, 0xa0, 0xf4, 0x06, 0xc4, 0x4d, 0xa8, 0x40, 0xe2, 0x26, 0x72, 0x93, - 0xb3, 0xc8, 0x9a, 0x1b, 0x07, 0xfb, 0xa4, 0x5b, 0x79, 0x03, 0x5e, 0x86, 0x67, 0x44, 0xb6, 0xb3, - 0x70, 0xb3, 0xdd, 0x1d, 0x7f, 0xe7, 0xb7, 0xfd, 0x9f, 0x5f, 0x07, 0xe6, 0x07, 0x2a, 0x49, 0x55, - 0x82, 0xc4, 0xaa, 0x35, 0x9a, 0x34, 0x9b, 0x0c, 0x60, 0x31, 0x53, 0xba, 0xee, 0x48, 0xaa, 0xd0, - 0x59, 0xfe, 0x80, 0xc5, 0xe6, 0x16, 0xcb, 0x8e, 0xf0, 0xbb, 0x93, 0xac, 0xf5, 0x7e, 0x2f, 0x9a, - 0x2a, 0xc7, 0x5f, 0x1d, 0x5a, 0x62, 0x0c, 0xc6, 0xc2, 0xd4, 0x96, 0x47, 0xc9, 0x28, 0x9d, 0xe4, - 0xbe, 0x66, 0x6f, 0x20, 0x16, 0x25, 0x49, 0xdd, 0x14, 0x24, 0xf7, 0xa8, 0x3b, 0xe2, 0x27, 0x49, - 0x94, 0x8e, 0xf2, 0x59, 0xa0, 0xdb, 0x00, 0x97, 0x6b, 0x78, 0x7e, 0xef, 0xc3, 0xb6, 0xd5, 0x8d, - 0x45, 0xf6, 0x1a, 0x4e, 0xf1, 0x80, 0x0d, 0xf1, 0x28, 0x89, 0xd2, 0xe9, 0x65, 0xbc, 0xba, 0xb3, - 0xb5, 0x71, 0x34, 0x0f, 0xcd, 0xe5, 0x9f, 0x08, 0xf8, 0x56, 0xec, 0x14, 0x7e, 0x15, 0x84, 0x46, - 0x0a, 0x25, 0x7f, 0xe3, 0x37, 0x24, 0x92, 0x4d, 0x6d, 0xd9, 0x4b, 0x78, 0x4c, 0xc2, 0xd4, 0x48, - 0x05, 0x39, 0x89, 0x7f, 0x69, 0x92, 0x4f, 0x03, 0xf3, 0xb7, 0xd8, 0x3b, 0x78, 0x6a, 0x75, 0x67, - 0x4a, 0x2c, 0xf0, 0xb6, 0x35, 0x68, 0xad, 0xd4, 0x8d, 0xb7, 0x3b, 0xc9, 0x9f, 0x84, 0xc6, 0x66, - 0xe0, 0xec, 0x05, 0x40, 0x69, 0x50, 0x10, 0x16, 0x55, 0xa5, 0xf8, 0xc8, 0xab, 0x26, 0x81, 0x7c, - 0xaa, 0xd4, 0xf2, 0xef, 0x09, 0x3c, 0xbb, 0xcf, 0xc6, 0x02, 0xce, 0x6f, 0xb4, 0xb9, 0xbe, 0x52, - 0xfa, 0xa6, 0xb7, 0x30, 0x9c, 0xd9, 0x5b, 0x98, 0xf7, 0xff, 0x5f, 0xe3, 0xd1, 0xb6, 0xa2, 0xc4, - 0xfe, 0xf7, 0x38, 0xe0, 0x2f, 0x3d, 0x75, 0xc2, 0x7e, 0x96, 0x41, 0x18, 0x0c, 0xc4, 0x01, 0x0f, - 0xc2, 0x0b, 0x98, 0x5b, 0xd2, 0x6d, 0x21, 0xae, 0x08, 0x4d, 0x51, 0xea, 0xf6, 0xc8, 0xc7, 0x49, - 0x94, 0x9e, 0xe7, 0x33, 0x87, 0x3f, 0x38, 0xba, 0xd6, 0xed, 0x91, 0x7d, 0x86, 0xd8, 0xa7, 0x52, - 0xd8, 0xde, 0x27, 0x3f, 0x4d, 0x46, 0xe9, 0xf4, 0xf2, 0xd5, 0xea, 0xff, 0x6e, 0x3c, 0x94, 0x6c, - 0x3e, 0xf3, 0x57, 0x87, 0x09, 0x19, 0x8c, 0x4b, 0x54, 0x8a, 0x9f, 0x79, 0x47, 0xbe, 0x0e, 0xe1, - 0xef, 0x94, 0x0b, 0xff, 0xd8, 0xa2, 0xe5, 0x8f, 0xee, 0xc2, 0x77, 0x6c, 0xeb, 0xd0, 0xc7, 0xf4, - 0xe7, 0xc5, 0x41, 0x12, 0x5a, 0xbb, 0x92, 0x3a, 0x0b, 0x55, 0x56, 0xeb, 0xec, 0x40, 0x99, 0x5f, - 0xbd, 0x6c, 0x30, 0xb2, 0x3b, 0xf3, 0xe0, 0xfd, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x46, 0x37, - 0xd0, 0x53, 0xb8, 0x02, 0x00, 0x00, + // 2293 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x59, 0xdd, 0x6e, 0x1b, 0xc7, + 0x15, 0xc6, 0x8a, 0x14, 0x45, 0x1e, 0x52, 0x3f, 0x1e, 0x51, 0xd2, 0x86, 0x8d, 0x63, 0x67, 0x1d, + 0x3b, 0xaa, 0x1b, 0x93, 0xb6, 0xd2, 0x04, 0x81, 0x9b, 0xa0, 0xb1, 0x65, 0xc9, 0x90, 0xe3, 0xba, + 0xea, 0x52, 0x75, 0x80, 0x16, 0xe8, 0x76, 0xc8, 0x1d, 0xd2, 0x0b, 0x2d, 0x77, 0x99, 0x9d, 0x21, + 0x25, 0xa6, 0x17, 0xbd, 0x69, 0x2f, 0x0a, 0xf4, 0x0d, 0x82, 0x02, 0xbd, 0x2a, 0xfa, 0x04, 0x01, + 0x7a, 0xd3, 0xcb, 0xbe, 0x43, 0xdf, 0xa6, 0x98, 0x33, 0x33, 0xcb, 0xe5, 0xaf, 0x6d, 0xa5, 0x57, + 0xda, 0x39, 0x73, 0xce, 0x9c, 0x6f, 0xce, 0xff, 0x50, 0xb0, 0x39, 0x14, 0x6d, 0x11, 0xfa, 0x54, + 0xd0, 0x7a, 0x3f, 0x89, 0x45, 0x4c, 0x4a, 0x29, 0xa1, 0xb6, 0xd5, 0x0a, 0xa2, 0x30, 0xee, 0x8e, + 0x37, 0x6b, 0xeb, 0x61, 0xdc, 0x1d, 0x88, 0x20, 0xd4, 0xcb, 0x8d, 0xde, 0x88, 0x7f, 0x13, 0xb6, + 0x85, 0x59, 0xef, 0x09, 0xda, 0x0a, 0x99, 0xe8, 0xd1, 0x88, 0x76, 0x59, 0x92, 0x91, 0xdb, 0x10, + 0x71, 0x3f, 0xce, 0x9e, 0x33, 0xe4, 0xed, 0x57, 0xac, 0x67, 0x96, 0x95, 0xa1, 0x10, 0x41, 0x8f, + 0xa9, 0x95, 0xf3, 0x35, 0xd4, 0x8e, 0x2e, 0x59, 0x7b, 0x20, 0xd8, 0x4b, 0x09, 0xe5, 0x30, 0xee, + 0xf5, 0x68, 0xe4, 0xbb, 0xec, 0x9b, 0x01, 0xe3, 0x82, 0x10, 0xc8, 0xd3, 0xa4, 0xcb, 0x6d, 0xeb, + 0x66, 0x6e, 0xbf, 0xe4, 0xe2, 0x37, 0xb9, 0x0d, 0x1b, 0xb4, 0x2d, 0x82, 0x38, 0xf2, 0xe4, 0x31, + 0xf1, 0x40, 0xd8, 0x2b, 0x37, 0xad, 0xfd, 0x9c, 0xbb, 0xae, 0xa8, 0x67, 0x8a, 0xe8, 0x1c, 0xc2, + 0x8f, 0xe6, 0x1e, 0xcc, 0xfb, 0x71, 0xc4, 0x19, 0xf9, 0x00, 0x56, 0xd9, 0x90, 0x45, 0xc2, 0xb6, + 0x6e, 0x5a, 0xfb, 0xe5, 0x83, 0x8d, 0xba, 0xb9, 0xec, 0x91, 0xa4, 0xba, 0x6a, 0xd3, 0xf9, 0x8b, + 0x05, 0xf6, 0x99, 0xbc, 0xe6, 0x2f, 0xa8, 0x60, 0x49, 0x40, 0xc3, 0xe0, 0x5b, 0xd6, 0x64, 0x42, + 0x04, 0x51, 0x97, 0x93, 0xf7, 0xa1, 0x22, 0x68, 0xd2, 0x65, 0xc2, 0x43, 0x4b, 0xe0, 0x49, 0x25, + 0xb7, 0xac, 0x68, 0x28, 0x45, 0x7e, 0x02, 0xd7, 0x78, 0x3c, 0x48, 0xda, 0xcc, 0x63, 0x97, 0xfd, + 0x84, 0x71, 0x1e, 0xc4, 0x11, 0xc2, 0x2d, 0xb9, 0x5b, 0x6a, 0xe3, 0x28, 0xa5, 0x93, 0xeb, 0x00, + 0xed, 0x84, 0x51, 0xc1, 0x3c, 0xdf, 0x0f, 0xed, 0x1c, 0x72, 0x95, 0x14, 0xe5, 0x89, 0x1f, 0x3a, + 0xff, 0x5d, 0x81, 0xed, 0x79, 0x30, 0x6a, 0x50, 0xbc, 0x88, 0x93, 0xf3, 0x4e, 0x18, 0x5f, 0x68, + 0x08, 0xe9, 0x9a, 0x7c, 0x08, 0x9b, 0x5a, 0xff, 0x39, 0x1b, 0xf1, 0x3e, 0x6d, 0x33, 0xad, 0x7d, + 0x43, 0x91, 0xbf, 0xd2, 0x54, 0xc9, 0xa8, 0xef, 0x92, 0x32, 0x2a, 0x00, 0x1b, 0x8a, 0x9c, 0x32, + 0xde, 0x81, 0x4d, 0x2e, 0xe2, 0xbe, 0x47, 0x3b, 0x82, 0x25, 0x5e, 0x3b, 0xee, 0x8f, 0xec, 0xfc, + 0x4d, 0x6b, 0xbf, 0xe8, 0xae, 0x4b, 0xf2, 0x23, 0x49, 0x3d, 0x8c, 0xfb, 0x23, 0xf2, 0x0c, 0x36, + 0xd0, 0x2a, 0x1e, 0xd7, 0x38, 0xed, 0xd5, 0x9b, 0xb9, 0xfd, 0xf2, 0xc1, 0xad, 0xfa, 0x38, 0x06, + 0x17, 0x59, 0xd6, 0x5d, 0x47, 0xd1, 0xf4, 0x86, 0x04, 0xf2, 0x6d, 0x16, 0x86, 0x76, 0x01, 0x11, + 0xe1, 0xb7, 0x32, 0xbe, 0x8c, 0x3f, 0x4f, 0x8c, 0xfa, 0x8c, 0xdb, 0x6b, 0xc6, 0xf8, 0x92, 0x76, + 0x26, 0x49, 0xe4, 0xc7, 0xb0, 0xc5, 0x2e, 0x05, 0x4b, 0x22, 0x1a, 0x7a, 0xed, 0x70, 0xc0, 0x05, + 0x4b, 0xec, 0x22, 0xb2, 0x6d, 0x1a, 0xfa, 0xa1, 0x22, 0x3b, 0x2f, 0xa0, 0x98, 0xde, 0x90, 0x40, + 0x3e, 0xa2, 0x3d, 0xe3, 0x4e, 0xfc, 0x26, 0x75, 0x28, 0x4e, 0x18, 0xb0, 0x7c, 0x40, 0xea, 0x69, + 0x94, 0x1b, 0x49, 0x37, 0xe5, 0x71, 0x7e, 0x07, 0xab, 0xcd, 0x57, 0x34, 0xf1, 0xa5, 0x73, 0x52, + 0x41, 0xed, 0x9c, 0xf3, 0x69, 0x45, 0x2b, 0x19, 0x45, 0xb7, 0x61, 0x95, 0x4b, 0x41, 0xb4, 0x7e, + 0xf9, 0x60, 0x73, 0xac, 0x05, 0xcf, 0x73, 0xd5, 0xae, 0xf3, 0xcf, 0x12, 0x14, 0xbf, 0x36, 0x4e, + 0x9e, 0x07, 0xf8, 0xe7, 0x50, 0x50, 0x1e, 0xd6, 0x70, 0x3f, 0xcc, 0x98, 0xdd, 0x08, 0xd6, 0x5d, + 0xd6, 0x0f, 0x83, 0x36, 0x95, 0x59, 0xf3, 0x3c, 0x56, 0x7f, 0x5d, 0x2d, 0x26, 0x0f, 0x50, 0x9e, + 0xd7, 0x48, 0xde, 0xfc, 0x00, 0x25, 0x46, 0x1e, 0xc0, 0x4e, 0x8f, 0x5e, 0x7a, 0x43, 0x2f, 0x19, + 0x33, 0x79, 0x21, 0xed, 0x62, 0xb8, 0xe4, 0x5c, 0xd2, 0xa3, 0x97, 0x2f, 0xb3, 0xf2, 0xb4, 0x4b, + 0x9e, 0xc1, 0x3a, 0x5e, 0xcf, 0xe3, 0x22, 0x61, 0xb4, 0x67, 0x42, 0xe6, 0xf6, 0x3c, 0xd5, 0x68, + 0x8e, 0xa6, 0xe2, 0x3b, 0x8a, 0x44, 0x32, 0x72, 0x2b, 0x3c, 0x43, 0xaa, 0xfd, 0x1e, 0xae, 0xcd, + 0xb0, 0x90, 0x2d, 0xc8, 0x9d, 0xb3, 0x91, 0x36, 0x94, 0xfc, 0x24, 0x9f, 0xc0, 0xea, 0x90, 0x86, + 0x03, 0x63, 0xa6, 0x1b, 0xaf, 0x51, 0xe5, 0x2a, 0xee, 0x87, 0x2b, 0x9f, 0x59, 0xb5, 0x13, 0xd8, + 0x9e, 0x73, 0xff, 0xa5, 0x1e, 0xdf, 0x85, 0x02, 0x82, 0xe4, 0xf6, 0x0a, 0x16, 0x34, 0xbd, 0xaa, + 0xfd, 0xcb, 0x82, 0x72, 0x46, 0x0b, 0xf9, 0x29, 0xac, 0x19, 0x13, 0x58, 0x68, 0x82, 0xda, 0x5c, + 0x5c, 0x0a, 0x92, 0x61, 0x25, 0xc7, 0x32, 0x87, 0x31, 0x25, 0xda, 0x71, 0x24, 0x92, 0x38, 0x54, + 0x6a, 0xca, 0x07, 0xd7, 0xa7, 0xa2, 0x48, 0x25, 0x9e, 0x38, 0x54, 0x5c, 0xae, 0x4a, 0x54, 0xb3, + 0xe4, 0xe4, 0x23, 0x20, 0x01, 0xf7, 0xfa, 0x49, 0xd0, 0xa3, 0xc9, 0xc8, 0xe3, 0x2c, 0x19, 0x06, + 0x51, 0x17, 0xc3, 0xa0, 0xe8, 0x6e, 0x05, 0xfc, 0x54, 0x6d, 0x34, 0x15, 0xbd, 0xf6, 0xb7, 0x3c, + 0x14, 0x34, 0xec, 0x0d, 0x58, 0x09, 0x7c, 0xbc, 0x74, 0xce, 0x5d, 0x09, 0x7c, 0x52, 0x35, 0xc1, + 0xac, 0x22, 0x5c, 0x2d, 0xc8, 0x3d, 0x19, 0x59, 0x52, 0xa1, 0x8e, 0xac, 0x9d, 0x31, 0x3a, 0x85, + 0xeb, 0x51, 0x18, 0x50, 0xee, 0x6a, 0x26, 0xf2, 0x05, 0xac, 0xab, 0xce, 0xe4, 0xe9, 0x80, 0xce, + 0xa3, 0x94, 0x5d, 0xcf, 0xf4, 0xab, 0xc7, 0xf8, 0xd9, 0xc4, 0x7d, 0xb7, 0xd2, 0xca, 0xac, 0xa4, + 0x3b, 0xfa, 0x31, 0x0f, 0xa4, 0x6b, 0xec, 0x55, 0xe5, 0x0e, 0xb3, 0x26, 0xb7, 0x00, 0x8b, 0x96, + 0x97, 0x32, 0xa8, 0x02, 0x53, 0x91, 0xc4, 0x53, 0xc3, 0x24, 0x2f, 0x21, 0xa8, 0x60, 0xba, 0xc2, + 0xa8, 0x05, 0xd9, 0x83, 0x35, 0xbf, 0xe5, 0x61, 0xda, 0xa9, 0x92, 0x52, 0xf0, 0x5b, 0x2f, 0x64, + 0xe2, 0x3d, 0x82, 0x1d, 0x91, 0xd0, 0x88, 0x67, 0x5a, 0x14, 0x17, 0xb4, 0xd7, 0xb7, 0x4b, 0x08, + 0xbb, 0x52, 0xd7, 0xdd, 0x4f, 0xb6, 0x29, 0xb7, 0x9a, 0x61, 0x3d, 0x33, 0x9c, 0xa4, 0x01, 0x15, + 0xc9, 0xe2, 0x0d, 0xfa, 0x3e, 0x15, 0xcc, 0xb7, 0x61, 0x8e, 0x64, 0x59, 0x7e, 0xfe, 0x5a, 0x31, + 0x10, 0x1b, 0xd6, 0x7a, 0x8c, 0x73, 0xda, 0x65, 0x76, 0x19, 0xc1, 0x98, 0x25, 0x39, 0x82, 0xb2, + 0x2c, 0xd1, 0x1e, 0x82, 0xe6, 0x76, 0x05, 0xc3, 0xe1, 0x83, 0xc5, 0xc1, 0x54, 0x97, 0xb5, 0xbb, + 0x29, 0x99, 0x5d, 0x68, 0x9b, 0x4f, 0x5e, 0x7b, 0x08, 0xa5, 0x74, 0x43, 0x1a, 0x24, 0xdb, 0xef, + 0xd4, 0x42, 0x1a, 0x24, 0xa4, 0x5c, 0x78, 0xfd, 0x73, 0xed, 0xed, 0x82, 0x5c, 0x9e, 0x9e, 0x3b, + 0xdf, 0x59, 0xb0, 0x77, 0xf8, 0x8a, 0x46, 0x5d, 0x76, 0x96, 0xd6, 0x66, 0xd3, 0xde, 0x3f, 0x4b, + 0x8b, 0x38, 0x95, 0x3e, 0xd7, 0xbd, 0x78, 0x41, 0x40, 0xe8, 0xda, 0x8e, 0x0b, 0x72, 0x0f, 0xed, + 0x2f, 0x4b, 0x3f, 0xaa, 0xdb, 0x38, 0xa8, 0x4e, 0x0b, 0xa1, 0x9e, 0x82, 0xdf, 0x92, 0x7f, 0xd1, + 0x5d, 0xc9, 0xc8, 0x4b, 0x06, 0x91, 0x8e, 0xe3, 0x82, 0x9f, 0x8c, 0xdc, 0x41, 0xe4, 0xfc, 0xc3, + 0x02, 0x7b, 0x16, 0x9d, 0x9e, 0x11, 0x3e, 0x81, 0xf5, 0x16, 0xeb, 0xc4, 0x09, 0xf3, 0x74, 0xc0, + 0x2a, 0x7c, 0x5b, 0xd3, 0xaa, 0xdc, 0x8a, 0x62, 0x53, 0x2b, 0xf2, 0x31, 0x54, 0x54, 0x77, 0xd4, + 0x52, 0x2b, 0x0b, 0xa4, 0xca, 0xc8, 0xa5, 0x85, 0xde, 0x83, 0xf2, 0x05, 0xe5, 0xde, 0x24, 0xca, + 0xd2, 0x05, 0xe5, 0x4f, 0x14, 0xd0, 0xef, 0x73, 0xb0, 0x73, 0x88, 0xb3, 0x40, 0xda, 0x6e, 0xc6, + 0x33, 0xd2, 0x4c, 0xf9, 0xaf, 0xc2, 0x6a, 0x27, 0x36, 0xd5, 0xbf, 0xe8, 0xaa, 0x05, 0x69, 0x40, + 0x95, 0x86, 0x61, 0x7c, 0xe1, 0xb1, 0x5e, 0x5f, 0x8c, 0xbc, 0xa1, 0xa7, 0xe6, 0x32, 0xad, 0xec, + 0x1a, 0xee, 0x1d, 0xc9, 0xad, 0x97, 0x4d, 0xdc, 0x20, 0xf7, 0xa1, 0x8a, 0x39, 0x1b, 0x44, 0x5d, + 0xaf, 0x1d, 0x87, 0x83, 0x5e, 0xa4, 0x42, 0x3e, 0x8f, 0xaa, 0x88, 0xd9, 0x3b, 0xc4, 0x2d, 0x0c, + 0xff, 0x67, 0xb3, 0x12, 0xe8, 0xa4, 0x55, 0x74, 0x92, 0x3d, 0xdb, 0x34, 0x4f, 0x7c, 0x34, 0xf9, + 0xd4, 0x59, 0xe8, 0xb4, 0x2f, 0xa1, 0x22, 0x8b, 0x0f, 0xf3, 0xbd, 0x4e, 0x12, 0xf7, 0xb8, 0x5d, + 0x98, 0x2e, 0x66, 0xe6, 0x8c, 0x7a, 0x13, 0xd9, 0x8e, 0x93, 0xb8, 0xe7, 0x96, 0x79, 0xfa, 0xcd, + 0xc9, 0x5d, 0xc8, 0xa3, 0xf6, 0x35, 0xd4, 0xbe, 0x3b, 0x2b, 0x89, 0xba, 0x91, 0x47, 0x16, 0x83, + 0x16, 0xe5, 0x99, 0x41, 0x49, 0xe5, 0x75, 0x45, 0x12, 0xd3, 0xd9, 0xe0, 0x01, 0xac, 0xf3, 0x88, + 0xf6, 0xf9, 0xab, 0x58, 0x60, 0x6a, 0xcf, 0xcd, 0xea, 0x8a, 0x61, 0x91, 0x2b, 0xe7, 0x04, 0x76, + 0xa7, 0xfd, 0xa6, 0xc3, 0xab, 0x31, 0xd5, 0x29, 0xca, 0x07, 0xdb, 0x99, 0xcc, 0x9c, 0x33, 0x55, + 0xfc, 0xd5, 0x02, 0xa2, 0xce, 0x52, 0xc3, 0x80, 0x0e, 0x80, 0x65, 0x1d, 0xe7, 0x3a, 0x80, 0x6a, + 0xa9, 0x99, 0x49, 0xa3, 0x84, 0x94, 0x17, 0x13, 0x71, 0x92, 0xcb, 0xc6, 0xc9, 0x6d, 0xd8, 0x08, + 0xa2, 0x76, 0x38, 0xf0, 0x99, 0xd7, 0xa7, 0x89, 0x1c, 0x92, 0xf5, 0x88, 0xa7, 0xa9, 0xa7, 0x48, + 0x74, 0xfe, 0x6e, 0xc1, 0xf6, 0x04, 0x9c, 0x2b, 0xde, 0x8b, 0xdc, 0xc9, 0xf6, 0x09, 0x99, 0x29, + 0x63, 0xee, 0xec, 0xd4, 0x93, 0x86, 0xa3, 0x47, 0xc3, 0x84, 0x51, 0x7f, 0xe4, 0xb1, 0xcb, 0x80, + 0x0b, 0xae, 0xc1, 0xab, 0x10, 0x7a, 0xa4, 0xb6, 0x8e, 0x70, 0xc7, 0xf9, 0x15, 0xec, 0x3c, 0x61, + 0x21, 0x9b, 0x4d, 0x9a, 0x65, 0x36, 0x7b, 0x17, 0x4a, 0x09, 0x6b, 0x0f, 0x12, 0x1e, 0x0c, 0x4d, + 0x02, 0x8d, 0x09, 0x8e, 0x0d, 0xbb, 0xd3, 0x47, 0xaa, 0x7b, 0x3b, 0x7f, 0xb6, 0x60, 0x5b, 0x6d, + 0x21, 0x6a, 0x6e, 0x74, 0xed, 0xa7, 0x5d, 0x5f, 0x35, 0xf3, 0xd9, 0xfb, 0xe9, 0xfd, 0xe5, 0x9a, + 0xe5, 0xe8, 0x2d, 0x5f, 0x25, 0x5e, 0xd0, 0x49, 0x9b, 0xb2, 0xf6, 0x8b, 0x24, 0x9f, 0x74, 0x74, + 0x47, 0x76, 0x76, 0xa1, 0x3a, 0x09, 0x43, 0xe3, 0x1b, 0x19, 0xba, 0x2a, 0x39, 0x29, 0xbe, 0xcf, + 0xf5, 0xa8, 0xae, 0xab, 0x30, 0x33, 0x38, 0x17, 0xd4, 0xe1, 0xf5, 0x4c, 0x1d, 0x66, 0x5c, 0xe6, + 0x8d, 0x2a, 0x2a, 0x7a, 0x60, 0xd0, 0xb8, 0x2b, 0x48, 0xd4, 0xb3, 0x82, 0xb3, 0x67, 0xfc, 0x90, + 0xaa, 0xd6, 0x98, 0x3e, 0x87, 0xf7, 0x8e, 0x83, 0xc8, 0x7f, 0x14, 0x86, 0x0a, 0xec, 0x49, 0xf4, + 0x16, 0x9e, 0x72, 0xfe, 0x6d, 0xc1, 0x8d, 0x85, 0xe2, 0x3a, 0x1a, 0x5f, 0x4c, 0x59, 0xff, 0xd3, + 0x8c, 0xf5, 0x5f, 0x23, 0xab, 0xbc, 0xa3, 0xc7, 0x4b, 0x33, 0xab, 0x7d, 0xa5, 0x47, 0xb5, 0x85, + 0x23, 0xe5, 0x9d, 0xc9, 0x91, 0x72, 0x4e, 0x34, 0xa7, 0x33, 0xa4, 0x73, 0x04, 0xd7, 0x9e, 0x32, + 0xf1, 0x98, 0xb6, 0xcf, 0x07, 0x7d, 0xfe, 0x26, 0xb1, 0x39, 0x77, 0xa4, 0x72, 0x9e, 0x00, 0xc9, + 0x1e, 0xa3, 0x6f, 0x5e, 0x87, 0xb5, 0x96, 0x22, 0xe9, 0xab, 0x57, 0xeb, 0xe9, 0x13, 0x5e, 0xf1, + 0x9e, 0x44, 0x9d, 0xd8, 0x35, 0x4c, 0xce, 0x3b, 0xb0, 0xf7, 0x94, 0x89, 0x43, 0x16, 0x86, 0x92, + 0x2e, 0xeb, 0x83, 0x81, 0xe4, 0xdc, 0x07, 0x7b, 0x76, 0x4b, 0xab, 0xa9, 0xc2, 0xaa, 0x2c, 0x2e, + 0xe6, 0x91, 0xae, 0x16, 0xce, 0x3e, 0x42, 0x32, 0x12, 0x99, 0x5e, 0x85, 0x2f, 0x39, 0x6b, 0xfc, + 0x92, 0x73, 0x8e, 0x61, 0x7b, 0x82, 0x33, 0xad, 0x22, 0x25, 0xb9, 0xed, 0x05, 0x51, 0x27, 0xd6, + 0x65, 0x24, 0xf3, 0xe6, 0x4a, 0xd9, 0x8b, 0x6d, 0xfd, 0x25, 0x13, 0x53, 0x9f, 0xc3, 0x75, 0x6c, + 0x1a, 0xf4, 0xdf, 0x5b, 0xe9, 0xcd, 0xc6, 0x5b, 0x5a, 0xcd, 0x09, 0xac, 0x4d, 0x46, 0x7d, 0x23, + 0xe3, 0xaf, 0x05, 0x42, 0x75, 0xbd, 0x56, 0x81, 0x61, 0xe4, 0x6b, 0xa7, 0x50, 0xc9, 0x6e, 0xcc, + 0x09, 0x8d, 0xbb, 0x93, 0xa1, 0x51, 0x9d, 0xbc, 0x8f, 0x52, 0x93, 0x0d, 0x8f, 0x1d, 0x34, 0x8d, + 0x09, 0xcb, 0xf4, 0x3e, 0x27, 0x50, 0x9d, 0x24, 0xeb, 0xbb, 0x3c, 0x80, 0x92, 0x09, 0x14, 0x73, + 0x9b, 0xb9, 0x95, 0x77, 0xcc, 0xe5, 0xdc, 0x47, 0x37, 0xbd, 0x4d, 0xce, 0x1d, 0x4f, 0x60, 0xba, + 0x7a, 0x33, 0xfb, 0xd3, 0x0a, 0x6c, 0x3d, 0x65, 0x42, 0x4d, 0x1a, 0x3f, 0x7c, 0x20, 0xdc, 0xd5, + 0xaf, 0x8a, 0xf4, 0x69, 0xa5, 0x56, 0xb2, 0x97, 0xb1, 0x4b, 0xd5, 0xcb, 0xf4, 0x7e, 0x0e, 0xf7, + 0xd7, 0x35, 0xf5, 0x4c, 0xb1, 0xdd, 0x02, 0xd3, 0xdc, 0xbc, 0x61, 0xc0, 0x2e, 0xb8, 0xae, 0xac, + 0x15, 0x4d, 0x7c, 0x29, 0x69, 0x64, 0x1f, 0xb6, 0xd4, 0x6f, 0x1a, 0x18, 0xe2, 0x5e, 0x1c, 0x85, + 0x23, 0x1c, 0x6c, 0x8a, 0xfa, 0x09, 0x85, 0x79, 0xf1, 0xcb, 0x28, 0x1c, 0x8d, 0x39, 0x79, 0xf0, + 0xad, 0xe1, 0x2c, 0x64, 0x38, 0x9b, 0x92, 0x2c, 0x39, 0x9d, 0x53, 0xac, 0x00, 0xc6, 0x0a, 0xda, + 0x98, 0x3f, 0x83, 0x82, 0x1e, 0xcd, 0x94, 0x01, 0x6e, 0xd5, 0x67, 0x7f, 0x6b, 0x53, 0x22, 0x4f, + 0x58, 0x27, 0x88, 0x02, 0xfd, 0x72, 0x47, 0x8a, 0xf3, 0x1c, 0x36, 0xe5, 0x89, 0xff, 0x9f, 0x09, + 0xc1, 0x79, 0xa8, 0xbc, 0x34, 0xd1, 0xe0, 0xd3, 0x7e, 0x6d, 0x2d, 0xed, 0xd7, 0xce, 0x5d, 0x8c, + 0xd3, 0x66, 0x32, 0x7c, 0x39, 0xe9, 0xe5, 0x79, 0x55, 0xe0, 0x05, 0xec, 0x4c, 0xf1, 0xa6, 0x43, + 0x78, 0x85, 0x27, 0xc3, 0xf1, 0xb0, 0x9a, 0x06, 0x97, 0xfe, 0x51, 0x31, 0x23, 0x02, 0x3c, 0xfd, + 0x76, 0x9e, 0x23, 0x6e, 0x3d, 0x69, 0xff, 0xd0, 0xe8, 0x72, 0xbe, 0x40, 0x2f, 0x99, 0xd3, 0x34, + 0xb2, 0xfd, 0xf4, 0x21, 0xbb, 0xe8, 0x5d, 0xa0, 0xf7, 0x9d, 0xdf, 0x66, 0xc4, 0xaf, 0x5e, 0xe6, + 0x25, 0x55, 0xda, 0xca, 0x84, 0xb0, 0x5a, 0x38, 0x5f, 0x62, 0x0a, 0x4f, 0x35, 0x56, 0x72, 0x17, + 0xd6, 0x94, 0xf2, 0xf1, 0xd4, 0x31, 0x8d, 0xce, 0x30, 0x38, 0x0d, 0x84, 0x37, 0xe5, 0xa4, 0x65, + 0x35, 0xe0, 0x31, 0xaa, 0x9c, 0xf6, 0xd4, 0x47, 0x50, 0x9c, 0xf2, 0xd2, 0xb5, 0xd4, 0x4b, 0x69, + 0x01, 0x58, 0x1b, 0x6a, 0x07, 0xb9, 0x58, 0x47, 0xcc, 0xfb, 0xf3, 0x8d, 0xac, 0x72, 0x03, 0xca, + 0xf2, 0xad, 0x3c, 0x64, 0x2a, 0xa1, 0xd4, 0xa0, 0x01, 0x8a, 0x84, 0xc9, 0xa4, 0x0a, 0x63, 0xe6, + 0xcc, 0x71, 0x61, 0x34, 0x3f, 0x89, 0xce, 0x2b, 0x8c, 0x46, 0xc0, 0x1d, 0x73, 0x39, 0x7f, 0x84, + 0x77, 0x5c, 0xd6, 0x8b, 0x87, 0xe9, 0x98, 0x27, 0x2b, 0xf4, 0x9b, 0x80, 0x34, 0xc1, 0xbd, 0x92, + 0xf9, 0xb1, 0x72, 0xfe, 0x98, 0x3d, 0x31, 0xed, 0xe5, 0xa7, 0xe7, 0xcc, 0x77, 0xa1, 0x36, 0x0f, + 0x80, 0x9e, 0x9b, 0xbe, 0xb3, 0x60, 0x57, 0x6d, 0x63, 0xc6, 0xbd, 0x29, 0xb8, 0xd7, 0x3c, 0x07, + 0x0c, 0xf6, 0xdc, 0x3c, 0xec, 0xf9, 0x85, 0xd8, 0x57, 0xa7, 0xb1, 0xbf, 0x03, 0x7b, 0x33, 0xe0, + 0x34, 0xf0, 0x63, 0xd8, 0x71, 0x99, 0x7a, 0x55, 0x4c, 0x26, 0xe7, 0xbd, 0xa9, 0x6c, 0x5a, 0xfe, + 0xb3, 0x90, 0xf3, 0x07, 0x79, 0xff, 0xc9, 0x73, 0xb4, 0xb3, 0xdf, 0x3e, 0xaf, 0x1a, 0xb0, 0x66, + 0x86, 0xd7, 0xa5, 0x3f, 0x49, 0x19, 0x2e, 0xc7, 0x85, 0xf7, 0x15, 0xfd, 0x48, 0xff, 0x8e, 0x1c, + 0x8e, 0x0c, 0x18, 0xe6, 0x5f, 0xf1, 0x42, 0xff, 0xb1, 0xc0, 0x59, 0x76, 0xe8, 0x95, 0x6f, 0xf7, + 0x29, 0x94, 0x23, 0x36, 0x1e, 0xcf, 0x97, 0xde, 0x10, 0x22, 0x66, 0x66, 0x76, 0x29, 0x17, 0x87, + 0x7e, 0x2a, 0x97, 0x5f, 0x2a, 0x17, 0x87, 0xbe, 0x96, 0x7b, 0xbc, 0xff, 0x9b, 0x3b, 0xc3, 0x40, + 0x30, 0xce, 0xeb, 0x41, 0xdc, 0x50, 0x5f, 0x8d, 0x6e, 0xdc, 0x18, 0x8a, 0x06, 0xfe, 0xc7, 0xa7, + 0x91, 0xe6, 0x5d, 0xab, 0x80, 0x84, 0x8f, 0xff, 0x17, 0x00, 0x00, 0xff, 0xff, 0x7b, 0xfc, 0x24, + 0xcf, 0x97, 0x1a, 0x00, 0x00, } diff --git a/go/vt/proto/vtctlservice/vtctlservice.pb.go b/go/vt/proto/vtctlservice/vtctlservice.pb.go index d661e476922..13e2a965efa 100644 --- a/go/vt/proto/vtctlservice/vtctlservice.pb.go +++ b/go/vt/proto/vtctlservice/vtctlservice.pb.go @@ -29,17 +29,43 @@ const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package func init() { proto.RegisterFile("vtctlservice.proto", fileDescriptor_27055cdbb1148d2b) } var fileDescriptor_27055cdbb1148d2b = []byte{ - // 146 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x2a, 0x2b, 0x49, 0x2e, - 0xc9, 0x29, 0x4e, 0x2d, 0x2a, 0xcb, 0x4c, 0x4e, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, - 0x41, 0x16, 0x93, 0xe2, 0x07, 0xf3, 0x52, 0x12, 0x4b, 0x12, 0x21, 0xd2, 0x46, 0x85, 0x5c, 0xac, - 0x61, 0x20, 0x21, 0xa1, 0x0c, 0x2e, 0x61, 0xd7, 0x8a, 0xd4, 0xe4, 0xd2, 0x92, 0x54, 0x30, 0xdf, - 0x39, 0x3f, 0x37, 0x37, 0x31, 0x2f, 0x45, 0x48, 0x55, 0x0f, 0xa1, 0x03, 0x8b, 0x7c, 0x50, 0x6a, - 0x61, 0x69, 0x6a, 0x71, 0x89, 0x94, 0x1a, 0x21, 0x65, 0xc5, 0x05, 0xf9, 0x79, 0xc5, 0xa9, 0x4a, - 0x0c, 0x06, 0x8c, 0x4e, 0xda, 0x51, 0x9a, 0x65, 0x99, 0x25, 0xa9, 0xc5, 0xc5, 0x7a, 0x99, 0xf9, - 0xfa, 0x10, 0x96, 0x7e, 0x7a, 0xbe, 0x7e, 0x59, 0x89, 0x3e, 0xd8, 0x49, 0xfa, 0xc8, 0x0e, 0x4e, - 0x62, 0x03, 0x8b, 0x19, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0x9d, 0xb5, 0x06, 0x92, 0xdb, 0x00, - 0x00, 0x00, + // 570 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x96, 0x6f, 0x4f, 0xdb, 0x30, + 0x10, 0xc6, 0xb7, 0x17, 0x43, 0x9b, 0xc7, 0x00, 0x79, 0x2f, 0x26, 0x01, 0xed, 0x68, 0x37, 0x26, + 0xb1, 0x49, 0xcd, 0xc4, 0x3e, 0x01, 0x74, 0xac, 0x43, 0x48, 0x48, 0x83, 0x0a, 0x24, 0x24, 0x5e, + 0x98, 0xe4, 0xa0, 0x11, 0x4e, 0x1c, 0x62, 0x37, 0x1b, 0xdf, 0x76, 0x1f, 0x65, 0xaa, 0x5d, 0xbb, + 0x8e, 0xff, 0x94, 0xbd, 0x23, 0xf7, 0x7b, 0xee, 0x39, 0x5f, 0x72, 0x67, 0x8a, 0x70, 0x23, 0x52, + 0x41, 0x39, 0xd4, 0x4d, 0x9e, 0xc2, 0xa0, 0xaa, 0x99, 0x60, 0x78, 0xd5, 0x8e, 0x6d, 0xae, 0xcb, + 0xa7, 0x8c, 0x08, 0xa2, 0xf0, 0xfe, 0x03, 0x7a, 0x71, 0x31, 0x0b, 0xe1, 0x09, 0x7a, 0x7b, 0xf4, + 0x07, 0xd2, 0xa9, 0x00, 0xf9, 0x3c, 0x64, 0x45, 0x41, 0xca, 0x0c, 0xef, 0x0e, 0x16, 0x19, 0x01, + 0x7e, 0x06, 0x0f, 0x53, 0xe0, 0x62, 0xf3, 0xd3, 0x53, 0x32, 0x5e, 0xb1, 0x92, 0x43, 0xff, 0xd9, + 0xd7, 0xe7, 0xfb, 0x7f, 0xd7, 0xd0, 0x8a, 0x84, 0x19, 0xbe, 0x46, 0x1b, 0xc3, 0x09, 0x29, 0xef, + 0x60, 0x4c, 0x6e, 0x28, 0x88, 0xf1, 0x63, 0x05, 0xb8, 0x6f, 0x59, 0xb9, 0x50, 0x97, 0xfb, 0xb0, + 0x54, 0xa3, 0x6b, 0xe1, 0x4b, 0xb4, 0x36, 0xac, 0x81, 0x08, 0x38, 0x81, 0x47, 0x5e, 0x91, 0x14, + 0xf0, 0x8e, 0x9d, 0xd8, 0x42, 0xda, 0xba, 0xb7, 0x44, 0x61, 0x8c, 0x4f, 0xd1, 0x6b, 0xc5, 0xce, + 0x27, 0xa4, 0xce, 0x70, 0xc7, 0xcb, 0x91, 0x71, 0x6d, 0xd9, 0x8d, 0x61, 0xfb, 0xa0, 0xdf, 0x81, + 0x42, 0xe4, 0xa0, 0x6d, 0x14, 0x3a, 0xa8, 0xab, 0x30, 0xc6, 0xbf, 0xd0, 0xaa, 0x62, 0xb2, 0x22, + 0xc7, 0x5d, 0x2f, 0x49, 0x01, 0x6d, 0xfa, 0x3e, 0xca, 0x8d, 0xe5, 0x18, 0xbd, 0x51, 0x44, 0xbd, + 0x72, 0x8e, 0xfd, 0x9c, 0x39, 0xd1, 0xa6, 0x3b, 0x71, 0x81, 0x71, 0xad, 0xd1, 0xbb, 0x1f, 0x79, + 0x99, 0x1d, 0x50, 0xaa, 0x0a, 0x1e, 0x97, 0xe6, 0x55, 0xec, 0x59, 0xe9, 0x11, 0x8d, 0xae, 0xf4, + 0xf9, 0x7f, 0xa4, 0xa6, 0xe6, 0x09, 0x42, 0x23, 0x10, 0x87, 0x24, 0xbd, 0x9f, 0x56, 0x1c, 0x6f, + 0x5b, 0xb9, 0x8b, 0xb0, 0x76, 0xee, 0x44, 0xa8, 0x31, 0xbb, 0x46, 0x1b, 0x23, 0x10, 0x43, 0xa0, + 0xf4, 0xb8, 0xbc, 0x65, 0xa7, 0xa4, 0x00, 0xde, 0x1a, 0x65, 0x17, 0x86, 0x46, 0xd9, 0xd7, 0xd8, + 0x13, 0x67, 0x51, 0xdc, 0x09, 0x67, 0x85, 0x26, 0xae, 0x85, 0x8d, 0xdf, 0x15, 0x5a, 0x9f, 0x03, + 0x7e, 0x40, 0x73, 0xc2, 0x81, 0xe3, 0x9e, 0x9f, 0xa4, 0x99, 0xf6, 0xed, 0x2f, 0x93, 0x38, 0x67, + 0x35, 0xdf, 0xcf, 0x39, 0xab, 0xfb, 0xcd, 0xba, 0x31, 0x6c, 0x0f, 0xb1, 0x05, 0xda, 0x43, 0x6c, + 0x83, 0xd0, 0x10, 0xb7, 0xb9, 0xb1, 0xfc, 0x89, 0x5e, 0x8d, 0x40, 0x9c, 0xa7, 0x13, 0x28, 0x08, + 0xde, 0x6a, 0xeb, 0x55, 0x54, 0x9b, 0x6d, 0x87, 0xa1, 0x71, 0x3a, 0x42, 0x2f, 0x67, 0x61, 0x79, + 0x0f, 0x6c, 0x3a, 0x5a, 0xfb, 0x12, 0xd8, 0x0a, 0x32, 0x7b, 0xab, 0x66, 0xd1, 0xba, 0xb9, 0x98, + 0x1f, 0xca, 0x69, 0x62, 0x41, 0x42, 0x5b, 0xe5, 0x08, 0x9c, 0x36, 0xd5, 0xb6, 0xb9, 0x6d, 0xaa, + 0x68, 0xa4, 0x4d, 0x0d, 0x9d, 0x5d, 0xd1, 0x2b, 0x1f, 0x54, 0xc7, 0x76, 0xc5, 0x5f, 0x76, 0x65, + 0xa6, 0x3b, 0x75, 0xcc, 0x9c, 0x36, 0x3b, 0x11, 0xea, 0x4c, 0xc7, 0x25, 0xab, 0xef, 0x6f, 0x29, + 0xfb, 0xed, 0x4d, 0x87, 0x01, 0x91, 0xe9, 0xb0, 0xb8, 0xb1, 0x4c, 0x11, 0x3e, 0x83, 0x82, 0x35, + 0xe6, 0x46, 0x9d, 0x0d, 0x3a, 0xfe, 0x68, 0x25, 0xfa, 0x58, 0xdb, 0xef, 0x3e, 0xa1, 0xb2, 0x37, + 0x50, 0x71, 0x39, 0x0a, 0xb2, 0x42, 0xcf, 0xcb, 0x35, 0x2c, 0xb4, 0x81, 0x9e, 0x44, 0x7b, 0x1f, + 0x7e, 0xb9, 0xda, 0x6b, 0x72, 0x01, 0x9c, 0x0f, 0x72, 0x96, 0xa8, 0xbf, 0x92, 0x3b, 0x96, 0x34, + 0x22, 0x91, 0xff, 0xf5, 0x13, 0xfb, 0x37, 0xc1, 0xcd, 0x8a, 0x8c, 0x7d, 0xfb, 0x17, 0x00, 0x00, + 0xff, 0xff, 0xb0, 0xcb, 0xe1, 0x8e, 0x3e, 0x08, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -148,3 +174,913 @@ var _Vtctl_serviceDesc = grpc.ServiceDesc{ }, Metadata: "vtctlservice.proto", } + +// VtctldClient is the client API for Vtctld service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type VtctldClient interface { + // ChangeTabletType changes the db type for the specified tablet, if possible. + // This is used primarily to arrange replicas, and it will not convert a + // primary. For that, use InitShardPrimary. + // + // NOTE: This command automatically updates the serving graph. + ChangeTabletType(ctx context.Context, in *vtctldata.ChangeTabletTypeRequest, opts ...grpc.CallOption) (*vtctldata.ChangeTabletTypeResponse, error) + // CreateKeyspace creates the specified keyspace in the topology. For a + // SNAPSHOT keyspace, the request must specify the name of a base keyspace, + // as well as a snapshot time. + CreateKeyspace(ctx context.Context, in *vtctldata.CreateKeyspaceRequest, opts ...grpc.CallOption) (*vtctldata.CreateKeyspaceResponse, error) + // CreateShard creates the specified shard in the topology. + CreateShard(ctx context.Context, in *vtctldata.CreateShardRequest, opts ...grpc.CallOption) (*vtctldata.CreateShardResponse, error) + // DeleteKeyspace deletes the specified keyspace from the topology. In + // recursive mode, it also recursively deletes all shards in the keyspace. + // Otherwise, the keyspace must be empty (have no shards), or DeleteKeyspace + // returns an error. + DeleteKeyspace(ctx context.Context, in *vtctldata.DeleteKeyspaceRequest, opts ...grpc.CallOption) (*vtctldata.DeleteKeyspaceResponse, error) + // DeleteShards deletes the specified shards from the topology. In recursive + // mode, it also deletes all tablets belonging to the shard. Otherwise, the + // shard must be empty (have no tablets) or DeleteShards returns an error for + // that shard. + DeleteShards(ctx context.Context, in *vtctldata.DeleteShardsRequest, opts ...grpc.CallOption) (*vtctldata.DeleteShardsResponse, error) + // DeleteTablets deletes one or more tablets from the topology. + DeleteTablets(ctx context.Context, in *vtctldata.DeleteTabletsRequest, opts ...grpc.CallOption) (*vtctldata.DeleteTabletsResponse, error) + // FindAllShardsInKeyspace returns a map of shard names to shard references + // for a given keyspace. + FindAllShardsInKeyspace(ctx context.Context, in *vtctldata.FindAllShardsInKeyspaceRequest, opts ...grpc.CallOption) (*vtctldata.FindAllShardsInKeyspaceResponse, error) + // GetBackups returns all the backups for a shard. + GetBackups(ctx context.Context, in *vtctldata.GetBackupsRequest, opts ...grpc.CallOption) (*vtctldata.GetBackupsResponse, error) + // GetCellInfoNames returns all the cells for which we have a CellInfo object, + // meaning we have a topology service registered. + GetCellInfoNames(ctx context.Context, in *vtctldata.GetCellInfoNamesRequest, opts ...grpc.CallOption) (*vtctldata.GetCellInfoNamesResponse, error) + // GetCellInfo returns the information for a cell. + GetCellInfo(ctx context.Context, in *vtctldata.GetCellInfoRequest, opts ...grpc.CallOption) (*vtctldata.GetCellInfoResponse, error) + // GetCellsAliases returns a mapping of cell alias to cells identified by that + // alias. + GetCellsAliases(ctx context.Context, in *vtctldata.GetCellsAliasesRequest, opts ...grpc.CallOption) (*vtctldata.GetCellsAliasesResponse, error) + // GetKeyspace reads the given keyspace from the topo and returns it. + GetKeyspace(ctx context.Context, in *vtctldata.GetKeyspaceRequest, opts ...grpc.CallOption) (*vtctldata.GetKeyspaceResponse, error) + // GetKeyspaces returns the keyspace struct of all keyspaces in the topo. + GetKeyspaces(ctx context.Context, in *vtctldata.GetKeyspacesRequest, opts ...grpc.CallOption) (*vtctldata.GetKeyspacesResponse, error) + // GetSchema returns the schema for a tablet, or just the schema for the + // specified tables in that tablet. + GetSchema(ctx context.Context, in *vtctldata.GetSchemaRequest, opts ...grpc.CallOption) (*vtctldata.GetSchemaResponse, error) + // GetShard returns information about a shard in the topology. + GetShard(ctx context.Context, in *vtctldata.GetShardRequest, opts ...grpc.CallOption) (*vtctldata.GetShardResponse, error) + // GetSrvVSchema returns a the SrvVSchema for a cell. + GetSrvVSchema(ctx context.Context, in *vtctldata.GetSrvVSchemaRequest, opts ...grpc.CallOption) (*vtctldata.GetSrvVSchemaResponse, error) + // GetTablet returns information about a tablet. + GetTablet(ctx context.Context, in *vtctldata.GetTabletRequest, opts ...grpc.CallOption) (*vtctldata.GetTabletResponse, error) + // GetTablets returns tablets, optionally filtered by keyspace and shard. + GetTablets(ctx context.Context, in *vtctldata.GetTabletsRequest, opts ...grpc.CallOption) (*vtctldata.GetTabletsResponse, error) + // GetVSchema returns the vschema for a keyspace. + GetVSchema(ctx context.Context, in *vtctldata.GetVSchemaRequest, opts ...grpc.CallOption) (*vtctldata.GetVSchemaResponse, error) + // GetWorkflows returns a list of workflows for the given keyspace. + GetWorkflows(ctx context.Context, in *vtctldata.GetWorkflowsRequest, opts ...grpc.CallOption) (*vtctldata.GetWorkflowsResponse, error) + // RemoveKeyspaceCell removes the specified cell from the Cells list for all + // shards in the specified keyspace, as well as from the SrvKeyspace for that + // keyspace in that cell. + RemoveKeyspaceCell(ctx context.Context, in *vtctldata.RemoveKeyspaceCellRequest, opts ...grpc.CallOption) (*vtctldata.RemoveKeyspaceCellResponse, error) + // RemoveShardCell removes the specified cell from the specified shard's Cells + // list. + RemoveShardCell(ctx context.Context, in *vtctldata.RemoveShardCellRequest, opts ...grpc.CallOption) (*vtctldata.RemoveShardCellResponse, error) +} + +type vtctldClient struct { + cc *grpc.ClientConn +} + +func NewVtctldClient(cc *grpc.ClientConn) VtctldClient { + return &vtctldClient{cc} +} + +func (c *vtctldClient) ChangeTabletType(ctx context.Context, in *vtctldata.ChangeTabletTypeRequest, opts ...grpc.CallOption) (*vtctldata.ChangeTabletTypeResponse, error) { + out := new(vtctldata.ChangeTabletTypeResponse) + err := c.cc.Invoke(ctx, "/vtctlservice.Vtctld/ChangeTabletType", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vtctldClient) CreateKeyspace(ctx context.Context, in *vtctldata.CreateKeyspaceRequest, opts ...grpc.CallOption) (*vtctldata.CreateKeyspaceResponse, error) { + out := new(vtctldata.CreateKeyspaceResponse) + err := c.cc.Invoke(ctx, "/vtctlservice.Vtctld/CreateKeyspace", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vtctldClient) CreateShard(ctx context.Context, in *vtctldata.CreateShardRequest, opts ...grpc.CallOption) (*vtctldata.CreateShardResponse, error) { + out := new(vtctldata.CreateShardResponse) + err := c.cc.Invoke(ctx, "/vtctlservice.Vtctld/CreateShard", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vtctldClient) DeleteKeyspace(ctx context.Context, in *vtctldata.DeleteKeyspaceRequest, opts ...grpc.CallOption) (*vtctldata.DeleteKeyspaceResponse, error) { + out := new(vtctldata.DeleteKeyspaceResponse) + err := c.cc.Invoke(ctx, "/vtctlservice.Vtctld/DeleteKeyspace", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vtctldClient) DeleteShards(ctx context.Context, in *vtctldata.DeleteShardsRequest, opts ...grpc.CallOption) (*vtctldata.DeleteShardsResponse, error) { + out := new(vtctldata.DeleteShardsResponse) + err := c.cc.Invoke(ctx, "/vtctlservice.Vtctld/DeleteShards", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vtctldClient) DeleteTablets(ctx context.Context, in *vtctldata.DeleteTabletsRequest, opts ...grpc.CallOption) (*vtctldata.DeleteTabletsResponse, error) { + out := new(vtctldata.DeleteTabletsResponse) + err := c.cc.Invoke(ctx, "/vtctlservice.Vtctld/DeleteTablets", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vtctldClient) FindAllShardsInKeyspace(ctx context.Context, in *vtctldata.FindAllShardsInKeyspaceRequest, opts ...grpc.CallOption) (*vtctldata.FindAllShardsInKeyspaceResponse, error) { + out := new(vtctldata.FindAllShardsInKeyspaceResponse) + err := c.cc.Invoke(ctx, "/vtctlservice.Vtctld/FindAllShardsInKeyspace", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vtctldClient) GetBackups(ctx context.Context, in *vtctldata.GetBackupsRequest, opts ...grpc.CallOption) (*vtctldata.GetBackupsResponse, error) { + out := new(vtctldata.GetBackupsResponse) + err := c.cc.Invoke(ctx, "/vtctlservice.Vtctld/GetBackups", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vtctldClient) GetCellInfoNames(ctx context.Context, in *vtctldata.GetCellInfoNamesRequest, opts ...grpc.CallOption) (*vtctldata.GetCellInfoNamesResponse, error) { + out := new(vtctldata.GetCellInfoNamesResponse) + err := c.cc.Invoke(ctx, "/vtctlservice.Vtctld/GetCellInfoNames", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vtctldClient) GetCellInfo(ctx context.Context, in *vtctldata.GetCellInfoRequest, opts ...grpc.CallOption) (*vtctldata.GetCellInfoResponse, error) { + out := new(vtctldata.GetCellInfoResponse) + err := c.cc.Invoke(ctx, "/vtctlservice.Vtctld/GetCellInfo", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vtctldClient) GetCellsAliases(ctx context.Context, in *vtctldata.GetCellsAliasesRequest, opts ...grpc.CallOption) (*vtctldata.GetCellsAliasesResponse, error) { + out := new(vtctldata.GetCellsAliasesResponse) + err := c.cc.Invoke(ctx, "/vtctlservice.Vtctld/GetCellsAliases", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vtctldClient) GetKeyspace(ctx context.Context, in *vtctldata.GetKeyspaceRequest, opts ...grpc.CallOption) (*vtctldata.GetKeyspaceResponse, error) { + out := new(vtctldata.GetKeyspaceResponse) + err := c.cc.Invoke(ctx, "/vtctlservice.Vtctld/GetKeyspace", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vtctldClient) GetKeyspaces(ctx context.Context, in *vtctldata.GetKeyspacesRequest, opts ...grpc.CallOption) (*vtctldata.GetKeyspacesResponse, error) { + out := new(vtctldata.GetKeyspacesResponse) + err := c.cc.Invoke(ctx, "/vtctlservice.Vtctld/GetKeyspaces", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vtctldClient) GetSchema(ctx context.Context, in *vtctldata.GetSchemaRequest, opts ...grpc.CallOption) (*vtctldata.GetSchemaResponse, error) { + out := new(vtctldata.GetSchemaResponse) + err := c.cc.Invoke(ctx, "/vtctlservice.Vtctld/GetSchema", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vtctldClient) GetShard(ctx context.Context, in *vtctldata.GetShardRequest, opts ...grpc.CallOption) (*vtctldata.GetShardResponse, error) { + out := new(vtctldata.GetShardResponse) + err := c.cc.Invoke(ctx, "/vtctlservice.Vtctld/GetShard", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vtctldClient) GetSrvVSchema(ctx context.Context, in *vtctldata.GetSrvVSchemaRequest, opts ...grpc.CallOption) (*vtctldata.GetSrvVSchemaResponse, error) { + out := new(vtctldata.GetSrvVSchemaResponse) + err := c.cc.Invoke(ctx, "/vtctlservice.Vtctld/GetSrvVSchema", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vtctldClient) GetTablet(ctx context.Context, in *vtctldata.GetTabletRequest, opts ...grpc.CallOption) (*vtctldata.GetTabletResponse, error) { + out := new(vtctldata.GetTabletResponse) + err := c.cc.Invoke(ctx, "/vtctlservice.Vtctld/GetTablet", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vtctldClient) GetTablets(ctx context.Context, in *vtctldata.GetTabletsRequest, opts ...grpc.CallOption) (*vtctldata.GetTabletsResponse, error) { + out := new(vtctldata.GetTabletsResponse) + err := c.cc.Invoke(ctx, "/vtctlservice.Vtctld/GetTablets", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vtctldClient) GetVSchema(ctx context.Context, in *vtctldata.GetVSchemaRequest, opts ...grpc.CallOption) (*vtctldata.GetVSchemaResponse, error) { + out := new(vtctldata.GetVSchemaResponse) + err := c.cc.Invoke(ctx, "/vtctlservice.Vtctld/GetVSchema", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vtctldClient) GetWorkflows(ctx context.Context, in *vtctldata.GetWorkflowsRequest, opts ...grpc.CallOption) (*vtctldata.GetWorkflowsResponse, error) { + out := new(vtctldata.GetWorkflowsResponse) + err := c.cc.Invoke(ctx, "/vtctlservice.Vtctld/GetWorkflows", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vtctldClient) RemoveKeyspaceCell(ctx context.Context, in *vtctldata.RemoveKeyspaceCellRequest, opts ...grpc.CallOption) (*vtctldata.RemoveKeyspaceCellResponse, error) { + out := new(vtctldata.RemoveKeyspaceCellResponse) + err := c.cc.Invoke(ctx, "/vtctlservice.Vtctld/RemoveKeyspaceCell", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vtctldClient) RemoveShardCell(ctx context.Context, in *vtctldata.RemoveShardCellRequest, opts ...grpc.CallOption) (*vtctldata.RemoveShardCellResponse, error) { + out := new(vtctldata.RemoveShardCellResponse) + err := c.cc.Invoke(ctx, "/vtctlservice.Vtctld/RemoveShardCell", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// VtctldServer is the server API for Vtctld service. +type VtctldServer interface { + // ChangeTabletType changes the db type for the specified tablet, if possible. + // This is used primarily to arrange replicas, and it will not convert a + // primary. For that, use InitShardPrimary. + // + // NOTE: This command automatically updates the serving graph. + ChangeTabletType(context.Context, *vtctldata.ChangeTabletTypeRequest) (*vtctldata.ChangeTabletTypeResponse, error) + // CreateKeyspace creates the specified keyspace in the topology. For a + // SNAPSHOT keyspace, the request must specify the name of a base keyspace, + // as well as a snapshot time. + CreateKeyspace(context.Context, *vtctldata.CreateKeyspaceRequest) (*vtctldata.CreateKeyspaceResponse, error) + // CreateShard creates the specified shard in the topology. + CreateShard(context.Context, *vtctldata.CreateShardRequest) (*vtctldata.CreateShardResponse, error) + // DeleteKeyspace deletes the specified keyspace from the topology. In + // recursive mode, it also recursively deletes all shards in the keyspace. + // Otherwise, the keyspace must be empty (have no shards), or DeleteKeyspace + // returns an error. + DeleteKeyspace(context.Context, *vtctldata.DeleteKeyspaceRequest) (*vtctldata.DeleteKeyspaceResponse, error) + // DeleteShards deletes the specified shards from the topology. In recursive + // mode, it also deletes all tablets belonging to the shard. Otherwise, the + // shard must be empty (have no tablets) or DeleteShards returns an error for + // that shard. + DeleteShards(context.Context, *vtctldata.DeleteShardsRequest) (*vtctldata.DeleteShardsResponse, error) + // DeleteTablets deletes one or more tablets from the topology. + DeleteTablets(context.Context, *vtctldata.DeleteTabletsRequest) (*vtctldata.DeleteTabletsResponse, error) + // FindAllShardsInKeyspace returns a map of shard names to shard references + // for a given keyspace. + FindAllShardsInKeyspace(context.Context, *vtctldata.FindAllShardsInKeyspaceRequest) (*vtctldata.FindAllShardsInKeyspaceResponse, error) + // GetBackups returns all the backups for a shard. + GetBackups(context.Context, *vtctldata.GetBackupsRequest) (*vtctldata.GetBackupsResponse, error) + // GetCellInfoNames returns all the cells for which we have a CellInfo object, + // meaning we have a topology service registered. + GetCellInfoNames(context.Context, *vtctldata.GetCellInfoNamesRequest) (*vtctldata.GetCellInfoNamesResponse, error) + // GetCellInfo returns the information for a cell. + GetCellInfo(context.Context, *vtctldata.GetCellInfoRequest) (*vtctldata.GetCellInfoResponse, error) + // GetCellsAliases returns a mapping of cell alias to cells identified by that + // alias. + GetCellsAliases(context.Context, *vtctldata.GetCellsAliasesRequest) (*vtctldata.GetCellsAliasesResponse, error) + // GetKeyspace reads the given keyspace from the topo and returns it. + GetKeyspace(context.Context, *vtctldata.GetKeyspaceRequest) (*vtctldata.GetKeyspaceResponse, error) + // GetKeyspaces returns the keyspace struct of all keyspaces in the topo. + GetKeyspaces(context.Context, *vtctldata.GetKeyspacesRequest) (*vtctldata.GetKeyspacesResponse, error) + // GetSchema returns the schema for a tablet, or just the schema for the + // specified tables in that tablet. + GetSchema(context.Context, *vtctldata.GetSchemaRequest) (*vtctldata.GetSchemaResponse, error) + // GetShard returns information about a shard in the topology. + GetShard(context.Context, *vtctldata.GetShardRequest) (*vtctldata.GetShardResponse, error) + // GetSrvVSchema returns a the SrvVSchema for a cell. + GetSrvVSchema(context.Context, *vtctldata.GetSrvVSchemaRequest) (*vtctldata.GetSrvVSchemaResponse, error) + // GetTablet returns information about a tablet. + GetTablet(context.Context, *vtctldata.GetTabletRequest) (*vtctldata.GetTabletResponse, error) + // GetTablets returns tablets, optionally filtered by keyspace and shard. + GetTablets(context.Context, *vtctldata.GetTabletsRequest) (*vtctldata.GetTabletsResponse, error) + // GetVSchema returns the vschema for a keyspace. + GetVSchema(context.Context, *vtctldata.GetVSchemaRequest) (*vtctldata.GetVSchemaResponse, error) + // GetWorkflows returns a list of workflows for the given keyspace. + GetWorkflows(context.Context, *vtctldata.GetWorkflowsRequest) (*vtctldata.GetWorkflowsResponse, error) + // RemoveKeyspaceCell removes the specified cell from the Cells list for all + // shards in the specified keyspace, as well as from the SrvKeyspace for that + // keyspace in that cell. + RemoveKeyspaceCell(context.Context, *vtctldata.RemoveKeyspaceCellRequest) (*vtctldata.RemoveKeyspaceCellResponse, error) + // RemoveShardCell removes the specified cell from the specified shard's Cells + // list. + RemoveShardCell(context.Context, *vtctldata.RemoveShardCellRequest) (*vtctldata.RemoveShardCellResponse, error) +} + +// UnimplementedVtctldServer can be embedded to have forward compatible implementations. +type UnimplementedVtctldServer struct { +} + +func (*UnimplementedVtctldServer) ChangeTabletType(ctx context.Context, req *vtctldata.ChangeTabletTypeRequest) (*vtctldata.ChangeTabletTypeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ChangeTabletType not implemented") +} +func (*UnimplementedVtctldServer) CreateKeyspace(ctx context.Context, req *vtctldata.CreateKeyspaceRequest) (*vtctldata.CreateKeyspaceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateKeyspace not implemented") +} +func (*UnimplementedVtctldServer) CreateShard(ctx context.Context, req *vtctldata.CreateShardRequest) (*vtctldata.CreateShardResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateShard not implemented") +} +func (*UnimplementedVtctldServer) DeleteKeyspace(ctx context.Context, req *vtctldata.DeleteKeyspaceRequest) (*vtctldata.DeleteKeyspaceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteKeyspace not implemented") +} +func (*UnimplementedVtctldServer) DeleteShards(ctx context.Context, req *vtctldata.DeleteShardsRequest) (*vtctldata.DeleteShardsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteShards not implemented") +} +func (*UnimplementedVtctldServer) DeleteTablets(ctx context.Context, req *vtctldata.DeleteTabletsRequest) (*vtctldata.DeleteTabletsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteTablets not implemented") +} +func (*UnimplementedVtctldServer) FindAllShardsInKeyspace(ctx context.Context, req *vtctldata.FindAllShardsInKeyspaceRequest) (*vtctldata.FindAllShardsInKeyspaceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method FindAllShardsInKeyspace not implemented") +} +func (*UnimplementedVtctldServer) GetBackups(ctx context.Context, req *vtctldata.GetBackupsRequest) (*vtctldata.GetBackupsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetBackups not implemented") +} +func (*UnimplementedVtctldServer) GetCellInfoNames(ctx context.Context, req *vtctldata.GetCellInfoNamesRequest) (*vtctldata.GetCellInfoNamesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetCellInfoNames not implemented") +} +func (*UnimplementedVtctldServer) GetCellInfo(ctx context.Context, req *vtctldata.GetCellInfoRequest) (*vtctldata.GetCellInfoResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetCellInfo not implemented") +} +func (*UnimplementedVtctldServer) GetCellsAliases(ctx context.Context, req *vtctldata.GetCellsAliasesRequest) (*vtctldata.GetCellsAliasesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetCellsAliases not implemented") +} +func (*UnimplementedVtctldServer) GetKeyspace(ctx context.Context, req *vtctldata.GetKeyspaceRequest) (*vtctldata.GetKeyspaceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetKeyspace not implemented") +} +func (*UnimplementedVtctldServer) GetKeyspaces(ctx context.Context, req *vtctldata.GetKeyspacesRequest) (*vtctldata.GetKeyspacesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetKeyspaces not implemented") +} +func (*UnimplementedVtctldServer) GetSchema(ctx context.Context, req *vtctldata.GetSchemaRequest) (*vtctldata.GetSchemaResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetSchema not implemented") +} +func (*UnimplementedVtctldServer) GetShard(ctx context.Context, req *vtctldata.GetShardRequest) (*vtctldata.GetShardResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetShard not implemented") +} +func (*UnimplementedVtctldServer) GetSrvVSchema(ctx context.Context, req *vtctldata.GetSrvVSchemaRequest) (*vtctldata.GetSrvVSchemaResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetSrvVSchema not implemented") +} +func (*UnimplementedVtctldServer) GetTablet(ctx context.Context, req *vtctldata.GetTabletRequest) (*vtctldata.GetTabletResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetTablet not implemented") +} +func (*UnimplementedVtctldServer) GetTablets(ctx context.Context, req *vtctldata.GetTabletsRequest) (*vtctldata.GetTabletsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetTablets not implemented") +} +func (*UnimplementedVtctldServer) GetVSchema(ctx context.Context, req *vtctldata.GetVSchemaRequest) (*vtctldata.GetVSchemaResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetVSchema not implemented") +} +func (*UnimplementedVtctldServer) GetWorkflows(ctx context.Context, req *vtctldata.GetWorkflowsRequest) (*vtctldata.GetWorkflowsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetWorkflows not implemented") +} +func (*UnimplementedVtctldServer) RemoveKeyspaceCell(ctx context.Context, req *vtctldata.RemoveKeyspaceCellRequest) (*vtctldata.RemoveKeyspaceCellResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RemoveKeyspaceCell not implemented") +} +func (*UnimplementedVtctldServer) RemoveShardCell(ctx context.Context, req *vtctldata.RemoveShardCellRequest) (*vtctldata.RemoveShardCellResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RemoveShardCell not implemented") +} + +func RegisterVtctldServer(s *grpc.Server, srv VtctldServer) { + s.RegisterService(&_Vtctld_serviceDesc, srv) +} + +func _Vtctld_ChangeTabletType_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(vtctldata.ChangeTabletTypeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VtctldServer).ChangeTabletType(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/vtctlservice.Vtctld/ChangeTabletType", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VtctldServer).ChangeTabletType(ctx, req.(*vtctldata.ChangeTabletTypeRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vtctld_CreateKeyspace_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(vtctldata.CreateKeyspaceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VtctldServer).CreateKeyspace(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/vtctlservice.Vtctld/CreateKeyspace", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VtctldServer).CreateKeyspace(ctx, req.(*vtctldata.CreateKeyspaceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vtctld_CreateShard_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(vtctldata.CreateShardRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VtctldServer).CreateShard(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/vtctlservice.Vtctld/CreateShard", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VtctldServer).CreateShard(ctx, req.(*vtctldata.CreateShardRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vtctld_DeleteKeyspace_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(vtctldata.DeleteKeyspaceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VtctldServer).DeleteKeyspace(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/vtctlservice.Vtctld/DeleteKeyspace", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VtctldServer).DeleteKeyspace(ctx, req.(*vtctldata.DeleteKeyspaceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vtctld_DeleteShards_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(vtctldata.DeleteShardsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VtctldServer).DeleteShards(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/vtctlservice.Vtctld/DeleteShards", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VtctldServer).DeleteShards(ctx, req.(*vtctldata.DeleteShardsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vtctld_DeleteTablets_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(vtctldata.DeleteTabletsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VtctldServer).DeleteTablets(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/vtctlservice.Vtctld/DeleteTablets", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VtctldServer).DeleteTablets(ctx, req.(*vtctldata.DeleteTabletsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vtctld_FindAllShardsInKeyspace_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(vtctldata.FindAllShardsInKeyspaceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VtctldServer).FindAllShardsInKeyspace(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/vtctlservice.Vtctld/FindAllShardsInKeyspace", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VtctldServer).FindAllShardsInKeyspace(ctx, req.(*vtctldata.FindAllShardsInKeyspaceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vtctld_GetBackups_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(vtctldata.GetBackupsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VtctldServer).GetBackups(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/vtctlservice.Vtctld/GetBackups", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VtctldServer).GetBackups(ctx, req.(*vtctldata.GetBackupsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vtctld_GetCellInfoNames_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(vtctldata.GetCellInfoNamesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VtctldServer).GetCellInfoNames(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/vtctlservice.Vtctld/GetCellInfoNames", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VtctldServer).GetCellInfoNames(ctx, req.(*vtctldata.GetCellInfoNamesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vtctld_GetCellInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(vtctldata.GetCellInfoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VtctldServer).GetCellInfo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/vtctlservice.Vtctld/GetCellInfo", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VtctldServer).GetCellInfo(ctx, req.(*vtctldata.GetCellInfoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vtctld_GetCellsAliases_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(vtctldata.GetCellsAliasesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VtctldServer).GetCellsAliases(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/vtctlservice.Vtctld/GetCellsAliases", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VtctldServer).GetCellsAliases(ctx, req.(*vtctldata.GetCellsAliasesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vtctld_GetKeyspace_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(vtctldata.GetKeyspaceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VtctldServer).GetKeyspace(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/vtctlservice.Vtctld/GetKeyspace", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VtctldServer).GetKeyspace(ctx, req.(*vtctldata.GetKeyspaceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vtctld_GetKeyspaces_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(vtctldata.GetKeyspacesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VtctldServer).GetKeyspaces(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/vtctlservice.Vtctld/GetKeyspaces", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VtctldServer).GetKeyspaces(ctx, req.(*vtctldata.GetKeyspacesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vtctld_GetSchema_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(vtctldata.GetSchemaRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VtctldServer).GetSchema(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/vtctlservice.Vtctld/GetSchema", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VtctldServer).GetSchema(ctx, req.(*vtctldata.GetSchemaRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vtctld_GetShard_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(vtctldata.GetShardRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VtctldServer).GetShard(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/vtctlservice.Vtctld/GetShard", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VtctldServer).GetShard(ctx, req.(*vtctldata.GetShardRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vtctld_GetSrvVSchema_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(vtctldata.GetSrvVSchemaRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VtctldServer).GetSrvVSchema(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/vtctlservice.Vtctld/GetSrvVSchema", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VtctldServer).GetSrvVSchema(ctx, req.(*vtctldata.GetSrvVSchemaRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vtctld_GetTablet_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(vtctldata.GetTabletRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VtctldServer).GetTablet(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/vtctlservice.Vtctld/GetTablet", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VtctldServer).GetTablet(ctx, req.(*vtctldata.GetTabletRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vtctld_GetTablets_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(vtctldata.GetTabletsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VtctldServer).GetTablets(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/vtctlservice.Vtctld/GetTablets", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VtctldServer).GetTablets(ctx, req.(*vtctldata.GetTabletsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vtctld_GetVSchema_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(vtctldata.GetVSchemaRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VtctldServer).GetVSchema(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/vtctlservice.Vtctld/GetVSchema", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VtctldServer).GetVSchema(ctx, req.(*vtctldata.GetVSchemaRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vtctld_GetWorkflows_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(vtctldata.GetWorkflowsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VtctldServer).GetWorkflows(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/vtctlservice.Vtctld/GetWorkflows", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VtctldServer).GetWorkflows(ctx, req.(*vtctldata.GetWorkflowsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vtctld_RemoveKeyspaceCell_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(vtctldata.RemoveKeyspaceCellRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VtctldServer).RemoveKeyspaceCell(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/vtctlservice.Vtctld/RemoveKeyspaceCell", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VtctldServer).RemoveKeyspaceCell(ctx, req.(*vtctldata.RemoveKeyspaceCellRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Vtctld_RemoveShardCell_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(vtctldata.RemoveShardCellRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VtctldServer).RemoveShardCell(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/vtctlservice.Vtctld/RemoveShardCell", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VtctldServer).RemoveShardCell(ctx, req.(*vtctldata.RemoveShardCellRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Vtctld_serviceDesc = grpc.ServiceDesc{ + ServiceName: "vtctlservice.Vtctld", + HandlerType: (*VtctldServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "ChangeTabletType", + Handler: _Vtctld_ChangeTabletType_Handler, + }, + { + MethodName: "CreateKeyspace", + Handler: _Vtctld_CreateKeyspace_Handler, + }, + { + MethodName: "CreateShard", + Handler: _Vtctld_CreateShard_Handler, + }, + { + MethodName: "DeleteKeyspace", + Handler: _Vtctld_DeleteKeyspace_Handler, + }, + { + MethodName: "DeleteShards", + Handler: _Vtctld_DeleteShards_Handler, + }, + { + MethodName: "DeleteTablets", + Handler: _Vtctld_DeleteTablets_Handler, + }, + { + MethodName: "FindAllShardsInKeyspace", + Handler: _Vtctld_FindAllShardsInKeyspace_Handler, + }, + { + MethodName: "GetBackups", + Handler: _Vtctld_GetBackups_Handler, + }, + { + MethodName: "GetCellInfoNames", + Handler: _Vtctld_GetCellInfoNames_Handler, + }, + { + MethodName: "GetCellInfo", + Handler: _Vtctld_GetCellInfo_Handler, + }, + { + MethodName: "GetCellsAliases", + Handler: _Vtctld_GetCellsAliases_Handler, + }, + { + MethodName: "GetKeyspace", + Handler: _Vtctld_GetKeyspace_Handler, + }, + { + MethodName: "GetKeyspaces", + Handler: _Vtctld_GetKeyspaces_Handler, + }, + { + MethodName: "GetSchema", + Handler: _Vtctld_GetSchema_Handler, + }, + { + MethodName: "GetShard", + Handler: _Vtctld_GetShard_Handler, + }, + { + MethodName: "GetSrvVSchema", + Handler: _Vtctld_GetSrvVSchema_Handler, + }, + { + MethodName: "GetTablet", + Handler: _Vtctld_GetTablet_Handler, + }, + { + MethodName: "GetTablets", + Handler: _Vtctld_GetTablets_Handler, + }, + { + MethodName: "GetVSchema", + Handler: _Vtctld_GetVSchema_Handler, + }, + { + MethodName: "GetWorkflows", + Handler: _Vtctld_GetWorkflows_Handler, + }, + { + MethodName: "RemoveKeyspaceCell", + Handler: _Vtctld_RemoveKeyspaceCell_Handler, + }, + { + MethodName: "RemoveShardCell", + Handler: _Vtctld_RemoveShardCell_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "vtctlservice.proto", +} diff --git a/go/vt/topo/tablet.go b/go/vt/topo/tablet.go index 06d44cab813..314cb8fcc8b 100644 --- a/go/vt/topo/tablet.go +++ b/go/vt/topo/tablet.go @@ -470,3 +470,21 @@ func (ts *Server) GetTabletsByCell(ctx context.Context, cell string) ([]*topodat } return result, nil } + +// ParseServingTabletType parses the tablet type into the enum, and makes sure +// that the enum is of serving type (MASTER, REPLICA, RDONLY/BATCH). +// +// Note: This function more closely belongs in topoproto, but that would create +// a circular import between packages topo and topoproto. +func ParseServingTabletType(param string) (topodatapb.TabletType, error) { + servedType, err := topoproto.ParseTabletType(param) + if err != nil { + return topodatapb.TabletType_UNKNOWN, err + } + + if !IsInServingGraph(servedType) { + return topodatapb.TabletType_UNKNOWN, fmt.Errorf("served_type has to be in the serving graph, not %v", param) + } + + return servedType, nil +} diff --git a/go/vt/topo/topoproto/keyspace.go b/go/vt/topo/topoproto/keyspace.go index e409f763fd7..625ec0b7293 100644 --- a/go/vt/topo/topoproto/keyspace.go +++ b/go/vt/topo/topoproto/keyspace.go @@ -23,7 +23,7 @@ import ( topodatapb "vitess.io/vitess/go/vt/proto/topodata" ) -// ParseKeyspaceType parses a string into a KeyspaceType +// ParseKeyspaceType parses a string into a KeyspaceType. func ParseKeyspaceType(param string) (topodatapb.KeyspaceType, error) { value, ok := topodatapb.KeyspaceType_value[strings.ToUpper(param)] if !ok { @@ -32,3 +32,13 @@ func ParseKeyspaceType(param string) (topodatapb.KeyspaceType, error) { } return topodatapb.KeyspaceType(value), nil } + +// KeyspaceTypeString returns the string representation of a KeyspaceType. +func KeyspaceTypeString(kt topodatapb.KeyspaceType) string { + str, ok := topodatapb.KeyspaceType_name[int32(kt)] + if !ok { + return "UNKNOWN" + } + + return str +} diff --git a/go/vt/topotools/tablet.go b/go/vt/topotools/tablet.go index b8fc57b1130..9022c322a1d 100644 --- a/go/vt/topotools/tablet.go +++ b/go/vt/topotools/tablet.go @@ -102,6 +102,44 @@ func CheckOwnership(oldTablet, newTablet *topodatapb.Tablet) error { return nil } +// IsPrimaryTablet is a helper function to determine whether the current tablet +// is a primary before we allow its tablet record to be deleted. The canonical +// way to determine the only true primary in a shard is to list all the tablets +// and find the one with the highest MasterTermStartTime among the ones that +// claim to be master. +// +// We err on the side of caution here, i.e. we should never return false for +// a true primary tablet, but it is okay to return true for a tablet that isn't +// the true primary. This can occur if someone issues a DeleteTablet while +// the system is in transition (a reparenting event is in progress and parts of +// the topo have not yet been updated). +func IsPrimaryTablet(ctx context.Context, ts *topo.Server, ti *topo.TabletInfo) (bool, error) { + // Tablet record claims to be non-master, we believe it + if ti.Type != topodatapb.TabletType_MASTER { + return false, nil + } + + si, err := ts.GetShard(ctx, ti.Keyspace, ti.Shard) + if err != nil { + // strictly speaking it isn't correct to return false here, the tablet + // status is unknown + return false, err + } + + // Tablet record claims to be master, and shard record matches + if topoproto.TabletAliasEqual(si.MasterAlias, ti.Tablet.Alias) { + return true, nil + } + + // Shard record has another tablet as master, so check MasterTermStartTime + // If tablet record's MasterTermStartTime is later than the one in the shard + // record, then the tablet is master + tabletMTST := ti.GetMasterTermStartTime() + shardMTST := si.GetMasterTermStartTime() + + return tabletMTST.After(shardMTST), nil +} + // DeleteTablet removes a tablet record from the topology: // - the replication data record if any // - the tablet record diff --git a/go/vt/vtctl/endtoend/get_schema_test.go b/go/vt/vtctl/endtoend/get_schema_test.go new file mode 100644 index 00000000000..ddb0c204048 --- /dev/null +++ b/go/vt/vtctl/endtoend/get_schema_test.go @@ -0,0 +1,235 @@ +package endtoend + +import ( + "context" + "fmt" + "testing" + + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/json2" + "vitess.io/vitess/go/vt/logutil" + "vitess.io/vitess/go/vt/topo/memorytopo" + "vitess.io/vitess/go/vt/topo/topoproto" + "vitess.io/vitess/go/vt/vtctl" + "vitess.io/vitess/go/vt/vttablet/faketmclient" + "vitess.io/vitess/go/vt/vttablet/tmclient" + "vitess.io/vitess/go/vt/wrangler" + + querypb "vitess.io/vitess/go/vt/proto/query" + tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" + topodatapb "vitess.io/vitess/go/vt/proto/topodata" +) + +type fakeTabletManagerClient struct { + tmclient.TabletManagerClient + schemas map[string]*tabletmanagerdatapb.SchemaDefinition +} + +func newTMClient() *fakeTabletManagerClient { + return &fakeTabletManagerClient{ + TabletManagerClient: faketmclient.NewFakeTabletManagerClient(), + schemas: map[string]*tabletmanagerdatapb.SchemaDefinition{}, + } +} + +func (c *fakeTabletManagerClient) GetSchema(ctx context.Context, tablet *topodatapb.Tablet, tablets []string, excludeTables []string, includeViews bool) (*tabletmanagerdatapb.SchemaDefinition, error) { + key := topoproto.TabletAliasString(tablet.Alias) + + schema, ok := c.schemas[key] + if !ok { + return nil, fmt.Errorf("no schemas for %s", key) + } + + return schema, nil +} + +func TestGetSchema(t *testing.T) { + ctx := context.Background() + + topo := memorytopo.NewServer("zone1", "zone2", "zone3") + + tablet := &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: uuid.New().ID(), + }, + Hostname: "abcd", + Keyspace: "testkeyspace", + Shard: "-", + Type: topodatapb.TabletType_MASTER, + } + require.NoError(t, topo.CreateTablet(ctx, tablet)) + + sd := &tabletmanagerdatapb.SchemaDefinition{ + TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ + { + Name: "foo", + RowCount: 1000, + DataLength: 1000000, + Schema: `CREATE TABLE foo ( + id INT(11) NOT NULL, + name VARCHAR(255) NOT NULL, + PRIMARY KEY(id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`, + Columns: []string{ + "id", + "name", + }, + PrimaryKeyColumns: []string{ + "id", + }, + Fields: []*querypb.Field{ + { + Name: "id", + Type: querypb.Type_INT32, + Table: "foo", + OrgTable: "foo", + Database: "vt_testkeyspace", + OrgName: "id", + ColumnLength: 11, + Charset: 63, + Decimals: 0, + }, + { + Name: "name", + Type: querypb.Type_VARCHAR, + Table: "foo", + OrgTable: "foo", + Database: "vt_testkeyspace", + OrgName: "name", + ColumnLength: 1020, + Charset: 45, + Decimals: 0, + }, + }, + }, + { + Name: "bar", + RowCount: 1, + DataLength: 10, + Schema: `CREATE TABLE bar ( + id INT(11) NOT NULL + foo_id INT(11) NOT NULL + is_active TINYINT(1) NOT NULL DEFAULT 1 +) ENGINE=InnoDB`, + Columns: []string{ + "id", + "foo_id", + "is_active", + }, + PrimaryKeyColumns: []string{ + "id", + }, + Fields: []*querypb.Field{ + { + Name: "id", + Type: querypb.Type_INT32, + Table: "bar", + OrgTable: "bar", + Database: "vt_testkeyspace", + OrgName: "id", + ColumnLength: 11, + Charset: 63, + Decimals: 0, + }, + { + Name: "foo_id", + Type: querypb.Type_INT32, + Table: "bar", + OrgTable: "bar", + Database: "vt_testkeyspace", + OrgName: "foo_id", + ColumnLength: 11, + Charset: 63, + Decimals: 0, + }, + { + Name: "is_active", + Type: querypb.Type_INT8, + Table: "bar", + OrgTable: "bar", + Database: "vt_testkeyspace", + OrgName: "is_active", + ColumnLength: 1, + Charset: 63, + Decimals: 0, + }, + }, + }, + }, + } + + tmc := newTMClient() + tmc.schemas[topoproto.TabletAliasString(tablet.Alias)] = sd + + logger := logutil.NewMemoryLogger() + + err := vtctl.RunCommand(ctx, wrangler.New(logger, topo, tmc), []string{ + "GetSchema", + topoproto.TabletAliasString(tablet.Alias), + }) + require.NoError(t, err) + + events := logger.Events + assert.Equal(t, 1, len(events), "expected 1 event from GetSchema") + val := events[0].Value + + actual := &tabletmanagerdatapb.SchemaDefinition{} + err = json2.Unmarshal([]byte(val), actual) + require.NoError(t, err) + + assert.Equal(t, sd, actual) + + // reset for the next invocation, where we verify that passing + // -table_sizes_only does not include the create table statement or columns. + logger.Events = nil + sd = &tabletmanagerdatapb.SchemaDefinition{ + TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ + { + Name: "foo", + RowCount: 1000, + DataLength: 1000000, + Columns: []string{}, + PrimaryKeyColumns: []string{}, + Fields: []*querypb.Field{}, + }, + { + Name: "bar", + RowCount: 1, + DataLength: 10, + Columns: []string{}, + PrimaryKeyColumns: []string{}, + Fields: []*querypb.Field{}, + }, + }, + } + + err = vtctl.RunCommand(ctx, wrangler.New(logger, topo, tmc), []string{ + "GetSchema", + "-table_sizes_only", + topoproto.TabletAliasString(tablet.Alias), + }) + require.NoError(t, err) + + events = logger.Events + assert.Equal(t, 1, len(events), "expected 1 event from GetSchema") + val = events[0].Value + + actual = &tabletmanagerdatapb.SchemaDefinition{} + err = json2.Unmarshal([]byte(val), actual) + require.NoError(t, err) + + assert.Equal(t, sd, actual) +} + +func init() { + // enforce we will use the right protocol (gRPC) (note the + // client is unused, but it is initialized, so it needs to exist) + *tmclient.TabletManagerProtocol = "grpc" + tmclient.RegisterTabletManagerClientFactory("grpc", func() tmclient.TabletManagerClient { + return nil + }) +} diff --git a/go/vt/vtctl/grpcvtctldclient/Makefile b/go/vt/vtctl/grpcvtctldclient/Makefile new file mode 100644 index 00000000000..d40d962d709 --- /dev/null +++ b/go/vt/vtctl/grpcvtctldclient/Makefile @@ -0,0 +1,19 @@ +# Copyright 2021 The Vitess Authors. +# +# 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. + +MAKEFLAGS = -s + +generate: + go generate ./... + gofmt -w client_gen.go diff --git a/go/vt/vtctl/grpcvtctldclient/client.go b/go/vt/vtctl/grpcvtctldclient/client.go new file mode 100644 index 00000000000..af3e8e07802 --- /dev/null +++ b/go/vt/vtctl/grpcvtctldclient/client.go @@ -0,0 +1,86 @@ +/* +Copyright 2021 The Vitess Authors. + +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 grpcvtctldclient contains the gRPC version of the vtctld client +// protocol. +package grpcvtctldclient + +import ( + "flag" + + "google.golang.org/grpc" + + "vitess.io/vitess/go/vt/grpcclient" + "vitess.io/vitess/go/vt/vtctl/vtctldclient" + + vtctlservicepb "vitess.io/vitess/go/vt/proto/vtctlservice" +) + +const connClosedMsg = "grpc: the client connection is closed" + +// (TODO:@amason) - These flags match exactly the flags used in grpcvtctlclient. +// If a program attempts to import both of these packages, it will panic during +// startup due to the duplicate flags. +// +// For everything else I've been doing a sed s/vtctl/vtctld, but I cannot do +// that here, since these flags are already "vtctld_*". My other options are to +// name them "vtctld2_*" or to omit them completely. +// +// Not to pitch project ideas in comments, but a nice project to solve +var ( + cert = flag.String("vtctld_grpc_cert", "", "the cert to use to connect") + key = flag.String("vtctld_grpc_key", "", "the key to use to connect") + ca = flag.String("vtctld_grpc_ca", "", "the server ca to use to validate servers when connecting") + name = flag.String("vtctld_grpc_server_name", "", "the server name to use to validate server certificate") +) + +type gRPCVtctldClient struct { + cc *grpc.ClientConn + c vtctlservicepb.VtctldClient +} + +//go:generate -command grpcvtctldclient go run ./codegen +//go:generate grpcvtctldclient -out client_gen.go + +func gRPCVtctldClientFactory(addr string) (vtctldclient.VtctldClient, error) { + opt, err := grpcclient.SecureDialOption(*cert, *key, *ca, *name) + if err != nil { + return nil, err + } + + conn, err := grpcclient.Dial(addr, grpcclient.FailFast(false), opt) + if err != nil { + return nil, err + } + + return &gRPCVtctldClient{ + cc: conn, + c: vtctlservicepb.NewVtctldClient(conn), + }, nil +} + +func (client *gRPCVtctldClient) Close() error { + err := client.cc.Close() + if err == nil { + client.c = nil + } + + return err +} + +func init() { + vtctldclient.Register("grpc", gRPCVtctldClientFactory) +} diff --git a/go/vt/vtctl/grpcvtctldclient/client_gen.go b/go/vt/vtctl/grpcvtctldclient/client_gen.go new file mode 100644 index 00000000000..a36b5c13797 --- /dev/null +++ b/go/vt/vtctl/grpcvtctldclient/client_gen.go @@ -0,0 +1,227 @@ +// Code generated by grpcvtctldclient-generator. DO NOT EDIT. + +/* +Copyright 2021 The Vitess Authors. + +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 grpcvtctldclient + +import ( + "context" + + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" +) + +// ChangeTabletType is part of the vtctlservicepb.VtctldClient interface. +func (client *gRPCVtctldClient) ChangeTabletType(ctx context.Context, in *vtctldatapb.ChangeTabletTypeRequest, opts ...grpc.CallOption) (*vtctldatapb.ChangeTabletTypeResponse, error) { + if client.c == nil { + return nil, status.Error(codes.Unavailable, connClosedMsg) + } + + return client.c.ChangeTabletType(ctx, in, opts...) +} + +// CreateKeyspace is part of the vtctlservicepb.VtctldClient interface. +func (client *gRPCVtctldClient) CreateKeyspace(ctx context.Context, in *vtctldatapb.CreateKeyspaceRequest, opts ...grpc.CallOption) (*vtctldatapb.CreateKeyspaceResponse, error) { + if client.c == nil { + return nil, status.Error(codes.Unavailable, connClosedMsg) + } + + return client.c.CreateKeyspace(ctx, in, opts...) +} + +// CreateShard is part of the vtctlservicepb.VtctldClient interface. +func (client *gRPCVtctldClient) CreateShard(ctx context.Context, in *vtctldatapb.CreateShardRequest, opts ...grpc.CallOption) (*vtctldatapb.CreateShardResponse, error) { + if client.c == nil { + return nil, status.Error(codes.Unavailable, connClosedMsg) + } + + return client.c.CreateShard(ctx, in, opts...) +} + +// DeleteKeyspace is part of the vtctlservicepb.VtctldClient interface. +func (client *gRPCVtctldClient) DeleteKeyspace(ctx context.Context, in *vtctldatapb.DeleteKeyspaceRequest, opts ...grpc.CallOption) (*vtctldatapb.DeleteKeyspaceResponse, error) { + if client.c == nil { + return nil, status.Error(codes.Unavailable, connClosedMsg) + } + + return client.c.DeleteKeyspace(ctx, in, opts...) +} + +// DeleteShards is part of the vtctlservicepb.VtctldClient interface. +func (client *gRPCVtctldClient) DeleteShards(ctx context.Context, in *vtctldatapb.DeleteShardsRequest, opts ...grpc.CallOption) (*vtctldatapb.DeleteShardsResponse, error) { + if client.c == nil { + return nil, status.Error(codes.Unavailable, connClosedMsg) + } + + return client.c.DeleteShards(ctx, in, opts...) +} + +// DeleteTablets is part of the vtctlservicepb.VtctldClient interface. +func (client *gRPCVtctldClient) DeleteTablets(ctx context.Context, in *vtctldatapb.DeleteTabletsRequest, opts ...grpc.CallOption) (*vtctldatapb.DeleteTabletsResponse, error) { + if client.c == nil { + return nil, status.Error(codes.Unavailable, connClosedMsg) + } + + return client.c.DeleteTablets(ctx, in, opts...) +} + +// FindAllShardsInKeyspace is part of the vtctlservicepb.VtctldClient interface. +func (client *gRPCVtctldClient) FindAllShardsInKeyspace(ctx context.Context, in *vtctldatapb.FindAllShardsInKeyspaceRequest, opts ...grpc.CallOption) (*vtctldatapb.FindAllShardsInKeyspaceResponse, error) { + if client.c == nil { + return nil, status.Error(codes.Unavailable, connClosedMsg) + } + + return client.c.FindAllShardsInKeyspace(ctx, in, opts...) +} + +// GetBackups is part of the vtctlservicepb.VtctldClient interface. +func (client *gRPCVtctldClient) GetBackups(ctx context.Context, in *vtctldatapb.GetBackupsRequest, opts ...grpc.CallOption) (*vtctldatapb.GetBackupsResponse, error) { + if client.c == nil { + return nil, status.Error(codes.Unavailable, connClosedMsg) + } + + return client.c.GetBackups(ctx, in, opts...) +} + +// GetCellInfo is part of the vtctlservicepb.VtctldClient interface. +func (client *gRPCVtctldClient) GetCellInfo(ctx context.Context, in *vtctldatapb.GetCellInfoRequest, opts ...grpc.CallOption) (*vtctldatapb.GetCellInfoResponse, error) { + if client.c == nil { + return nil, status.Error(codes.Unavailable, connClosedMsg) + } + + return client.c.GetCellInfo(ctx, in, opts...) +} + +// GetCellInfoNames is part of the vtctlservicepb.VtctldClient interface. +func (client *gRPCVtctldClient) GetCellInfoNames(ctx context.Context, in *vtctldatapb.GetCellInfoNamesRequest, opts ...grpc.CallOption) (*vtctldatapb.GetCellInfoNamesResponse, error) { + if client.c == nil { + return nil, status.Error(codes.Unavailable, connClosedMsg) + } + + return client.c.GetCellInfoNames(ctx, in, opts...) +} + +// GetCellsAliases is part of the vtctlservicepb.VtctldClient interface. +func (client *gRPCVtctldClient) GetCellsAliases(ctx context.Context, in *vtctldatapb.GetCellsAliasesRequest, opts ...grpc.CallOption) (*vtctldatapb.GetCellsAliasesResponse, error) { + if client.c == nil { + return nil, status.Error(codes.Unavailable, connClosedMsg) + } + + return client.c.GetCellsAliases(ctx, in, opts...) +} + +// GetKeyspace is part of the vtctlservicepb.VtctldClient interface. +func (client *gRPCVtctldClient) GetKeyspace(ctx context.Context, in *vtctldatapb.GetKeyspaceRequest, opts ...grpc.CallOption) (*vtctldatapb.GetKeyspaceResponse, error) { + if client.c == nil { + return nil, status.Error(codes.Unavailable, connClosedMsg) + } + + return client.c.GetKeyspace(ctx, in, opts...) +} + +// GetKeyspaces is part of the vtctlservicepb.VtctldClient interface. +func (client *gRPCVtctldClient) GetKeyspaces(ctx context.Context, in *vtctldatapb.GetKeyspacesRequest, opts ...grpc.CallOption) (*vtctldatapb.GetKeyspacesResponse, error) { + if client.c == nil { + return nil, status.Error(codes.Unavailable, connClosedMsg) + } + + return client.c.GetKeyspaces(ctx, in, opts...) +} + +// GetSchema is part of the vtctlservicepb.VtctldClient interface. +func (client *gRPCVtctldClient) GetSchema(ctx context.Context, in *vtctldatapb.GetSchemaRequest, opts ...grpc.CallOption) (*vtctldatapb.GetSchemaResponse, error) { + if client.c == nil { + return nil, status.Error(codes.Unavailable, connClosedMsg) + } + + return client.c.GetSchema(ctx, in, opts...) +} + +// GetShard is part of the vtctlservicepb.VtctldClient interface. +func (client *gRPCVtctldClient) GetShard(ctx context.Context, in *vtctldatapb.GetShardRequest, opts ...grpc.CallOption) (*vtctldatapb.GetShardResponse, error) { + if client.c == nil { + return nil, status.Error(codes.Unavailable, connClosedMsg) + } + + return client.c.GetShard(ctx, in, opts...) +} + +// GetSrvVSchema is part of the vtctlservicepb.VtctldClient interface. +func (client *gRPCVtctldClient) GetSrvVSchema(ctx context.Context, in *vtctldatapb.GetSrvVSchemaRequest, opts ...grpc.CallOption) (*vtctldatapb.GetSrvVSchemaResponse, error) { + if client.c == nil { + return nil, status.Error(codes.Unavailable, connClosedMsg) + } + + return client.c.GetSrvVSchema(ctx, in, opts...) +} + +// GetTablet is part of the vtctlservicepb.VtctldClient interface. +func (client *gRPCVtctldClient) GetTablet(ctx context.Context, in *vtctldatapb.GetTabletRequest, opts ...grpc.CallOption) (*vtctldatapb.GetTabletResponse, error) { + if client.c == nil { + return nil, status.Error(codes.Unavailable, connClosedMsg) + } + + return client.c.GetTablet(ctx, in, opts...) +} + +// GetTablets is part of the vtctlservicepb.VtctldClient interface. +func (client *gRPCVtctldClient) GetTablets(ctx context.Context, in *vtctldatapb.GetTabletsRequest, opts ...grpc.CallOption) (*vtctldatapb.GetTabletsResponse, error) { + if client.c == nil { + return nil, status.Error(codes.Unavailable, connClosedMsg) + } + + return client.c.GetTablets(ctx, in, opts...) +} + +// GetVSchema is part of the vtctlservicepb.VtctldClient interface. +func (client *gRPCVtctldClient) GetVSchema(ctx context.Context, in *vtctldatapb.GetVSchemaRequest, opts ...grpc.CallOption) (*vtctldatapb.GetVSchemaResponse, error) { + if client.c == nil { + return nil, status.Error(codes.Unavailable, connClosedMsg) + } + + return client.c.GetVSchema(ctx, in, opts...) +} + +// GetWorkflows is part of the vtctlservicepb.VtctldClient interface. +func (client *gRPCVtctldClient) GetWorkflows(ctx context.Context, in *vtctldatapb.GetWorkflowsRequest, opts ...grpc.CallOption) (*vtctldatapb.GetWorkflowsResponse, error) { + if client.c == nil { + return nil, status.Error(codes.Unavailable, connClosedMsg) + } + + return client.c.GetWorkflows(ctx, in, opts...) +} + +// RemoveKeyspaceCell is part of the vtctlservicepb.VtctldClient interface. +func (client *gRPCVtctldClient) RemoveKeyspaceCell(ctx context.Context, in *vtctldatapb.RemoveKeyspaceCellRequest, opts ...grpc.CallOption) (*vtctldatapb.RemoveKeyspaceCellResponse, error) { + if client.c == nil { + return nil, status.Error(codes.Unavailable, connClosedMsg) + } + + return client.c.RemoveKeyspaceCell(ctx, in, opts...) +} + +// RemoveShardCell is part of the vtctlservicepb.VtctldClient interface. +func (client *gRPCVtctldClient) RemoveShardCell(ctx context.Context, in *vtctldatapb.RemoveShardCellRequest, opts ...grpc.CallOption) (*vtctldatapb.RemoveShardCellResponse, error) { + if client.c == nil { + return nil, status.Error(codes.Unavailable, connClosedMsg) + } + + return client.c.RemoveShardCell(ctx, in, opts...) +} diff --git a/go/vt/vtctl/grpcvtctldclient/client_test.go b/go/vt/vtctl/grpcvtctldclient/client_test.go new file mode 100644 index 00000000000..9cee9efdbde --- /dev/null +++ b/go/vt/vtctl/grpcvtctldclient/client_test.go @@ -0,0 +1,169 @@ +/* +Copyright 2021 The Vitess Authors. + +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 grpcvtctldclient_test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/net/nettest" + "google.golang.org/grpc" + "vitess.io/vitess/go/vt/topo" + "vitess.io/vitess/go/vt/topo/memorytopo" + "vitess.io/vitess/go/vt/vtctl/grpcvtctldserver" + "vitess.io/vitess/go/vt/vtctl/grpcvtctldserver/testutil" + "vitess.io/vitess/go/vt/vtctl/vtctldclient" + + topodatapb "vitess.io/vitess/go/vt/proto/topodata" + vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" + vtctlservicepb "vitess.io/vitess/go/vt/proto/vtctlservice" +) + +// annoyingly, this is duplicated with theu tests in package grpcvtctldserver. +// fine for now, I suppose. +func addKeyspace(ctx context.Context, t *testing.T, ts *topo.Server, ks *vtctldatapb.Keyspace) { + in := *ks.Keyspace // take a copy to avoid the XXX_ fields changing + + err := ts.CreateKeyspace(ctx, ks.Name, &in) + require.NoError(t, err) +} + +func withTestServer( + t *testing.T, + server vtctlservicepb.VtctldServer, + test func(t *testing.T, client vtctldclient.VtctldClient), +) { + lis, err := nettest.NewLocalListener("tcp") + require.NoError(t, err, "cannot create nettest listener") + + defer lis.Close() + + s := grpc.NewServer() + vtctlservicepb.RegisterVtctldServer(s, server) + + go s.Serve(lis) + defer s.Stop() + + client, err := vtctldclient.New("grpc", lis.Addr().String()) + require.NoError(t, err, "cannot create vtctld client") + + test(t, client) +} + +func TestFindAllShardsInKeyspace(t *testing.T) { + ctx := context.Background() + ts := memorytopo.NewServer("cell1") + vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { + return grpcvtctldserver.NewVtctldServer(ts) + }) + + withTestServer(t, vtctld, func(t *testing.T, client vtctldclient.VtctldClient) { + ks := &vtctldatapb.Keyspace{ + Name: "testkeyspace", + Keyspace: &topodatapb.Keyspace{}, + } + addKeyspace(ctx, t, ts, ks) + + si1, err := ts.GetOrCreateShard(ctx, ks.Name, "-80") + require.NoError(t, err) + si2, err := ts.GetOrCreateShard(ctx, ks.Name, "80-") + require.NoError(t, err) + + resp, err := client.FindAllShardsInKeyspace(ctx, &vtctldatapb.FindAllShardsInKeyspaceRequest{Keyspace: ks.Name}) + assert.NoError(t, err) + assert.NotNil(t, resp) + + expected := map[string]*vtctldatapb.Shard{ + "-80": { + Keyspace: ks.Name, + Name: "-80", + Shard: si1.Shard, + }, + "80-": { + Keyspace: ks.Name, + Name: "80-", + Shard: si2.Shard, + }, + } + + assert.Equal(t, expected, resp.Shards) + + client.Close() + _, err = client.FindAllShardsInKeyspace(ctx, &vtctldatapb.FindAllShardsInKeyspaceRequest{Keyspace: ks.Name}) + assert.Error(t, err) + }) +} + +func TestGetKeyspace(t *testing.T) { + ctx := context.Background() + + ts := memorytopo.NewServer("cell1") + vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { + return grpcvtctldserver.NewVtctldServer(ts) + }) + + withTestServer(t, vtctld, func(t *testing.T, client vtctldclient.VtctldClient) { + expected := &vtctldatapb.GetKeyspaceResponse{ + Keyspace: &vtctldatapb.Keyspace{ + Name: "testkeyspace", + Keyspace: &topodatapb.Keyspace{ + ShardingColumnName: "col1", + }, + }, + } + addKeyspace(ctx, t, ts, expected.Keyspace) + + resp, err := client.GetKeyspace(ctx, &vtctldatapb.GetKeyspaceRequest{Keyspace: expected.Keyspace.Name}) + assert.NoError(t, err) + assert.Equal(t, expected, resp) + + client.Close() + _, err = client.GetKeyspace(ctx, &vtctldatapb.GetKeyspaceRequest{}) + assert.Error(t, err) + }) +} + +func TestGetKeyspaces(t *testing.T) { + ctx := context.Background() + + ts := memorytopo.NewServer("cell1") + vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { + return grpcvtctldserver.NewVtctldServer(ts) + }) + + withTestServer(t, vtctld, func(t *testing.T, client vtctldclient.VtctldClient) { + resp, err := client.GetKeyspaces(ctx, &vtctldatapb.GetKeyspacesRequest{}) + assert.NoError(t, err) + assert.Empty(t, resp.Keyspaces) + + expected := &vtctldatapb.Keyspace{ + Name: "testkeyspace", + Keyspace: &topodatapb.Keyspace{}, + } + addKeyspace(ctx, t, ts, expected) + + resp, err = client.GetKeyspaces(ctx, &vtctldatapb.GetKeyspacesRequest{}) + assert.NoError(t, err) + assert.Equal(t, []*vtctldatapb.Keyspace{expected}, resp.Keyspaces) + + client.Close() + _, err = client.GetKeyspaces(ctx, &vtctldatapb.GetKeyspacesRequest{}) + assert.Error(t, err) + }) +} diff --git a/go/vt/vtctl/grpcvtctldclient/codegen/main.go b/go/vt/vtctl/grpcvtctldclient/codegen/main.go new file mode 100644 index 00000000000..81a3a7fd759 --- /dev/null +++ b/go/vt/vtctl/grpcvtctldclient/codegen/main.go @@ -0,0 +1,290 @@ +/* +Copyright 2021 The Vitess Authors. + +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 main + +import ( + "errors" + "flag" + "fmt" + "go/types" + "io" + "os" + "path/filepath" + "regexp" + "sort" + + "golang.org/x/tools/go/packages" +) + +func main() { // nolint:funlen + source := flag.String("source", "../../proto/vtctlservice", "source package") + typeName := flag.String("type", "VtctldClient", "interface type to implement") + implType := flag.String("impl", "gRPCVtctldClient", "type implementing the interface") + pkgName := flag.String("targetpkg", "grpcvtctldclient", "package name to generate code for") + out := flag.String("out", "", "output destination. leave empty to use stdout") + + flag.Parse() + + if *source == "" { + panic("-source cannot be empty") + } + + if *typeName == "" { + panic("-type cannot be empty") + } + + if *implType == "" { + panic("-impl cannot be empty") + } + + if *pkgName == "" { + panic("-targetpkg cannot be empty") + } + + var output io.Writer = os.Stdout + + if *out != "" { + f, err := os.Create(*out) + if err != nil { + panic(err) + } + + defer f.Close() + output = f + } + + pkg, err := loadPackage(*source) + if err != nil { + panic(err) + } + + iface, err := extractSourceInterface(pkg, *typeName) + if err != nil { + panic(fmt.Errorf("error getting %s in %s: %w", *typeName, *source, err)) + } + + imports := map[string]string{ + "context": "context", + } + importNames := []string{} + funcs := make(map[string]*Func, iface.NumExplicitMethods()) + funcNames := make([]string, iface.NumExplicitMethods()) + + for i := 0; i < iface.NumExplicitMethods(); i++ { + m := iface.ExplicitMethod(i) + funcNames[i] = m.Name() + + sig, ok := m.Type().(*types.Signature) + if !ok { + panic(fmt.Sprintf("could not derive signature from method %s, have %T", m.FullName(), m.Type())) + } + + if sig.Params().Len() != 3 { + panic(fmt.Sprintf("all methods in a grpc client interface should have exactly 3 params; found\n=> %s", sig)) + } + + if sig.Results().Len() != 2 { + panic(fmt.Sprintf("all methods in a grpc client interface should have exactly 2 results; found\n=> %s", sig)) + } + + f := &Func{ + Name: m.Name(), + } + funcs[f.Name] = f + + // The first parameter is always context.Context. The third parameter is + // always a ...grpc.CallOption. + param := sig.Params().At(1) + + localType, localImport, pkgPath, err := extractLocalPointerType(param) + if err != nil { + panic(err) + } + + f.Param.Name = param.Name() + f.Param.Type = "*" + localImport + "." + localType + + if _, ok := imports[localImport]; !ok { + importNames = append(importNames, localImport) + } + + imports[localImport] = pkgPath + + // (TODO|@amason): check which grpc lib CallOption is imported from in + // this interface; it could be either google.golang.org/grpc or + // github.com/golang/protobuf/grpc, although in vitess we currently + // always use the former. + + // The second result is always error. + result := sig.Results().At(0) + + localType, localImport, pkgPath, err = extractLocalPointerType(result) // (TODO|@amason): does not work for streaming rpcs + if err != nil { + panic(err) + } + + f.Result.Name = result.Name() + f.Result.Type = "*" + localImport + "." + localType + + if _, ok := imports[localImport]; !ok { + importNames = append(importNames, localImport) + } + + imports[localImport] = pkgPath + } + + sort.Strings(importNames) + sort.Strings(funcNames) + + def := &ClientInterfaceDef{ + PackageName: *pkgName, + Type: *implType, + } + + for _, name := range importNames { + imp := &Import{ + Path: imports[name], + } + + if filepath.Base(imp.Path) != name { + imp.Alias = name + } + + def.Imports = append(def.Imports, imp) + } + + for _, name := range funcNames { + def.Methods = append(def.Methods, funcs[name]) + } + + if err := tmpl.Execute(output, def); err != nil { + panic(err) + } +} + +// ClientInterfaceDef is a struct providing enough information to generate an +// implementation of a gRPC Client interface. +type ClientInterfaceDef struct { + PackageName string + Type string + Imports []*Import + Methods []*Func +} + +// Import contains the meta information about a Go import. +type Import struct { + Alias string + Path string +} + +// Func is the variable part of a gRPC client interface method (i.e. not the +// context or dialopts arguments, or the error part of the result tuple). +type Func struct { + Name string + Param Param + Result Param +} + +// Param represents an element of either a parameter list or result list. It +// contains an optional name, and a package-local type. This struct exists +// purely to power template execution, which is why the Type field is simply a +// bare string. +type Param struct { + Name string + // locally-qualified type, e.g. "grpc.CallOption", and not "google.golang.org/grpc.CallOption". + Type string +} + +func loadPackage(source string) (*packages.Package, error) { + pkgs, err := packages.Load(&packages.Config{ + Mode: packages.NeedTypes | packages.NeedSyntax | packages.NeedTypesInfo, + }, source) + if err != nil { + return nil, err + } + + if len(pkgs) != 1 { + return nil, errors.New("must specify exactly one package") + } + + pkg := pkgs[0] + if len(pkg.Errors) > 0 { + var err error + + for _, e := range pkg.Errors { + switch err { + case nil: + err = fmt.Errorf("errors loading package %s: %s", source, e.Error()) + default: + err = fmt.Errorf("%w; %s", err, e.Error()) + } + } + + return nil, err + } + + return pkg, nil +} + +func extractSourceInterface(pkg *packages.Package, name string) (*types.Interface, error) { + obj := pkg.Types.Scope().Lookup(name) + if obj == nil { + return nil, fmt.Errorf("no symbol found with name %s", name) + } + + switch t := obj.Type().(type) { + case *types.Named: + iface, ok := t.Underlying().(*types.Interface) + if !ok { + return nil, fmt.Errorf("symbol %s was not an interface but %T", name, t.Underlying()) + } + + return iface, nil + case *types.Interface: + return t, nil + } + + return nil, fmt.Errorf("symbol %s was not an interface but %T", name, obj.Type()) +} + +var vitessProtoRegexp = regexp.MustCompile(`^vitess.io.*/proto/.*`) + +func rewriteProtoImports(pkg *types.Package) string { + if vitessProtoRegexp.MatchString(pkg.Path()) { + return pkg.Name() + "pb" + } + + return pkg.Name() +} + +func extractLocalPointerType(v *types.Var) (name string, localImport string, pkgPath string, err error) { + ptr, ok := v.Type().(*types.Pointer) + if !ok { + return "", "", "", fmt.Errorf("expected a pointer type for %s, got %V", v.Name(), v.Type()) + } + + typ, ok := ptr.Elem().(*types.Named) + if !ok { + return "", "", "", fmt.Errorf("expected an underlying named type for %s, got %V", v.Name(), ptr.Elem()) + } + + name = typ.Obj().Name() + localImport = rewriteProtoImports(typ.Obj().Pkg()) + pkgPath = typ.Obj().Pkg().Path() + + return name, localImport, pkgPath, nil +} diff --git a/go/vt/vtctl/grpcvtctldclient/codegen/template.go b/go/vt/vtctl/grpcvtctldclient/codegen/template.go new file mode 100644 index 00000000000..d0c668bf577 --- /dev/null +++ b/go/vt/vtctl/grpcvtctldclient/codegen/template.go @@ -0,0 +1,63 @@ +/* +Copyright 2021 The Vitess Authors. + +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 main + +import "text/template" + +const tmplStr = `// Code generated by grpcvtctldclient-generator. DO NOT EDIT. + +/* +Copyright 2021 The Vitess Authors. + +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 {{ .PackageName }} + +import ( + "context" + + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + {{ range .Imports -}} + {{ if ne .Alias "" }}{{ .Alias }} {{ end }}"{{ .Path }}" + {{- end }} +) +{{ range .Methods }} +// {{ .Name }} is part of the vtctlservicepb.VtctldClient interface. +func (client *{{ $.Type }}) {{ .Name }}(ctx context.Context, {{ .Param.Name }} {{ .Param.Type }}, opts ...grpc.CallOption) ({{ .Result.Type }}, error) { + if client.c == nil { + return nil, status.Error(codes.Unavailable, connClosedMsg) + } + + return client.c.{{ .Name }}(ctx, in, opts...) +} +{{ end }}` + +var tmpl = template.Must(template.New("vtctldclient-generator").Parse(tmplStr)) diff --git a/go/vt/vtctl/grpcvtctldserver/server.go b/go/vt/vtctl/grpcvtctldserver/server.go new file mode 100644 index 00000000000..8bfa475fade --- /dev/null +++ b/go/vt/vtctl/grpcvtctldserver/server.go @@ -0,0 +1,662 @@ +/* +Copyright 2020 The Vitess Authors. + +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 grpcvtctldserver + +import ( + "context" + "errors" + "fmt" + "path/filepath" + "time" + + "google.golang.org/grpc" + + "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/vt/mysqlctl/backupstorage" + "vitess.io/vitess/go/vt/mysqlctl/mysqlctlproto" + "vitess.io/vitess/go/vt/topo" + "vitess.io/vitess/go/vt/topotools" + "vitess.io/vitess/go/vt/vtctl/workflow" + "vitess.io/vitess/go/vt/vterrors" + "vitess.io/vitess/go/vt/vttablet/tmclient" + + mysqlctlpb "vitess.io/vitess/go/vt/proto/mysqlctl" + tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" + topodatapb "vitess.io/vitess/go/vt/proto/topodata" + vschemapb "vitess.io/vitess/go/vt/proto/vschema" + vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" + vtctlservicepb "vitess.io/vitess/go/vt/proto/vtctlservice" + "vitess.io/vitess/go/vt/proto/vtrpc" +) + +// VtctldServer implements the Vtctld RPC service protocol. +type VtctldServer struct { + ts *topo.Server + tmc tmclient.TabletManagerClient + ws *workflow.Server +} + +// NewVtctldServer returns a new VtctldServer for the given topo server. +func NewVtctldServer(ts *topo.Server) *VtctldServer { + tmc := tmclient.NewTabletManagerClient() + + return &VtctldServer{ + ts: ts, + tmc: tmc, + ws: workflow.NewServer(ts, tmc), + } +} + +// ChangeTabletType is part of the vtctlservicepb.VtctldServer interface. +func (s *VtctldServer) ChangeTabletType(ctx context.Context, req *vtctldatapb.ChangeTabletTypeRequest) (*vtctldatapb.ChangeTabletTypeResponse, error) { + tablet, err := s.ts.GetTablet(ctx, req.TabletAlias) + if err != nil { + return nil, err + } + + if !topo.IsTrivialTypeChange(tablet.Type, req.DbType) { + return nil, fmt.Errorf("tablet %v type change %v -> %v is not an allowed transition for ChangeTabletType", req.TabletAlias, tablet.Type, req.DbType) + } + + if req.DryRun { + afterTablet := *tablet.Tablet + afterTablet.Type = req.DbType + + return &vtctldatapb.ChangeTabletTypeResponse{ + BeforeTablet: tablet.Tablet, + AfterTablet: &afterTablet, + WasDryRun: true, + }, nil + } + + err = s.tmc.ChangeType(ctx, tablet.Tablet, req.DbType) + if err != nil { + return nil, err + } + + var changedTablet *topodatapb.Tablet + + changedTabletInfo, err := s.ts.GetTablet(ctx, req.TabletAlias) + if err != nil { + log.Warningf("error while reading the tablet we just changed back out of the topo: %v", err) + } else { + changedTablet = changedTabletInfo.Tablet + } + + return &vtctldatapb.ChangeTabletTypeResponse{ + BeforeTablet: tablet.Tablet, + AfterTablet: changedTablet, + WasDryRun: false, + }, nil +} + +// CreateKeyspace is part of the vtctlservicepb.VtctldServer interface. +func (s *VtctldServer) CreateKeyspace(ctx context.Context, req *vtctldatapb.CreateKeyspaceRequest) (*vtctldatapb.CreateKeyspaceResponse, error) { + switch req.Type { + case topodatapb.KeyspaceType_NORMAL: + case topodatapb.KeyspaceType_SNAPSHOT: + if req.BaseKeyspace == "" { + return nil, errors.New("BaseKeyspace is required for SNAPSHOT keyspaces") + } + + if req.SnapshotTime == nil { + return nil, errors.New("SnapshotTime is required for SNAPSHOT keyspaces") + } + default: + return nil, fmt.Errorf("unknown keyspace type %v", req.Type) + } + + ki := &topodatapb.Keyspace{ + KeyspaceType: req.Type, + ShardingColumnName: req.ShardingColumnName, + ShardingColumnType: req.ShardingColumnType, + + ServedFroms: req.ServedFroms, + + BaseKeyspace: req.BaseKeyspace, + SnapshotTime: req.SnapshotTime, + } + + err := s.ts.CreateKeyspace(ctx, req.Name, ki) + if req.Force && topo.IsErrType(err, topo.NodeExists) { + log.Infof("keyspace %v already exists (ignoring error with Force=true)", req.Name) + err = nil + + // Get the actual keyspace out of the topo; it may differ in structure, + // and we want to return the authoritative version as the "created" one + // to the client. + var ks *topo.KeyspaceInfo + ks, _ = s.ts.GetKeyspace(ctx, req.Name) + ki = ks.Keyspace + } + + if err != nil { + return nil, err + } + + if !req.AllowEmptyVSchema { + if err := s.ts.EnsureVSchema(ctx, req.Name); err != nil { + return nil, err + } + } + + if req.Type == topodatapb.KeyspaceType_SNAPSHOT { + vs, err := s.ts.GetVSchema(ctx, req.BaseKeyspace) + if err != nil { + log.Infof("error from GetVSchema(%v) = %v", req.BaseKeyspace, err) + if topo.IsErrType(err, topo.NoNode) { + log.Infof("base keyspace %v does not exist; continuing with bare, unsharded vschema", req.BaseKeyspace) + vs = &vschemapb.Keyspace{ + Sharded: false, + Tables: map[string]*vschemapb.Table{}, + Vindexes: map[string]*vschemapb.Vindex{}, + } + } else { + return nil, err + } + } + + // SNAPSHOT keyspaces are excluded from global routing. + vs.RequireExplicitRouting = true + + if err := s.ts.SaveVSchema(ctx, req.Name, vs); err != nil { + return nil, fmt.Errorf("SaveVSchema(%v) = %w", vs, err) + } + } + + cells := []string{} + err = s.ts.RebuildSrvVSchema(ctx, cells) + if err != nil { + return nil, fmt.Errorf("RebuildSrvVSchema(%v) = %w", cells, err) + } + + return &vtctldatapb.CreateKeyspaceResponse{ + Keyspace: &vtctldatapb.Keyspace{ + Name: req.Name, + Keyspace: ki, + }, + }, nil +} + +// CreateShard is part of the vtctlservicepb.VtctldServer interface. +func (s *VtctldServer) CreateShard(ctx context.Context, req *vtctldatapb.CreateShardRequest) (*vtctldatapb.CreateShardResponse, error) { + if req.IncludeParent { + log.Infof("Creating empty keyspace for %s", req.Keyspace) + if err := s.ts.CreateKeyspace(ctx, req.Keyspace, &topodatapb.Keyspace{}); err != nil { + if req.Force && topo.IsErrType(err, topo.NodeExists) { + log.Infof("keyspace %v already exists; ignoring error because Force = true", req.Keyspace) + } else { + return nil, err + } + } + } + + shardExists := false + + if err := s.ts.CreateShard(ctx, req.Keyspace, req.ShardName); err != nil { + if req.Force && topo.IsErrType(err, topo.NodeExists) { + log.Infof("shard %v/%v already exists; ignoring error because Force = true", req.Keyspace, req.ShardName) + shardExists = true + } else { + return nil, err + } + } + + // Fetch what we just created out of the topo. Errors should never happen + // here, but we'll check them anyway. + + ks, err := s.ts.GetKeyspace(ctx, req.Keyspace) + if err != nil { + return nil, err + } + + shard, err := s.ts.GetShard(ctx, req.Keyspace, req.ShardName) + if err != nil { + return nil, err + } + + return &vtctldatapb.CreateShardResponse{ + Keyspace: &vtctldatapb.Keyspace{ + Name: req.Keyspace, + Keyspace: ks.Keyspace, + }, + Shard: &vtctldatapb.Shard{ + Keyspace: req.Keyspace, + Name: req.ShardName, + Shard: shard.Shard, + }, + ShardAlreadyExists: shardExists, + }, nil +} + +// DeleteKeyspace is part of the vtctlservicepb.VtctldServer interface. +func (s *VtctldServer) DeleteKeyspace(ctx context.Context, req *vtctldatapb.DeleteKeyspaceRequest) (*vtctldatapb.DeleteKeyspaceResponse, error) { + shards, err := s.ts.GetShardNames(ctx, req.Keyspace) + if err != nil { + return nil, err + } + + if len(shards) > 0 { + if !req.Recursive { + return nil, vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "keyspace %v still has %d shards; use Recursive=true or remove them manually", req.Keyspace, len(shards)) + } + + log.Infof("Deleting all %d shards (and their tablets) in keyspace %v", len(shards), req.Keyspace) + recursive := true + evenIfServing := true + + for _, shard := range shards { + log.Infof("Recursively deleting shard %v/%v", req.Keyspace, shard) + if err := deleteShard(ctx, s.ts, req.Keyspace, shard, recursive, evenIfServing); err != nil { + return nil, fmt.Errorf("cannot delete shard %v/%v: %w", req.Keyspace, shard, err) + } + } + } + + cells, err := s.ts.GetKnownCells(ctx) + if err != nil { + return nil, err + } + + for _, cell := range cells { + if err := s.ts.DeleteKeyspaceReplication(ctx, cell, req.Keyspace); err != nil && !topo.IsErrType(err, topo.NoNode) { + log.Warningf("Cannot delete KeyspaceReplication in cell %v for %v: %v", cell, req.Keyspace, err) + } + + if err := s.ts.DeleteSrvKeyspace(ctx, cell, req.Keyspace); err != nil && !topo.IsErrType(err, topo.NoNode) { + log.Warningf("Cannot delete SrvKeyspace in cell %v for %v: %v", cell, req.Keyspace, err) + } + } + + if err := s.ts.DeleteKeyspace(ctx, req.Keyspace); err != nil { + return nil, err + } + + return &vtctldatapb.DeleteKeyspaceResponse{}, nil +} + +// DeleteShards is part of the vtctlservicepb.VtctldServer interface. +func (s *VtctldServer) DeleteShards(ctx context.Context, req *vtctldatapb.DeleteShardsRequest) (*vtctldatapb.DeleteShardsResponse, error) { + for _, shard := range req.Shards { + if err := deleteShard(ctx, s.ts, shard.Keyspace, shard.Name, req.Recursive, req.EvenIfServing); err != nil { + return nil, err + } + } + + return &vtctldatapb.DeleteShardsResponse{}, nil +} + +// DeleteTablets is part of the vtctlservicepb.VtctldServer interface. +func (s *VtctldServer) DeleteTablets(ctx context.Context, req *vtctldatapb.DeleteTabletsRequest) (*vtctldatapb.DeleteTabletsResponse, error) { + for _, alias := range req.TabletAliases { + if err := deleteTablet(ctx, s.ts, alias, req.AllowPrimary); err != nil { + return nil, err + } + } + + return &vtctldatapb.DeleteTabletsResponse{}, nil +} + +// FindAllShardsInKeyspace is part of the vtctlservicepb.VtctldServer interface. +func (s *VtctldServer) FindAllShardsInKeyspace(ctx context.Context, req *vtctldatapb.FindAllShardsInKeyspaceRequest) (*vtctldatapb.FindAllShardsInKeyspaceResponse, error) { + result, err := s.ts.FindAllShardsInKeyspace(ctx, req.Keyspace) + if err != nil { + return nil, err + } + + shards := map[string]*vtctldatapb.Shard{} + for _, shard := range result { + shards[shard.ShardName()] = &vtctldatapb.Shard{ + Keyspace: req.Keyspace, + Name: shard.ShardName(), + Shard: shard.Shard, + } + } + + return &vtctldatapb.FindAllShardsInKeyspaceResponse{ + Shards: shards, + }, nil +} + +// GetBackups is part of the vtctldservicepb.VtctldServer interface. +func (s *VtctldServer) GetBackups(ctx context.Context, req *vtctldatapb.GetBackupsRequest) (*vtctldatapb.GetBackupsResponse, error) { + bs, err := backupstorage.GetBackupStorage() + if err != nil { + return nil, err + } + + defer bs.Close() + + bucket := filepath.Join(req.Keyspace, req.Shard) + bhs, err := bs.ListBackups(ctx, bucket) + if err != nil { + return nil, err + } + + resp := &vtctldatapb.GetBackupsResponse{ + Backups: make([]*mysqlctlpb.BackupInfo, len(bhs)), + } + + for i, bh := range bhs { + resp.Backups[i] = mysqlctlproto.BackupHandleToProto(bh) + } + + return resp, nil +} + +// GetCellInfoNames is part of the vtctlservicepb.VtctldServer interface. +func (s *VtctldServer) GetCellInfoNames(ctx context.Context, req *vtctldatapb.GetCellInfoNamesRequest) (*vtctldatapb.GetCellInfoNamesResponse, error) { + names, err := s.ts.GetCellInfoNames(ctx) + if err != nil { + return nil, err + } + + return &vtctldatapb.GetCellInfoNamesResponse{Names: names}, nil +} + +// GetCellInfo is part of the vtctlservicepb.VtctldServer interface. +func (s *VtctldServer) GetCellInfo(ctx context.Context, req *vtctldatapb.GetCellInfoRequest) (*vtctldatapb.GetCellInfoResponse, error) { + if req.Cell == "" { + return nil, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "cell field is required") + } + + // We use a strong read, because users using this command want the latest + // data, and this is user-generated, not used in any automated process. + strongRead := true + ci, err := s.ts.GetCellInfo(ctx, req.Cell, strongRead) + if err != nil { + return nil, err + } + + return &vtctldatapb.GetCellInfoResponse{CellInfo: ci}, nil +} + +// GetCellsAliases is part of the vtctlservicepb.VtctldServer interface. +func (s *VtctldServer) GetCellsAliases(ctx context.Context, req *vtctldatapb.GetCellsAliasesRequest) (*vtctldatapb.GetCellsAliasesResponse, error) { + strongRead := true + aliases, err := s.ts.GetCellsAliases(ctx, strongRead) + if err != nil { + return nil, err + } + + return &vtctldatapb.GetCellsAliasesResponse{Aliases: aliases}, nil +} + +// GetKeyspace is part of the vtctlservicepb.VtctldServer interface. +func (s *VtctldServer) GetKeyspace(ctx context.Context, req *vtctldatapb.GetKeyspaceRequest) (*vtctldatapb.GetKeyspaceResponse, error) { + keyspace, err := s.ts.GetKeyspace(ctx, req.Keyspace) + if err != nil { + return nil, err + } + + return &vtctldatapb.GetKeyspaceResponse{ + Keyspace: &vtctldatapb.Keyspace{ + Name: req.Keyspace, + Keyspace: keyspace.Keyspace, + }, + }, nil +} + +// GetKeyspaces is part of the vtctlservicepb.VtctldServer interface. +func (s *VtctldServer) GetKeyspaces(ctx context.Context, req *vtctldatapb.GetKeyspacesRequest) (*vtctldatapb.GetKeyspacesResponse, error) { + names, err := s.ts.GetKeyspaces(ctx) + if err != nil { + return nil, err + } + + keyspaces := make([]*vtctldatapb.Keyspace, len(names)) + + for i, name := range names { + ks, err := s.GetKeyspace(ctx, &vtctldatapb.GetKeyspaceRequest{Keyspace: name}) + if err != nil { + return nil, err + } + + keyspaces[i] = ks.Keyspace + } + + return &vtctldatapb.GetKeyspacesResponse{Keyspaces: keyspaces}, nil +} + +// GetSchema is part of the vtctlservicepb.VtctldServer interface. +func (s *VtctldServer) GetSchema(ctx context.Context, req *vtctldatapb.GetSchemaRequest) (*vtctldatapb.GetSchemaResponse, error) { + tablet, err := s.ts.GetTablet(ctx, req.TabletAlias) + if err != nil { + return nil, fmt.Errorf("GetTablet(%v) failed: %w", req.TabletAlias, err) + } + + sd, err := s.tmc.GetSchema(ctx, tablet.Tablet, req.Tables, req.ExcludeTables, req.IncludeViews) + if err != nil { + return nil, fmt.Errorf("GetSchema(%v, %v, %v, %v) failed: %w", tablet.Tablet, req.Tables, req.ExcludeTables, req.IncludeViews, err) + } + + if req.TableNamesOnly { + nameTds := make([]*tabletmanagerdatapb.TableDefinition, len(sd.TableDefinitions)) + + for i, td := range sd.TableDefinitions { + nameTds[i] = &tabletmanagerdatapb.TableDefinition{ + Name: td.Name, + } + } + + sd.TableDefinitions = nameTds + } else if req.TableSizesOnly { + sizeTds := make([]*tabletmanagerdatapb.TableDefinition, len(sd.TableDefinitions)) + + for i, td := range sd.TableDefinitions { + sizeTds[i] = &tabletmanagerdatapb.TableDefinition{ + Name: td.Name, + Type: td.Type, + RowCount: td.RowCount, + DataLength: td.DataLength, + } + } + + sd.TableDefinitions = sizeTds + } + + return &vtctldatapb.GetSchemaResponse{ + Schema: sd, + }, nil +} + +// GetShard is part of the vtctlservicepb.VtctldServer interface. +func (s *VtctldServer) GetShard(ctx context.Context, req *vtctldatapb.GetShardRequest) (*vtctldatapb.GetShardResponse, error) { + shard, err := s.ts.GetShard(ctx, req.Keyspace, req.ShardName) + if err != nil { + return nil, err + } + + return &vtctldatapb.GetShardResponse{ + Shard: &vtctldatapb.Shard{ + Keyspace: req.Keyspace, + Name: req.ShardName, + Shard: shard.Shard, + }, + }, nil +} + +// GetSrvVSchema is part of the vtctlservicepb.VtctldServer interface. +func (s *VtctldServer) GetSrvVSchema(ctx context.Context, req *vtctldatapb.GetSrvVSchemaRequest) (*vtctldatapb.GetSrvVSchemaResponse, error) { + vschema, err := s.ts.GetSrvVSchema(ctx, req.Cell) + if err != nil { + return nil, err + } + + return &vtctldatapb.GetSrvVSchemaResponse{ + SrvVSchema: vschema, + }, nil +} + +// GetTablet is part of the vtctlservicepb.VtctldServer interface. +func (s *VtctldServer) GetTablet(ctx context.Context, req *vtctldatapb.GetTabletRequest) (*vtctldatapb.GetTabletResponse, error) { + ti, err := s.ts.GetTablet(ctx, req.TabletAlias) + if err != nil { + return nil, err + } + + return &vtctldatapb.GetTabletResponse{ + Tablet: ti.Tablet, + }, nil +} + +// GetTablets is part of the vtctlservicepb.VtctldServer interface. +func (s *VtctldServer) GetTablets(ctx context.Context, req *vtctldatapb.GetTabletsRequest) (*vtctldatapb.GetTabletsResponse, error) { + // It is possible that an old primary has not yet updated its type in the + // topo. In that case, report its type as UNKNOWN. It used to be MASTER but + // is no longer the serving primary. + adjustTypeForStalePrimary := func(ti *topo.TabletInfo, mtst time.Time) { + if ti.Type == topodatapb.TabletType_MASTER && ti.GetMasterTermStartTime().Before(mtst) { + ti.Tablet.Type = topodatapb.TabletType_UNKNOWN + } + } + + if req.Keyspace != "" && req.Shard != "" { + tabletMap, err := s.ts.GetTabletMapForShard(ctx, req.Keyspace, req.Shard) + if err != nil { + return nil, err + } + + var trueMasterTimestamp time.Time + for _, ti := range tabletMap { + if ti.Type == topodatapb.TabletType_MASTER { + masterTimestamp := ti.GetMasterTermStartTime() + if masterTimestamp.After(trueMasterTimestamp) { + trueMasterTimestamp = masterTimestamp + } + } + } + + tablets := make([]*topodatapb.Tablet, 0, len(tabletMap)) + for _, ti := range tabletMap { + adjustTypeForStalePrimary(ti, trueMasterTimestamp) + tablets = append(tablets, ti.Tablet) + } + + return &vtctldatapb.GetTabletsResponse{Tablets: tablets}, nil + } + + cells := req.Cells + if len(cells) == 0 { + c, err := s.ts.GetKnownCells(ctx) + if err != nil { + return nil, err + } + + cells = c + } + + var allTablets []*topodatapb.Tablet + + for _, cell := range cells { + tablets, err := topotools.GetAllTablets(ctx, s.ts, cell) + if err != nil { + return nil, err + } + + // Collect true master term start times, and optionally filter out any + // tablets by keyspace according to the request. + masterTermStartTimes := map[string]time.Time{} + filteredTablets := make([]*topo.TabletInfo, 0, len(tablets)) + + for _, tablet := range tablets { + if req.Keyspace != "" && tablet.Keyspace != req.Keyspace { + continue + } + + key := tablet.Keyspace + "." + tablet.Shard + if v, ok := masterTermStartTimes[key]; ok { + if tablet.GetMasterTermStartTime().After(v) { + masterTermStartTimes[key] = tablet.GetMasterTermStartTime() + } + } else { + masterTermStartTimes[key] = tablet.GetMasterTermStartTime() + } + + filteredTablets = append(filteredTablets, tablet) + } + + // collect the tablets with adjusted master term start times. they've + // already been filtered by the above loop, so no keyspace filtering + // here. + for _, ti := range filteredTablets { + key := ti.Keyspace + "." + ti.Shard + adjustTypeForStalePrimary(ti, masterTermStartTimes[key]) + + allTablets = append(allTablets, ti.Tablet) + } + } + + return &vtctldatapb.GetTabletsResponse{ + Tablets: allTablets, + }, nil +} + +// GetVSchema is part of the vtctlservicepb.VtctldServer interface. +func (s *VtctldServer) GetVSchema(ctx context.Context, req *vtctldatapb.GetVSchemaRequest) (*vtctldatapb.GetVSchemaResponse, error) { + vschema, err := s.ts.GetVSchema(ctx, req.Keyspace) + if err != nil { + return nil, err + } + + return &vtctldatapb.GetVSchemaResponse{ + VSchema: vschema, + }, nil +} + +// GetWorkflows is part of the vtctlservicepb.VtctldServer interface. +func (s *VtctldServer) GetWorkflows(ctx context.Context, req *vtctldatapb.GetWorkflowsRequest) (*vtctldatapb.GetWorkflowsResponse, error) { + return s.ws.GetWorkflows(ctx, req) +} + +// RemoveKeyspaceCell is part of the vtctlservicepb.VtctldServer interface. +func (s *VtctldServer) RemoveKeyspaceCell(ctx context.Context, req *vtctldatapb.RemoveKeyspaceCellRequest) (*vtctldatapb.RemoveKeyspaceCellResponse, error) { + shards, err := s.ts.GetShardNames(ctx, req.Keyspace) + if err != nil { + return nil, err + } + + // Remove all the shards, serially. Stop immediately if any fail. + for _, shard := range shards { + log.Infof("Removing cell %v from shard %v/%v", req.Cell, req.Keyspace, shard) + if err := removeShardCell(ctx, s.ts, req.Cell, req.Keyspace, shard, req.Recursive, req.Force); err != nil { + return nil, fmt.Errorf("cannot remove cell %v from shard %v/%v: %w", req.Cell, req.Keyspace, shard, err) + } + } + + // Last, remove the SrvKeyspace object. + log.Infof("Removing cell %v keyspace %v SrvKeyspace object", req.Cell, req.Keyspace) + if err := s.ts.DeleteSrvKeyspace(ctx, req.Cell, req.Keyspace); err != nil { + return nil, fmt.Errorf("cannot delete SrvKeyspace from cell %v for keyspace %v: %w", req.Cell, req.Keyspace, err) + } + + return &vtctldatapb.RemoveKeyspaceCellResponse{}, nil +} + +// RemoveShardCell is part of the vtctlservicepb.VtctldServer interface. +func (s *VtctldServer) RemoveShardCell(ctx context.Context, req *vtctldatapb.RemoveShardCellRequest) (*vtctldatapb.RemoveShardCellResponse, error) { + if err := removeShardCell(ctx, s.ts, req.Cell, req.Keyspace, req.ShardName, req.Recursive, req.Force); err != nil { + return nil, err + } + + return &vtctldatapb.RemoveShardCellResponse{}, nil +} + +// StartServer registers a VtctldServer for RPCs on the given gRPC server. +func StartServer(s *grpc.Server, ts *topo.Server) { + vtctlservicepb.RegisterVtctldServer(s, NewVtctldServer(ts)) +} diff --git a/go/vt/vtctl/grpcvtctldserver/server_test.go b/go/vt/vtctl/grpcvtctldserver/server_test.go new file mode 100644 index 00000000000..261561563fe --- /dev/null +++ b/go/vt/vtctl/grpcvtctldserver/server_test.go @@ -0,0 +1,3370 @@ +/* +Copyright 2020 The Vitess Authors. + +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 grpcvtctldserver + +import ( + "context" + "errors" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/vt/logutil" + "vitess.io/vitess/go/vt/mysqlctl/backupstorage" + "vitess.io/vitess/go/vt/topo" + "vitess.io/vitess/go/vt/topo/memorytopo" + "vitess.io/vitess/go/vt/topo/topoproto" + "vitess.io/vitess/go/vt/vtctl/grpcvtctldserver/testutil" + "vitess.io/vitess/go/vt/vttablet/tmclient" + + mysqlctlpb "vitess.io/vitess/go/vt/proto/mysqlctl" + querypb "vitess.io/vitess/go/vt/proto/query" + tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" + topodatapb "vitess.io/vitess/go/vt/proto/topodata" + vschemapb "vitess.io/vitess/go/vt/proto/vschema" + vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" + vtctlservicepb "vitess.io/vitess/go/vt/proto/vtctlservice" + "vitess.io/vitess/go/vt/proto/vttime" +) + +func init() { + *backupstorage.BackupStorageImplementation = testutil.BackupStorageImplementation + + // For tests that don't actually care about mocking the tmclient (i.e. they + // call NewVtctldServer to initialize the unit under test), this needs to be + // set. + // + // Tests that do care about the tmclient should use + // testutil.NewVtctldServerWithTabletManagerClient to initialize their + // VtctldServer. + *tmclient.TabletManagerProtocol = "grpcvtctldserver.test" + tmclient.RegisterTabletManagerClientFactory("grpcvtctldserver.test", func() tmclient.TabletManagerClient { + return nil + }) +} + +func TestChangeTabletType(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + cells []string + tablets []*topodatapb.Tablet + req *vtctldatapb.ChangeTabletTypeRequest + expected *vtctldatapb.ChangeTabletTypeResponse + shouldErr bool + }{ + { + name: "success", + cells: []string{"zone1"}, + tablets: []*topodatapb.Tablet{ + { + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + Type: topodatapb.TabletType_REPLICA, + }, + }, + req: &vtctldatapb.ChangeTabletTypeRequest{ + TabletAlias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + DbType: topodatapb.TabletType_RDONLY, + }, + expected: &vtctldatapb.ChangeTabletTypeResponse{ + BeforeTablet: &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + Type: topodatapb.TabletType_REPLICA, + }, + AfterTablet: &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + Type: topodatapb.TabletType_RDONLY, + }, + WasDryRun: false, + }, + shouldErr: false, + }, + { + name: "dry run", + cells: []string{"zone1"}, + tablets: []*topodatapb.Tablet{ + { + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + Type: topodatapb.TabletType_REPLICA, + }, + }, + req: &vtctldatapb.ChangeTabletTypeRequest{ + TabletAlias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + DbType: topodatapb.TabletType_RDONLY, + DryRun: true, + }, + expected: &vtctldatapb.ChangeTabletTypeResponse{ + BeforeTablet: &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + Type: topodatapb.TabletType_REPLICA, + }, + AfterTablet: &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + Type: topodatapb.TabletType_RDONLY, + }, + WasDryRun: true, + }, + shouldErr: false, + }, + { + name: "tablet not found", + cells: []string{"zone1"}, + tablets: []*topodatapb.Tablet{ + { + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 200, + }, + Type: topodatapb.TabletType_REPLICA, + }, + }, + req: &vtctldatapb.ChangeTabletTypeRequest{ + TabletAlias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + DbType: topodatapb.TabletType_RDONLY, + }, + expected: nil, + shouldErr: true, + }, + { + name: "master promotions not allowed", + cells: []string{"zone1"}, + tablets: []*topodatapb.Tablet{ + { + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + Type: topodatapb.TabletType_REPLICA, + }, + }, + req: &vtctldatapb.ChangeTabletTypeRequest{ + TabletAlias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + DbType: topodatapb.TabletType_MASTER, + }, + expected: nil, + shouldErr: true, + }, + { + name: "master demotions not allowed", + cells: []string{"zone1"}, + tablets: []*topodatapb.Tablet{ + { + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + Type: topodatapb.TabletType_MASTER, + }, + }, + req: &vtctldatapb.ChangeTabletTypeRequest{ + TabletAlias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + DbType: topodatapb.TabletType_REPLICA, + }, + expected: nil, + shouldErr: true, + }, + } + + for _, tt := range tests { + tt := tt + + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + ctx := context.Background() + ts := memorytopo.NewServer(tt.cells...) + vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &testutil.TabletManagerClient{ + TopoServer: ts, + }, func(ts *topo.Server) vtctlservicepb.VtctldServer { return NewVtctldServer(ts) }) + + for _, tablet := range tt.tablets { + testutil.AddTablet(ctx, t, ts, tablet) + } + + resp, err := vtctld.ChangeTabletType(ctx, tt.req) + if tt.shouldErr { + assert.Error(t, err) + return + } + + assert.NoError(t, err) + assert.Equal(t, tt.expected, resp) + + // If we are testing a dry-run, then the tablet in the actual + // topo should match the BeforeTablet in the response. Otherwise, + // the tablet in the actual topo should match the AfterTablet in + // the response. + expectedRealType := resp.AfterTablet.Type + msg := "ChangeTabletType did not cause topo update" + if tt.req.DryRun { + expectedRealType = resp.BeforeTablet.Type + msg = "dryrun type change resulted in real type change" + } + + tablet, err := ts.GetTablet(ctx, tt.req.TabletAlias) + assert.NoError(t, err, + "could not load tablet %s from topo after type change %v -> %v [dryrun=%t]", + topoproto.TabletAliasString(tt.req.TabletAlias), + resp.BeforeTablet.Type, + resp.AfterTablet.Type, + resp.WasDryRun, + ) + assert.Equal(t, expectedRealType, tablet.Type, msg) + }) + } + + t.Run("tabletmanager failure", func(t *testing.T) { + t.Parallel() + + ctx := context.Background() + ts := memorytopo.NewServer("zone1") + vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &testutil.TabletManagerClient{ + TopoServer: nil, + }, func(ts *topo.Server) vtctlservicepb.VtctldServer { return NewVtctldServer(ts) }) + + testutil.AddTablet(ctx, t, ts, &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + Type: topodatapb.TabletType_REPLICA, + }) + + _, err := vtctld.ChangeTabletType(ctx, &vtctldatapb.ChangeTabletTypeRequest{ + TabletAlias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + DbType: topodatapb.TabletType_RDONLY, + }) + assert.Error(t, err) + }) +} + +func TestCreateKeyspace(t *testing.T) { + cells := []string{"zone1", "zone2", "zone3"} + tests := []struct { + name string + topo map[string]*topodatapb.Keyspace + vschemas map[string]*vschemapb.Keyspace + req *vtctldatapb.CreateKeyspaceRequest + expected *vtctldatapb.CreateKeyspaceResponse + shouldErr bool + vschemaShouldExist bool + expectedVSchema *vschemapb.Keyspace + }{ + { + name: "normal keyspace", + topo: nil, + req: &vtctldatapb.CreateKeyspaceRequest{ + Name: "testkeyspace", + Type: topodatapb.KeyspaceType_NORMAL, + }, + expected: &vtctldatapb.CreateKeyspaceResponse{ + Keyspace: &vtctldatapb.Keyspace{ + Name: "testkeyspace", + Keyspace: &topodatapb.Keyspace{ + KeyspaceType: topodatapb.KeyspaceType_NORMAL, + }, + }, + }, + vschemaShouldExist: true, + expectedVSchema: &vschemapb.Keyspace{ + Sharded: false, + }, + shouldErr: false, + }, + { + name: "snapshot keyspace", + topo: map[string]*topodatapb.Keyspace{ + "testkeyspace": { + KeyspaceType: topodatapb.KeyspaceType_NORMAL, + }, + }, + vschemas: map[string]*vschemapb.Keyspace{ + "testkeyspace": { + Sharded: true, + Vindexes: map[string]*vschemapb.Vindex{ + "h1": { + Type: "hash", + }, + }, + }, + }, + req: &vtctldatapb.CreateKeyspaceRequest{ + Name: "testsnapshot", + Type: topodatapb.KeyspaceType_SNAPSHOT, + BaseKeyspace: "testkeyspace", + SnapshotTime: &vttime.Time{ + Seconds: 1, + }, + }, + expected: &vtctldatapb.CreateKeyspaceResponse{ + Keyspace: &vtctldatapb.Keyspace{ + Name: "testsnapshot", + Keyspace: &topodatapb.Keyspace{ + KeyspaceType: topodatapb.KeyspaceType_SNAPSHOT, + BaseKeyspace: "testkeyspace", + SnapshotTime: &vttime.Time{ + Seconds: 1, + }, + }, + }, + }, + vschemaShouldExist: true, + expectedVSchema: &vschemapb.Keyspace{ + Sharded: true, + Vindexes: map[string]*vschemapb.Vindex{ + "h1": { + Type: "hash", + }, + }, + RequireExplicitRouting: true, + }, + shouldErr: false, + }, + { + name: "snapshot keyspace with no base keyspace specified", + topo: nil, + req: &vtctldatapb.CreateKeyspaceRequest{ + Name: "testsnapshot", + Type: topodatapb.KeyspaceType_SNAPSHOT, + SnapshotTime: &vttime.Time{}, + }, + expected: nil, + shouldErr: true, + }, + { + name: "snapshot keyspace with no snapshot time", + topo: nil, + req: &vtctldatapb.CreateKeyspaceRequest{ + Name: "testsnapshot", + Type: topodatapb.KeyspaceType_SNAPSHOT, + BaseKeyspace: "testkeyspace", + }, + expected: nil, + shouldErr: true, + }, + { + name: "snapshot keyspace with nonexistent base keyspace", + topo: nil, + req: &vtctldatapb.CreateKeyspaceRequest{ + Name: "testsnapshot", + Type: topodatapb.KeyspaceType_SNAPSHOT, + BaseKeyspace: "testkeyspace", + SnapshotTime: &vttime.Time{Seconds: 100}, + }, + expected: &vtctldatapb.CreateKeyspaceResponse{ + Keyspace: &vtctldatapb.Keyspace{ + Name: "testsnapshot", + Keyspace: &topodatapb.Keyspace{ + KeyspaceType: topodatapb.KeyspaceType_SNAPSHOT, + BaseKeyspace: "testkeyspace", + SnapshotTime: &vttime.Time{Seconds: 100}, + }, + }, + }, + vschemaShouldExist: true, + expectedVSchema: &vschemapb.Keyspace{ + Sharded: false, + RequireExplicitRouting: true, + }, + shouldErr: false, + }, + { + name: "invalid keyspace type", + topo: nil, + req: &vtctldatapb.CreateKeyspaceRequest{ + Name: "badkeyspacetype", + Type: 10000000, + }, + expected: nil, + shouldErr: true, + }, + { + name: "keyspace exists/no force", + topo: map[string]*topodatapb.Keyspace{ + "testkeyspace": { + KeyspaceType: topodatapb.KeyspaceType_NORMAL, + ShardingColumnName: "col1", + ShardingColumnType: topodatapb.KeyspaceIdType_UINT64, + }, + }, + req: &vtctldatapb.CreateKeyspaceRequest{ + Name: "testkeyspace", + Type: topodatapb.KeyspaceType_NORMAL, + Force: false, + }, + expected: nil, + shouldErr: true, + }, + { + name: "keyspace exists/force", + topo: map[string]*topodatapb.Keyspace{ + "testkeyspace": { + KeyspaceType: topodatapb.KeyspaceType_NORMAL, + ShardingColumnName: "col1", + ShardingColumnType: topodatapb.KeyspaceIdType_UINT64, + }, + }, + req: &vtctldatapb.CreateKeyspaceRequest{ + Name: "testkeyspace", + Type: topodatapb.KeyspaceType_NORMAL, + Force: true, + }, + expected: &vtctldatapb.CreateKeyspaceResponse{ + Keyspace: &vtctldatapb.Keyspace{ + Name: "testkeyspace", + Keyspace: &topodatapb.Keyspace{ + KeyspaceType: topodatapb.KeyspaceType_NORMAL, + ShardingColumnName: "col1", + ShardingColumnType: topodatapb.KeyspaceIdType_UINT64, + }, + }, + }, + vschemaShouldExist: true, + expectedVSchema: &vschemapb.Keyspace{ + Sharded: false, + }, + shouldErr: false, + }, + { + name: "allow empty vschema", + topo: nil, + req: &vtctldatapb.CreateKeyspaceRequest{ + Name: "testkeyspace", + Type: topodatapb.KeyspaceType_NORMAL, + AllowEmptyVSchema: true, + }, + expected: &vtctldatapb.CreateKeyspaceResponse{ + Keyspace: &vtctldatapb.Keyspace{ + Name: "testkeyspace", + Keyspace: &topodatapb.Keyspace{ + KeyspaceType: topodatapb.KeyspaceType_NORMAL, + }, + }, + }, + vschemaShouldExist: false, + expectedVSchema: nil, + shouldErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.req == nil { + t.Skip("test not yet implemented") + } + + ctx := context.Background() + ts := memorytopo.NewServer(cells...) + vtctld := NewVtctldServer(ts) + + for name, ks := range tt.topo { + testutil.AddKeyspace(ctx, t, ts, &vtctldatapb.Keyspace{ + Name: name, + Keyspace: ks, + }) + } + + for name, vs := range tt.vschemas { + require.NoError(t, ts.SaveVSchema(ctx, name, vs), "error in SaveVSchema(%v, %+v)", name, vs) + } + + // Create the keyspace and make some assertions + resp, err := vtctld.CreateKeyspace(ctx, tt.req) + if tt.shouldErr { + assert.Error(t, err) + return + } + + assert.NoError(t, err) + testutil.AssertKeyspacesEqual(t, tt.expected.Keyspace, resp.Keyspace, "%+v\n%+v\n", tt.expected.Keyspace, resp.Keyspace) + + // Fetch the newly-created keyspace out of the topo and assert on it + ks, err := ts.GetKeyspace(ctx, tt.req.Name) + assert.NoError(t, err, "cannot get keyspace %v after creating", tt.req.Name) + require.NotNil(t, ks.Keyspace) + + actualKs := &vtctldatapb.Keyspace{ + Name: tt.req.Name, + Keyspace: ks.Keyspace, + } + testutil.AssertKeyspacesEqual( + t, + tt.expected.Keyspace, + actualKs, + "created keyspace %v does not match requested keyspace (name = %v) %v", + actualKs, + tt.expected.Keyspace, + ) + + // Finally, check the VSchema + vs, err := ts.GetVSchema(ctx, tt.req.Name) + if !tt.vschemaShouldExist { + assert.True(t, topo.IsErrType(err, topo.NoNode), "vschema should not exist, but got other error = %v", err) + return + } + assert.NoError(t, err) + assert.Equal(t, tt.expectedVSchema, vs) + }) + } +} + +func TestCreateShard(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + keyspaces []*vtctldatapb.Keyspace + shards []*vtctldatapb.Shard + topoErr error + req *vtctldatapb.CreateShardRequest + expected *vtctldatapb.CreateShardResponse + shouldErr bool + }{ + { + name: "success", + keyspaces: []*vtctldatapb.Keyspace{ + { + Name: "testkeyspace", + Keyspace: &topodatapb.Keyspace{}, + }, + }, + shards: nil, + topoErr: nil, + req: &vtctldatapb.CreateShardRequest{ + Keyspace: "testkeyspace", + ShardName: "-", + }, + expected: &vtctldatapb.CreateShardResponse{ + Keyspace: &vtctldatapb.Keyspace{ + Name: "testkeyspace", + Keyspace: &topodatapb.Keyspace{}, + }, + Shard: &vtctldatapb.Shard{ + Keyspace: "testkeyspace", + Name: "-", + Shard: &topodatapb.Shard{ + KeyRange: &topodatapb.KeyRange{}, + IsMasterServing: true, + }, + }, + ShardAlreadyExists: false, + }, + shouldErr: false, + }, + { + name: "include parent", + keyspaces: nil, + shards: nil, + topoErr: nil, + req: &vtctldatapb.CreateShardRequest{ + Keyspace: "testkeyspace", + ShardName: "-", + IncludeParent: true, + }, + expected: &vtctldatapb.CreateShardResponse{ + Keyspace: &vtctldatapb.Keyspace{ + Name: "testkeyspace", + Keyspace: &topodatapb.Keyspace{}, + }, + Shard: &vtctldatapb.Shard{ + Keyspace: "testkeyspace", + Name: "-", + Shard: &topodatapb.Shard{ + KeyRange: &topodatapb.KeyRange{}, + IsMasterServing: true, + }, + }, + ShardAlreadyExists: false, + }, + shouldErr: false, + }, + { + name: "keyspace does not exist", + keyspaces: nil, + shards: nil, + topoErr: nil, + req: &vtctldatapb.CreateShardRequest{ + Keyspace: "testkeyspace", + ShardName: "-", + }, + expected: nil, + shouldErr: true, + }, + { + name: "include parent/keyspace exists/no force", + keyspaces: []*vtctldatapb.Keyspace{ + { + Name: "testkeyspace", + Keyspace: &topodatapb.Keyspace{}, + }, + }, + shards: nil, + topoErr: nil, + req: &vtctldatapb.CreateShardRequest{ + Keyspace: "testkeyspace", + ShardName: "-", + IncludeParent: true, + }, + expected: nil, + shouldErr: true, + }, + { + name: "include parent/keyspace exists/force", + keyspaces: []*vtctldatapb.Keyspace{ + { + Name: "testkeyspace", + Keyspace: &topodatapb.Keyspace{}, + }, + }, + shards: nil, + topoErr: nil, + req: &vtctldatapb.CreateShardRequest{ + Keyspace: "testkeyspace", + ShardName: "-", + IncludeParent: true, + Force: true, + }, + expected: &vtctldatapb.CreateShardResponse{ + Keyspace: &vtctldatapb.Keyspace{ + Name: "testkeyspace", + Keyspace: &topodatapb.Keyspace{}, + }, + Shard: &vtctldatapb.Shard{ + Keyspace: "testkeyspace", + Name: "-", + Shard: &topodatapb.Shard{ + KeyRange: &topodatapb.KeyRange{}, + IsMasterServing: true, + }, + }, + ShardAlreadyExists: false, + }, + shouldErr: false, + }, + { + name: "shard exists/no force", + keyspaces: []*vtctldatapb.Keyspace{ + { + Name: "testkeyspace", + Keyspace: &topodatapb.Keyspace{}, + }, + }, + shards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + }, + topoErr: nil, + req: &vtctldatapb.CreateShardRequest{ + Keyspace: "testkeyspace", + ShardName: "-", + }, + expected: nil, + shouldErr: true, + }, + { + name: "shard exists/force", + keyspaces: []*vtctldatapb.Keyspace{ + { + Name: "testkeyspace", + Keyspace: &topodatapb.Keyspace{}, + }, + }, + shards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + }, + topoErr: nil, + req: &vtctldatapb.CreateShardRequest{ + Keyspace: "testkeyspace", + ShardName: "-", + Force: true, + }, + expected: &vtctldatapb.CreateShardResponse{ + Keyspace: &vtctldatapb.Keyspace{ + Name: "testkeyspace", + Keyspace: &topodatapb.Keyspace{}, + }, + Shard: &vtctldatapb.Shard{ + Keyspace: "testkeyspace", + Name: "-", + Shard: &topodatapb.Shard{ + KeyRange: &topodatapb.KeyRange{}, + IsMasterServing: true, + }, + }, + ShardAlreadyExists: true, + }, + shouldErr: false, + }, + { + name: "topo is down", + keyspaces: []*vtctldatapb.Keyspace{ + { + Name: "testkeyspace", + Keyspace: &topodatapb.Keyspace{}, + }, + }, + shards: nil, + topoErr: assert.AnError, + req: &vtctldatapb.CreateShardRequest{ + Keyspace: "testkeyspace", + ShardName: "-", + }, + expected: nil, + shouldErr: true, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + if tt.req == nil { + t.Skip("focusing on other tests") + } + + ctx := context.Background() + ts, topofactory := memorytopo.NewServerAndFactory("zone1") + vtctld := NewVtctldServer(ts) + + for _, ks := range tt.keyspaces { + testutil.AddKeyspace(ctx, t, ts, ks) + } + + testutil.AddShards(ctx, t, ts, tt.shards...) + + if tt.topoErr != nil { + topofactory.SetError(tt.topoErr) + } + + resp, err := vtctld.CreateShard(ctx, tt.req) + if tt.shouldErr { + assert.Error(t, err) + return + } + + assert.NoError(t, err) + assert.Equal(t, tt.expected, resp) + }) + } +} + +func TestDeleteKeyspace(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + keyspaces []*vtctldatapb.Keyspace + shards []*vtctldatapb.Shard + srvKeyspaces map[string]map[string]*topodatapb.SrvKeyspace + topoErr error + req *vtctldatapb.DeleteKeyspaceRequest + expected *vtctldatapb.DeleteKeyspaceResponse + expectedRemainingKeyspaces []string + expectedRemainingShards map[string][]string + shouldErr bool + }{ + { + name: "success", + keyspaces: []*vtctldatapb.Keyspace{ + { + Name: "testkeyspace", + Keyspace: &topodatapb.Keyspace{}, + }, + }, + shards: nil, + srvKeyspaces: nil, + topoErr: nil, + req: &vtctldatapb.DeleteKeyspaceRequest{ + Keyspace: "testkeyspace", + }, + expected: &vtctldatapb.DeleteKeyspaceResponse{}, + expectedRemainingKeyspaces: []string{}, + expectedRemainingShards: map[string][]string{}, + shouldErr: false, + }, + { + name: "keyspace does not exist", + keyspaces: []*vtctldatapb.Keyspace{ + { + Name: "otherkeyspace", + Keyspace: &topodatapb.Keyspace{}, + }, + }, + shards: nil, + srvKeyspaces: nil, + topoErr: nil, + req: &vtctldatapb.DeleteKeyspaceRequest{ + Keyspace: "testkeyspace", + }, + expected: nil, + expectedRemainingKeyspaces: []string{"otherkeyspace"}, + expectedRemainingShards: map[string][]string{ + "otherkeyspace": nil, + }, + shouldErr: true, + }, + { + name: "keyspace has shards/Recursive=false", + keyspaces: []*vtctldatapb.Keyspace{ + { + Name: "testkeyspace", + Keyspace: &topodatapb.Keyspace{}, + }, + }, + shards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-80", + }, + { + Keyspace: "testkeyspace", + Name: "80-", + }, + }, + srvKeyspaces: nil, + topoErr: nil, + req: &vtctldatapb.DeleteKeyspaceRequest{ + Keyspace: "testkeyspace", + }, + expected: nil, + expectedRemainingKeyspaces: []string{"testkeyspace"}, + expectedRemainingShards: map[string][]string{ + "testkeyspace": {"-80", "80-"}, + }, + shouldErr: true, + }, + { + name: "keyspace has shards/Recursive=true", + keyspaces: []*vtctldatapb.Keyspace{ + { + Name: "testkeyspace", + Keyspace: &topodatapb.Keyspace{}, + }, + }, + shards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-80", + }, + { + Keyspace: "testkeyspace", + Name: "80-", + }, + }, + srvKeyspaces: nil, + topoErr: nil, + req: &vtctldatapb.DeleteKeyspaceRequest{ + Keyspace: "testkeyspace", + Recursive: true, + }, + expected: &vtctldatapb.DeleteKeyspaceResponse{}, + expectedRemainingKeyspaces: []string{}, + expectedRemainingShards: map[string][]string{}, + shouldErr: false, + }, + // Not sure how to force this case because we always pass + // (Recursive=true, EvenIfServing=true) so anything short of "topo + // server is down" won't fail, and "topo server is down" will cause us + // to error before we even reach this point in the code, so, ¯\_(ツ)_/¯. + // { + // name: "recursive/cannot delete shard", + // }, + { + name: "topo error", + keyspaces: []*vtctldatapb.Keyspace{ + { + Name: "testkeyspace", + Keyspace: &topodatapb.Keyspace{}, + }, + }, + shards: nil, + srvKeyspaces: nil, + topoErr: assert.AnError, + req: &vtctldatapb.DeleteKeyspaceRequest{ + Keyspace: "testkeyspace", + }, + expected: nil, + expectedRemainingKeyspaces: []string{"testkeyspace"}, + expectedRemainingShards: map[string][]string{ + "testkeyspace": nil, + }, + shouldErr: true, + }, + } + + for _, tt := range tests { + tt := tt + + t.Run(tt.name, func(t *testing.T) { + cells := []string{"zone1", "zone2", "zone3"} + + ctx := context.Background() + ts, topofactory := memorytopo.NewServerAndFactory(cells...) + vtctld := NewVtctldServer(ts) + + testutil.AddKeyspaces(ctx, t, ts, tt.keyspaces...) + testutil.AddShards(ctx, t, ts, tt.shards...) + testutil.UpdateSrvKeyspaces(ctx, t, ts, tt.srvKeyspaces) + + if tt.topoErr != nil { + topofactory.SetError(tt.topoErr) + } + + defer func() { + if tt.expectedRemainingKeyspaces == nil { + return + } + + topofactory.SetError(nil) + + keyspaces, err := ts.GetKeyspaces(ctx) + require.NoError(t, err, "cannot get keyspaces names after DeleteKeyspace call") + assert.ElementsMatch(t, tt.expectedRemainingKeyspaces, keyspaces) + + if tt.expectedRemainingShards == nil { + return + } + + remainingShards := make(map[string][]string, len(keyspaces)) + + for _, ks := range keyspaces { + shards, err := ts.GetShardNames(ctx, ks) + require.NoError(t, err, "cannot get shard names for keyspace %s", ks) + + remainingShards[ks] = shards + } + + assert.Equal(t, tt.expectedRemainingShards, remainingShards) + }() + + resp, err := vtctld.DeleteKeyspace(ctx, tt.req) + if tt.shouldErr { + assert.Error(t, err) + + return + } + + assert.NoError(t, err) + assert.Equal(t, tt.expected, resp) + }) + } +} + +func TestDeleteShards(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + shards []*vtctldatapb.Shard + tablets []*topodatapb.Tablet + replicationGraphs []*topo.ShardReplicationInfo + srvKeyspaces map[string]map[string]*topodatapb.SrvKeyspace + topoErr error + req *vtctldatapb.DeleteShardsRequest + expected *vtctldatapb.DeleteShardsResponse + expectedRemainingShards []*vtctldatapb.Shard + shouldErr bool + }{ + { + name: "success", + shards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + }, + tablets: nil, + topoErr: nil, + req: &vtctldatapb.DeleteShardsRequest{ + Shards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + }, + }, + expected: &vtctldatapb.DeleteShardsResponse{}, + expectedRemainingShards: []*vtctldatapb.Shard{}, + shouldErr: false, + }, + { + name: "shard not found", + shards: nil, + tablets: nil, + topoErr: nil, + req: &vtctldatapb.DeleteShardsRequest{ + Shards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + }, + }, + expected: nil, + shouldErr: true, + }, + { + name: "multiple shards", + shards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + { + Keyspace: "otherkeyspace", + Name: "-80", + }, + { + Keyspace: "otherkeyspace", + Name: "80-", + }, + }, + tablets: nil, + topoErr: nil, + req: &vtctldatapb.DeleteShardsRequest{ + Shards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + { + Keyspace: "otherkeyspace", + Name: "-80", + }, + }, + }, + expected: &vtctldatapb.DeleteShardsResponse{}, + expectedRemainingShards: []*vtctldatapb.Shard{ + { + Keyspace: "otherkeyspace", + Name: "80-", + }, + }, + shouldErr: false, + }, + { + name: "topo is down", + shards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + }, + tablets: nil, + topoErr: assert.AnError, + req: &vtctldatapb.DeleteShardsRequest{ + Shards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + }, + }, + expected: nil, + expectedRemainingShards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + }, + shouldErr: true, + }, + { + name: "shard is serving/EvenIfServing=false", + shards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + }, + tablets: nil, + srvKeyspaces: map[string]map[string]*topodatapb.SrvKeyspace{ + "zone1": { + "testkeyspace": &topodatapb.SrvKeyspace{ + Partitions: []*topodatapb.SrvKeyspace_KeyspacePartition{ + { + ServedType: topodatapb.TabletType_MASTER, + ShardReferences: []*topodatapb.ShardReference{ + { + Name: "-", + KeyRange: &topodatapb.KeyRange{}, + }, + }, + }, + }, + }, + }, + }, + topoErr: nil, + req: &vtctldatapb.DeleteShardsRequest{ + Shards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + }, + }, + expected: nil, + expectedRemainingShards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + }, + shouldErr: true, + }, + { + name: "shard is serving/EvenIfServing=true", + shards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + }, + tablets: nil, + srvKeyspaces: map[string]map[string]*topodatapb.SrvKeyspace{ + "zone1": { + "testkeyspace": &topodatapb.SrvKeyspace{ + Partitions: []*topodatapb.SrvKeyspace_KeyspacePartition{ + { + ServedType: topodatapb.TabletType_MASTER, + ShardReferences: []*topodatapb.ShardReference{ + { + Name: "-", + KeyRange: &topodatapb.KeyRange{}, + }, + }, + }, + }, + }, + }, + }, + topoErr: nil, + req: &vtctldatapb.DeleteShardsRequest{ + Shards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + }, + EvenIfServing: true, + }, + expected: &vtctldatapb.DeleteShardsResponse{}, + expectedRemainingShards: []*vtctldatapb.Shard{}, + shouldErr: false, + }, + { + name: "ShardReplication in topo", + shards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + }, + tablets: nil, + replicationGraphs: []*topo.ShardReplicationInfo{ + topo.NewShardReplicationInfo(&topodatapb.ShardReplication{ + Nodes: []*topodatapb.ShardReplication_Node{ + { + TabletAlias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + }, + }, + }, "zone1", "testkeyspace", "-"), + topo.NewShardReplicationInfo(&topodatapb.ShardReplication{ + Nodes: []*topodatapb.ShardReplication_Node{ + { + TabletAlias: &topodatapb.TabletAlias{ + Cell: "zone2", + Uid: 200, + }, + }, + }, + }, "zone2", "testkeyspace", "-"), + topo.NewShardReplicationInfo(&topodatapb.ShardReplication{ + Nodes: []*topodatapb.ShardReplication_Node{ + { + TabletAlias: &topodatapb.TabletAlias{ + Cell: "zone3", + Uid: 300, + }, + }, + }, + }, "zone3", "testkeyspace", "-"), + }, + topoErr: nil, + req: &vtctldatapb.DeleteShardsRequest{ + Shards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + }, + }, + expected: &vtctldatapb.DeleteShardsResponse{}, + expectedRemainingShards: []*vtctldatapb.Shard{}, + shouldErr: false, + }, + { + name: "shard has tablets/Recursive=false", + shards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + }, + tablets: []*topodatapb.Tablet{ + { + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + Keyspace: "testkeyspace", + Shard: "-", + }, + }, + topoErr: nil, + req: &vtctldatapb.DeleteShardsRequest{ + Shards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + }, + }, + expected: nil, + expectedRemainingShards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + }, + shouldErr: true, + }, + { + name: "shard has tablets/Recursive=true", + shards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + }, + tablets: []*topodatapb.Tablet{ + { + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + Keyspace: "testkeyspace", + Shard: "-", + }, + }, + topoErr: nil, + req: &vtctldatapb.DeleteShardsRequest{ + Shards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + }, + Recursive: true, + }, + expected: &vtctldatapb.DeleteShardsResponse{}, + expectedRemainingShards: []*vtctldatapb.Shard{}, + shouldErr: false, + }, + { + name: "tablets in topo belonging to other shard", + shards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-80", + }, + { + Keyspace: "testkeyspace", + Name: "80-", + }, + }, + tablets: []*topodatapb.Tablet{ + { + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + Keyspace: "testkeyspace", + Shard: "80-", + }, + }, + topoErr: nil, + req: &vtctldatapb.DeleteShardsRequest{ + Shards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-80", + }, + }, + }, + expected: &vtctldatapb.DeleteShardsResponse{}, + expectedRemainingShards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "80-", + }, + }, + shouldErr: false, + }, + } + + for _, tt := range tests { + tt := tt + + t.Run(tt.name, func(t *testing.T) { + cells := []string{"zone1", "zone2", "zone3"} + + ctx := context.Background() + ts, topofactory := memorytopo.NewServerAndFactory(cells...) + vtctld := NewVtctldServer(ts) + + testutil.AddShards(ctx, t, ts, tt.shards...) + testutil.AddTablets(ctx, t, ts, tt.tablets...) + testutil.SetupReplicationGraphs(ctx, t, ts, tt.replicationGraphs...) + testutil.UpdateSrvKeyspaces(ctx, t, ts, tt.srvKeyspaces) + + if tt.topoErr != nil { + topofactory.SetError(tt.topoErr) + } + + if tt.expectedRemainingShards != nil { + defer func() { + topofactory.SetError(nil) + + actualShards := []*vtctldatapb.Shard{} + + keyspaces, err := ts.GetKeyspaces(ctx) + require.NoError(t, err, "cannot get keyspace names to check remaining shards") + + for _, ks := range keyspaces { + shards, err := ts.GetShardNames(ctx, ks) + require.NoError(t, err, "cannot get shard names for keyspace %s", ks) + + for _, shard := range shards { + actualShards = append(actualShards, &vtctldatapb.Shard{ + Keyspace: ks, + Name: shard, + }) + } + } + + assert.ElementsMatch(t, tt.expectedRemainingShards, actualShards) + }() + } + + resp, err := vtctld.DeleteShards(ctx, tt.req) + if tt.shouldErr { + assert.Error(t, err) + + return + } + + assert.NoError(t, err) + assert.Equal(t, tt.expected, resp) + }) + } +} + +func TestDeleteTablets(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + tablets []*topodatapb.Tablet + shardFieldUpdates map[string]func(*topo.ShardInfo) error + lockedShards []*vtctldatapb.Shard + topoError error + req *vtctldatapb.DeleteTabletsRequest + expected *vtctldatapb.DeleteTabletsResponse + expectedRemainingTablets []*topodatapb.Tablet + shouldErr bool + }{ + { + name: "single replica", + tablets: []*topodatapb.Tablet{ + { + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + Type: topodatapb.TabletType_REPLICA, + Keyspace: "testkeyspace", + Shard: "-", + }, + }, + lockedShards: nil, + topoError: nil, + req: &vtctldatapb.DeleteTabletsRequest{ + TabletAliases: []*topodatapb.TabletAlias{ + { + Cell: "zone1", + Uid: 100, + }, + }, + }, + expected: &vtctldatapb.DeleteTabletsResponse{}, + expectedRemainingTablets: []*topodatapb.Tablet{}, + shouldErr: false, + }, + { + name: "single primary/no AllowPrimary", + tablets: []*topodatapb.Tablet{ + { + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + Type: topodatapb.TabletType_MASTER, + Keyspace: "testkeyspace", + Shard: "-", + MasterTermStartTime: &vttime.Time{ + Seconds: 100, + Nanoseconds: 10, + }, + }, + }, + lockedShards: nil, + topoError: nil, + req: &vtctldatapb.DeleteTabletsRequest{ + TabletAliases: []*topodatapb.TabletAlias{ + { + Cell: "zone1", + Uid: 100, + }, + }, + }, + expected: nil, + shouldErr: true, + }, + { + name: "single primary/with AllowPrimary", + tablets: []*topodatapb.Tablet{ + { + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + Type: topodatapb.TabletType_MASTER, + Keyspace: "testkeyspace", + Shard: "-", + MasterTermStartTime: &vttime.Time{ + Seconds: 100, + Nanoseconds: 10, + }, + }, + }, + lockedShards: nil, + topoError: nil, + req: &vtctldatapb.DeleteTabletsRequest{ + TabletAliases: []*topodatapb.TabletAlias{ + { + Cell: "zone1", + Uid: 100, + }, + }, + AllowPrimary: true, + }, + expected: &vtctldatapb.DeleteTabletsResponse{}, + expectedRemainingTablets: []*topodatapb.Tablet{}, + shouldErr: false, + }, + { + name: "multiple tablets", + tablets: []*topodatapb.Tablet{ + { + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + Type: topodatapb.TabletType_REPLICA, + Keyspace: "testkeyspace", + Shard: "-", + }, + { + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 101, + }, + Type: topodatapb.TabletType_REPLICA, + Keyspace: "testkeyspace", + Shard: "-", + }, + { + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 102, + }, + Type: topodatapb.TabletType_REPLICA, + Keyspace: "testkeyspace", + Shard: "-", + }, + }, + lockedShards: nil, + topoError: nil, + req: &vtctldatapb.DeleteTabletsRequest{ + TabletAliases: []*topodatapb.TabletAlias{ + { + Cell: "zone1", + Uid: 100, + }, + { + Cell: "zone1", + Uid: 102, + }, + }, + }, + expected: &vtctldatapb.DeleteTabletsResponse{}, + expectedRemainingTablets: []*topodatapb.Tablet{ + { + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 101, + }, + Type: topodatapb.TabletType_REPLICA, + Keyspace: "testkeyspace", + Shard: "-", + }, + }, + shouldErr: false, + }, + { + name: "stale primary record", + tablets: []*topodatapb.Tablet{ + { + // The stale primary we're going to delete. + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + Type: topodatapb.TabletType_MASTER, + Keyspace: "testkeyspace", + Shard: "-", + MasterTermStartTime: &vttime.Time{ + Seconds: 100, + Nanoseconds: 10, + }, + }, + { + // The real shard primary, which we'll update in the shard + // record below. + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 101, + }, + Type: topodatapb.TabletType_MASTER, + Keyspace: "testkeyspace", + Shard: "-", + MasterTermStartTime: &vttime.Time{ + Seconds: 1001, + Nanoseconds: 101, + }, + }, + }, + shardFieldUpdates: map[string]func(*topo.ShardInfo) error{ + "testkeyspace/-": func(si *topo.ShardInfo) error { + si.MasterAlias = &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 101, + } + si.MasterTermStartTime = &vttime.Time{ + Seconds: 1001, + Nanoseconds: 101, + } + + return nil + }, + }, + lockedShards: nil, + topoError: nil, + req: &vtctldatapb.DeleteTabletsRequest{ + TabletAliases: []*topodatapb.TabletAlias{ + { + Cell: "zone1", + Uid: 100, + }, + }, + }, + expected: &vtctldatapb.DeleteTabletsResponse{}, + expectedRemainingTablets: []*topodatapb.Tablet{ + { + // The true shard primary still exists (phew!) + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 101, + }, + Type: topodatapb.TabletType_MASTER, + Keyspace: "testkeyspace", + Shard: "-", + MasterTermStartTime: &vttime.Time{ + Seconds: 1001, + Nanoseconds: 101, + }, + }, + }, + shouldErr: false, + }, + { + name: "tablet not found", + tablets: []*topodatapb.Tablet{ + { + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + Type: topodatapb.TabletType_REPLICA, + Keyspace: "testkeyspace", + Shard: "-", + }, + }, + lockedShards: nil, + topoError: nil, + req: &vtctldatapb.DeleteTabletsRequest{ + TabletAliases: []*topodatapb.TabletAlias{ + { + Cell: "zone1", + Uid: 200, + }, + }, + }, + expected: nil, + expectedRemainingTablets: []*topodatapb.Tablet{ + { + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + Type: topodatapb.TabletType_REPLICA, + Keyspace: "testkeyspace", + Shard: "-", + }, + }, + shouldErr: true, + }, + { + name: "shard is locked", + tablets: []*topodatapb.Tablet{ + { + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + Type: topodatapb.TabletType_MASTER, + Keyspace: "testkeyspace", + Shard: "-", + MasterTermStartTime: &vttime.Time{ + Seconds: 100, + Nanoseconds: 10, + }, + }, + }, + lockedShards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + }, + topoError: nil, + req: &vtctldatapb.DeleteTabletsRequest{ + TabletAliases: []*topodatapb.TabletAlias{ + { + Cell: "zone1", + Uid: 100, + }, + }, + AllowPrimary: true, + }, + expected: nil, + expectedRemainingTablets: []*topodatapb.Tablet{ + { + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + Type: topodatapb.TabletType_MASTER, + Keyspace: "testkeyspace", + Shard: "-", + MasterTermStartTime: &vttime.Time{ + Seconds: 100, + Nanoseconds: 10, + }, + }, + }, + shouldErr: true, + }, + { + name: "another shard is locked", + tablets: []*topodatapb.Tablet{ + { + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + Type: topodatapb.TabletType_MASTER, + Keyspace: "testkeyspace", + Shard: "-80", + MasterTermStartTime: &vttime.Time{ + Seconds: 100, + Nanoseconds: 10, + }, + }, + { + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 200, + }, + Type: topodatapb.TabletType_MASTER, + Keyspace: "testkeyspace", + Shard: "80-", + MasterTermStartTime: &vttime.Time{ + Seconds: 200, + Nanoseconds: 20, + }, + }, + }, + lockedShards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "80-", + }, + }, + topoError: nil, + req: &vtctldatapb.DeleteTabletsRequest{ + TabletAliases: []*topodatapb.TabletAlias{ + { + // testkeyspace/-80 + Cell: "zone1", + Uid: 100, + }, + }, + AllowPrimary: true, + }, + expected: &vtctldatapb.DeleteTabletsResponse{}, + expectedRemainingTablets: []*topodatapb.Tablet{ + { + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 200, + }, + Type: topodatapb.TabletType_MASTER, + Keyspace: "testkeyspace", + Shard: "80-", + MasterTermStartTime: &vttime.Time{ + Seconds: 200, + Nanoseconds: 20, + }, + }, + }, + shouldErr: false, + }, + { + name: "topo server is down", + tablets: []*topodatapb.Tablet{ + { + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + Type: topodatapb.TabletType_REPLICA, + Keyspace: "testkeyspace", + Shard: "-", + }, + }, + lockedShards: nil, + topoError: assert.AnError, + req: &vtctldatapb.DeleteTabletsRequest{ + TabletAliases: []*topodatapb.TabletAlias{ + { + Cell: "zone1", + Uid: 200, + }, + }, + }, + expected: nil, + expectedRemainingTablets: []*topodatapb.Tablet{ + { + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + Type: topodatapb.TabletType_REPLICA, + Keyspace: "testkeyspace", + Shard: "-", + }, + }, + shouldErr: true, + }, + } + + for _, tt := range tests { + tt := tt + + t.Run(tt.name, func(t *testing.T) { + if tt.req == nil { + t.Skip("focusing on other tests") + } + + ctx := context.Background() + ts, topofactory := memorytopo.NewServerAndFactory("zone1") + vtctld := NewVtctldServer(ts) + + // Setup tablets and shards + for _, tablet := range tt.tablets { + testutil.AddTablet(ctx, t, ts, tablet) + } + + for key, updateFn := range tt.shardFieldUpdates { + ks, shard, err := topoproto.ParseKeyspaceShard(key) + require.NoError(t, err, "bad keyspace/shard provided in shardFieldUpdates: %s", key) + + _, err = ts.UpdateShardFields(ctx, ks, shard, updateFn) + require.NoError(t, err, "failed to update shard fields for %s", key) + } + + // Set locks + for _, shard := range tt.lockedShards { + lctx, unlock, lerr := ts.LockShard(ctx, shard.Keyspace, shard.Name, "testing locked shard") + require.NoError(t, lerr, "cannot lock shard %s/%s", shard.Keyspace, shard.Name) + // unlock at the end of the test, we don't care about this error + // value anymore + defer unlock(&lerr) + + // we do, however, care that the lock context gets propogated + // both to additional calls to lock, and to the actual RPC call. + ctx = lctx + } + + // Set errors + if tt.topoError != nil { + topofactory.SetError(tt.topoError) + } + + checkRemainingTablets := func() { + topofactory.SetError(nil) + + resp, err := vtctld.GetTablets(ctx, &vtctldatapb.GetTabletsRequest{}) + assert.NoError(t, err, "cannot look up tablets from topo after issuing DeleteTablets request") + + assert.ElementsMatch(t, tt.expectedRemainingTablets, resp.Tablets) + } + + // Run the test + resp, err := vtctld.DeleteTablets(ctx, tt.req) + if tt.shouldErr { + assert.Error(t, err) + + if tt.expectedRemainingTablets != nil { + checkRemainingTablets() + } + + return + } + + assert.NoError(t, err) + assert.Equal(t, tt.expected, resp) + checkRemainingTablets() + }) + } +} + +func TestFindAllShardsInKeyspace(t *testing.T) { + ctx := context.Background() + ts := memorytopo.NewServer("cell1") + vtctld := NewVtctldServer(ts) + + ks := &vtctldatapb.Keyspace{ + Name: "testkeyspace", + Keyspace: &topodatapb.Keyspace{}, + } + addKeyspace(ctx, t, ts, ks) + + si1, err := ts.GetOrCreateShard(ctx, ks.Name, "-80") + require.NoError(t, err) + si2, err := ts.GetOrCreateShard(ctx, ks.Name, "80-") + require.NoError(t, err) + + resp, err := vtctld.FindAllShardsInKeyspace(ctx, &vtctldatapb.FindAllShardsInKeyspaceRequest{Keyspace: ks.Name}) + assert.NoError(t, err) + assert.NotNil(t, resp) + + expected := map[string]*vtctldatapb.Shard{ + "-80": { + Keyspace: ks.Name, + Name: "-80", + Shard: si1.Shard, + }, + "80-": { + Keyspace: ks.Name, + Name: "80-", + Shard: si2.Shard, + }, + } + + assert.Equal(t, expected, resp.Shards) + + _, err = vtctld.FindAllShardsInKeyspace(ctx, &vtctldatapb.FindAllShardsInKeyspaceRequest{Keyspace: "nothing"}) + assert.Error(t, err) +} + +func TestGetBackups(t *testing.T) { + ctx := context.Background() + ts := memorytopo.NewServer() + vtctld := NewVtctldServer(ts) + + testutil.BackupStorage.Backups = map[string][]string{ + "testkeyspace/-": {"backup1", "backup2"}, + } + + expected := &vtctldatapb.GetBackupsResponse{ + Backups: []*mysqlctlpb.BackupInfo{ + { + Directory: "testkeyspace/-", + Name: "backup1", + }, + { + Directory: "testkeyspace/-", + Name: "backup2", + }, + }, + } + + resp, err := vtctld.GetBackups(ctx, &vtctldatapb.GetBackupsRequest{ + Keyspace: "testkeyspace", + Shard: "-", + }) + assert.NoError(t, err) + assert.Equal(t, expected, resp) + + t.Run("no backupstorage", func(t *testing.T) { + *backupstorage.BackupStorageImplementation = "doesnotexist" + defer func() { *backupstorage.BackupStorageImplementation = testutil.BackupStorageImplementation }() + + _, err := vtctld.GetBackups(ctx, &vtctldatapb.GetBackupsRequest{ + Keyspace: "testkeyspace", + Shard: "-", + }) + assert.Error(t, err) + }) + + t.Run("listbackups error", func(t *testing.T) { + testutil.BackupStorage.ListBackupsError = assert.AnError + defer func() { testutil.BackupStorage.ListBackupsError = nil }() + + _, err := vtctld.GetBackups(ctx, &vtctldatapb.GetBackupsRequest{ + Keyspace: "testkeyspace", + Shard: "-", + }) + assert.Error(t, err) + }) +} + +func TestGetKeyspace(t *testing.T) { + ctx := context.Background() + ts := memorytopo.NewServer("cell1") + vtctld := NewVtctldServer(ts) + + expected := &vtctldatapb.GetKeyspaceResponse{ + Keyspace: &vtctldatapb.Keyspace{ + Name: "testkeyspace", + Keyspace: &topodatapb.Keyspace{ + ShardingColumnName: "col1", + }, + }, + } + addKeyspace(ctx, t, ts, expected.Keyspace) + + ks, err := vtctld.GetKeyspace(ctx, &vtctldatapb.GetKeyspaceRequest{Keyspace: expected.Keyspace.Name}) + assert.NoError(t, err) + assert.Equal(t, expected, ks) + + _, err = vtctld.GetKeyspace(ctx, &vtctldatapb.GetKeyspaceRequest{Keyspace: "notfound"}) + assert.Error(t, err) +} + +func addKeyspace(ctx context.Context, t *testing.T, ts *topo.Server, ks *vtctldatapb.Keyspace) { + in := *ks.Keyspace // take a copy to avoid the XXX_ fields changing + + err := ts.CreateKeyspace(ctx, ks.Name, &in) + require.NoError(t, err) +} + +func TestGetKeyspaces(t *testing.T) { + ctx := context.Background() + ts, topofactory := memorytopo.NewServerAndFactory("cell1") + vtctld := NewVtctldServer(ts) + + resp, err := vtctld.GetKeyspaces(ctx, &vtctldatapb.GetKeyspacesRequest{}) + assert.NoError(t, err) + assert.Empty(t, resp.Keyspaces) + + expected := []*vtctldatapb.Keyspace{ + { + Name: "ks1", + Keyspace: &topodatapb.Keyspace{ + ShardingColumnName: "ks1_col1", + }, + }, + { + Name: "ks2", + Keyspace: &topodatapb.Keyspace{ + ShardingColumnName: "ks2_col1", + }, + }, + { + Name: "ks3", + Keyspace: &topodatapb.Keyspace{ + ShardingColumnName: "ks3_col1", + }, + }, + } + for _, ks := range expected { + addKeyspace(ctx, t, ts, ks) + } + + resp, err = vtctld.GetKeyspaces(ctx, &vtctldatapb.GetKeyspacesRequest{}) + assert.NoError(t, err) + assert.Equal(t, expected, resp.Keyspaces) + + topofactory.SetError(errors.New("error from toposerver")) + + _, err = vtctld.GetKeyspaces(ctx, &vtctldatapb.GetKeyspacesRequest{}) + assert.Error(t, err) +} + +func TestGetTablet(t *testing.T) { + ctx := context.Background() + ts := memorytopo.NewServer("cell1") + vtctld := NewVtctldServer(ts) + + tablet := &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "cell1", + Uid: 100, + }, + Hostname: "localhost", + Keyspace: "testkeyspace", + Shard: "-", + Type: topodatapb.TabletType_REPLICA, + } + + testutil.AddTablet(ctx, t, ts, tablet) + + resp, err := vtctld.GetTablet(ctx, &vtctldatapb.GetTabletRequest{ + TabletAlias: &topodatapb.TabletAlias{ + Cell: "cell1", + Uid: 100, + }, + }) + assert.NoError(t, err) + assert.Equal(t, resp.Tablet, tablet) + + // not found + _, err = vtctld.GetTablet(ctx, &vtctldatapb.GetTabletRequest{ + TabletAlias: &topodatapb.TabletAlias{ + Cell: "cell1", + Uid: 101, + }, + }) + assert.Error(t, err) +} + +func TestGetSchema(t *testing.T) { + ctx := context.Background() + ts := memorytopo.NewServer("zone1") + tmc := testutil.TabletManagerClient{ + GetSchemaResults: map[string]struct { + Schema *tabletmanagerdatapb.SchemaDefinition + Error error + }{}, + } + vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { + return NewVtctldServer(ts) + }) + + validAlias := &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + } + testutil.AddTablet(ctx, t, ts, &topodatapb.Tablet{ + Alias: validAlias, + }) + otherAlias := &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 101, + } + testutil.AddTablet(ctx, t, ts, &topodatapb.Tablet{ + Alias: otherAlias, + }) + + // we need to run this on each test case or they will pollute each other + setupSchema := func() { + tmc.GetSchemaResults[topoproto.TabletAliasString(validAlias)] = struct { + Schema *tabletmanagerdatapb.SchemaDefinition + Error error + }{ + Schema: &tabletmanagerdatapb.SchemaDefinition{ + DatabaseSchema: "CREATE DATABASE vt_testkeyspace", + TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ + { + Name: "t1", + Schema: `CREATE TABLE t1 ( + id int(11) not null, + PRIMARY KEY (id) +);`, + Type: "BASE", + Columns: []string{"id"}, + DataLength: 100, + RowCount: 50, + Fields: []*querypb.Field{ + { + Name: "id", + Type: querypb.Type_INT32, + }, + }, + }, + }, + }, + Error: nil, + } + } + + tests := []*struct { + name string + req *vtctldatapb.GetSchemaRequest + expected *vtctldatapb.GetSchemaResponse + shouldErr bool + }{ + { + name: "normal path", + req: &vtctldatapb.GetSchemaRequest{ + TabletAlias: validAlias, + }, + expected: &vtctldatapb.GetSchemaResponse{ + Schema: &tabletmanagerdatapb.SchemaDefinition{ + DatabaseSchema: "CREATE DATABASE vt_testkeyspace", + TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ + { + Name: "t1", + Schema: `CREATE TABLE t1 ( + id int(11) not null, + PRIMARY KEY (id) +);`, + Type: "BASE", + Columns: []string{"id"}, + DataLength: 100, + RowCount: 50, + Fields: []*querypb.Field{ + { + Name: "id", + Type: querypb.Type_INT32, + }, + }, + }, + }, + }, + }, + shouldErr: false, + }, + { + name: "table names only", + req: &vtctldatapb.GetSchemaRequest{ + TabletAlias: validAlias, + TableNamesOnly: true, + }, + expected: &vtctldatapb.GetSchemaResponse{ + Schema: &tabletmanagerdatapb.SchemaDefinition{ + DatabaseSchema: "CREATE DATABASE vt_testkeyspace", + TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ + { + Name: "t1", + }, + }, + }, + }, + shouldErr: false, + }, + { + name: "table sizes only", + req: &vtctldatapb.GetSchemaRequest{ + TabletAlias: validAlias, + TableSizesOnly: true, + }, + expected: &vtctldatapb.GetSchemaResponse{ + Schema: &tabletmanagerdatapb.SchemaDefinition{ + DatabaseSchema: "CREATE DATABASE vt_testkeyspace", + TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ + { + Name: "t1", + Type: "BASE", + DataLength: 100, + RowCount: 50, + }, + }, + }, + }, + shouldErr: false, + }, + { + name: "table names take precedence over table sizes", + req: &vtctldatapb.GetSchemaRequest{ + TabletAlias: validAlias, + TableNamesOnly: true, + TableSizesOnly: true, + }, + expected: &vtctldatapb.GetSchemaResponse{ + Schema: &tabletmanagerdatapb.SchemaDefinition{ + DatabaseSchema: "CREATE DATABASE vt_testkeyspace", + TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ + { + Name: "t1", + }, + }, + }, + }, + shouldErr: false, + }, + // error cases + { + name: "no tablet", + req: &vtctldatapb.GetSchemaRequest{ + TabletAlias: &topodatapb.TabletAlias{ + Cell: "notfound", + Uid: 100, + }, + }, + expected: nil, + shouldErr: true, + }, + { + name: "no schema", + req: &vtctldatapb.GetSchemaRequest{ + TabletAlias: otherAlias, + }, + expected: nil, + shouldErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + setupSchema() + + resp, err := vtctld.GetSchema(ctx, tt.req) + if tt.shouldErr { + assert.Error(t, err) + return + } + + assert.NoError(t, err) + assert.Equal(t, tt.expected, resp) + }) + } +} + +func TestGetShard(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + topo []*vtctldatapb.Shard + topoError error + req *vtctldatapb.GetShardRequest + expected *vtctldatapb.GetShardResponse + shouldErr bool + }{ + { + name: "success", + topo: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + }, + topoError: nil, + req: &vtctldatapb.GetShardRequest{ + Keyspace: "testkeyspace", + ShardName: "-", + }, + expected: &vtctldatapb.GetShardResponse{ + Shard: &vtctldatapb.Shard{ + Keyspace: "testkeyspace", + Name: "-", + Shard: &topodatapb.Shard{ + KeyRange: &topodatapb.KeyRange{}, + IsMasterServing: true, + }, + }, + }, + shouldErr: false, + }, + { + name: "shard not found", + topo: nil, + topoError: nil, + req: &vtctldatapb.GetShardRequest{ + Keyspace: "testkeyspace", + ShardName: "-", + }, + shouldErr: true, + }, + { + name: "unavailable topo server", + topo: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + }, + topoError: assert.AnError, + req: nil, + shouldErr: true, + }, + } + + for _, tt := range tests { + tt := tt + + cells := []string{"zone1", "zone2", "zone3"} + + ctx := context.Background() + ts, topofactory := memorytopo.NewServerAndFactory(cells...) + vtctld := NewVtctldServer(ts) + + testutil.AddShards(ctx, t, ts, tt.topo...) + + if tt.topoError != nil { + topofactory.SetError(tt.topoError) + } + + resp, err := vtctld.GetShard(ctx, tt.req) + if tt.shouldErr { + assert.Error(t, err) + return + } + + assert.Equal(t, tt.expected, resp) + } +} + +func TestGetSrvVSchema(t *testing.T) { + ctx := context.Background() + ts, topofactory := memorytopo.NewServerAndFactory("zone1", "zone2") + vtctld := NewVtctldServer(ts) + + zone1SrvVSchema := &vschemapb.SrvVSchema{ + Keyspaces: map[string]*vschemapb.Keyspace{ + "testkeyspace": { + Sharded: true, + RequireExplicitRouting: false, + }, + }, + RoutingRules: &vschemapb.RoutingRules{ + Rules: []*vschemapb.RoutingRule{}, + }, + } + zone2SrvVSchema := &vschemapb.SrvVSchema{ + Keyspaces: map[string]*vschemapb.Keyspace{ + "testkeyspace": { + Sharded: true, + RequireExplicitRouting: false, + }, + "unsharded": { + Sharded: false, + RequireExplicitRouting: false, + }, + }, + RoutingRules: &vschemapb.RoutingRules{ + Rules: []*vschemapb.RoutingRule{}, + }, + } + + err := ts.UpdateSrvVSchema(ctx, "zone1", zone1SrvVSchema) + require.NoError(t, err, "cannot add zone1 srv vschema") + err = ts.UpdateSrvVSchema(ctx, "zone2", zone2SrvVSchema) + require.NoError(t, err, "cannot add zone2 srv vschema") + + expected := &vschemapb.SrvVSchema{ // have to copy our structs because of proto marshal artifacts + Keyspaces: map[string]*vschemapb.Keyspace{ + "testkeyspace": { + Sharded: true, + RequireExplicitRouting: false, + }, + }, + RoutingRules: &vschemapb.RoutingRules{ + Rules: []*vschemapb.RoutingRule{}, + }, + } + resp, err := vtctld.GetSrvVSchema(ctx, &vtctldatapb.GetSrvVSchemaRequest{Cell: "zone1"}) + assert.NoError(t, err) + assert.Equal(t, expected.Keyspaces, resp.SrvVSchema.Keyspaces, "GetSrvVSchema(zone1) mismatch") + assert.ElementsMatch(t, expected.RoutingRules.Rules, resp.SrvVSchema.RoutingRules.Rules, "GetSrvVSchema(zone1) rules mismatch") + + expected = &vschemapb.SrvVSchema{ // have to copy our structs because of proto marshal artifacts + Keyspaces: map[string]*vschemapb.Keyspace{ + "testkeyspace": { + Sharded: true, + RequireExplicitRouting: false, + }, + "unsharded": { + Sharded: false, + RequireExplicitRouting: false, + }, + }, + RoutingRules: &vschemapb.RoutingRules{ + Rules: []*vschemapb.RoutingRule{}, + }, + } + resp, err = vtctld.GetSrvVSchema(ctx, &vtctldatapb.GetSrvVSchemaRequest{Cell: "zone2"}) + assert.NoError(t, err) + assert.Equal(t, expected.Keyspaces, resp.SrvVSchema.Keyspaces, "GetSrvVSchema(zone2) mismatch %+v %+v", zone2SrvVSchema.Keyspaces["testkeyspace"], resp.SrvVSchema.Keyspaces["testkeyspace"]) + assert.ElementsMatch(t, expected.RoutingRules.Rules, resp.SrvVSchema.RoutingRules.Rules, "GetSrvVSchema(zone2) rules mismatch") + + resp, err = vtctld.GetSrvVSchema(ctx, &vtctldatapb.GetSrvVSchemaRequest{Cell: "dne"}) + assert.Error(t, err, "GetSrvVSchema(dne)") + assert.Nil(t, resp, "GetSrvVSchema(dne)") + + topofactory.SetError(assert.AnError) + _, err = vtctld.GetSrvVSchema(ctx, &vtctldatapb.GetSrvVSchemaRequest{Cell: "zone1"}) + assert.Error(t, err) +} + +func TestGetTablets(t *testing.T) { + tests := []struct { + name string + cells []string + tablets []*topodatapb.Tablet + req *vtctldatapb.GetTabletsRequest + expected []*topodatapb.Tablet + shouldErr bool + }{ + { + name: "no tablets", + cells: []string{"cell1"}, + tablets: []*topodatapb.Tablet{}, + req: &vtctldatapb.GetTabletsRequest{}, + expected: []*topodatapb.Tablet{}, + shouldErr: false, + }, + { + name: "keyspace and shard filter", + cells: []string{"cell1"}, + tablets: []*topodatapb.Tablet{ + { + Alias: &topodatapb.TabletAlias{ + Cell: "cell1", + Uid: 100, + }, + Keyspace: "ks1", + Shard: "-80", + }, + { + Alias: &topodatapb.TabletAlias{ + Cell: "cell1", + Uid: 101, + }, + Keyspace: "ks1", + Shard: "80-", + }, + { + Alias: &topodatapb.TabletAlias{ + Cell: "cell1", + Uid: 102, + }, + Keyspace: "ks2", + Shard: "-", + }, + }, + req: &vtctldatapb.GetTabletsRequest{ + Keyspace: "ks1", + Shard: "80-", + }, + expected: []*topodatapb.Tablet{ + { + Alias: &topodatapb.TabletAlias{ + Cell: "cell1", + Uid: 101, + }, + Keyspace: "ks1", + Shard: "80-", + }, + }, + shouldErr: false, + }, + { + name: "keyspace filter", + cells: []string{"cell1"}, + tablets: []*topodatapb.Tablet{ + { + Alias: &topodatapb.TabletAlias{ + Cell: "cell1", + Uid: 100, + }, + Keyspace: "ks1", + }, + { + Alias: &topodatapb.TabletAlias{ + Cell: "cell1", + Uid: 101, + }, + Keyspace: "ks1", + }, + { + Alias: &topodatapb.TabletAlias{ + Cell: "cell1", + Uid: 102, + }, + Keyspace: "otherkeyspace", + }, + }, + req: &vtctldatapb.GetTabletsRequest{ + Keyspace: "ks1", + }, + expected: []*topodatapb.Tablet{ + { + Alias: &topodatapb.TabletAlias{ + Cell: "cell1", + Uid: 100, + }, + Keyspace: "ks1", + }, + { + Alias: &topodatapb.TabletAlias{ + Cell: "cell1", + Uid: 101, + }, + Keyspace: "ks1", + }, + }, + shouldErr: false, + }, + { + name: "keyspace and shard filter - stale primary", + cells: []string{"cell1"}, + tablets: []*topodatapb.Tablet{ + { + Alias: &topodatapb.TabletAlias{ + Cell: "cell1", + Uid: 100, + }, + Keyspace: "ks1", + Shard: "-80", + }, + { + Alias: &topodatapb.TabletAlias{ + Cell: "cell1", + Uid: 101, + }, + Keyspace: "ks1", + Shard: "80-", + }, + { + Alias: &topodatapb.TabletAlias{ + Cell: "cell1", + Uid: 102, + }, + Keyspace: "ks2", + Shard: "-", + Type: topodatapb.TabletType_MASTER, + MasterTermStartTime: logutil.TimeToProto(time.Date(2006, time.January, 2, 15, 4, 5, 0, time.UTC)), + }, + { + Alias: &topodatapb.TabletAlias{ + Cell: "cell1", + Uid: 103, + }, + Keyspace: "ks2", + Shard: "-", + Hostname: "stale.primary", + Type: topodatapb.TabletType_MASTER, + MasterTermStartTime: logutil.TimeToProto(time.Date(2006, time.January, 2, 14, 4, 5, 0, time.UTC)), + }, + }, + req: &vtctldatapb.GetTabletsRequest{ + Keyspace: "ks2", + Shard: "-", + }, + expected: []*topodatapb.Tablet{ + { + Alias: &topodatapb.TabletAlias{ + Cell: "cell1", + Uid: 102, + }, + Keyspace: "ks2", + Shard: "-", + Type: topodatapb.TabletType_MASTER, + MasterTermStartTime: logutil.TimeToProto(time.Date(2006, time.January, 2, 15, 4, 5, 0, time.UTC)), + }, + { + Alias: &topodatapb.TabletAlias{ + Cell: "cell1", + Uid: 103, + }, + Keyspace: "ks2", + Shard: "-", + Hostname: "stale.primary", + Type: topodatapb.TabletType_UNKNOWN, + MasterTermStartTime: logutil.TimeToProto(time.Date(2006, time.January, 2, 14, 4, 5, 0, time.UTC)), + }, + }, + shouldErr: false, + }, + { + name: "stale primary", + cells: []string{"cell1"}, + tablets: []*topodatapb.Tablet{ + { + Alias: &topodatapb.TabletAlias{ + Cell: "cell1", + Uid: 100, + }, + Keyspace: "ks1", + Shard: "-", + Hostname: "slightly less stale", + Type: topodatapb.TabletType_MASTER, + MasterTermStartTime: logutil.TimeToProto(time.Date(2006, time.January, 2, 15, 4, 5, 0, time.UTC)), + }, + { + Alias: &topodatapb.TabletAlias{ + Cell: "cell1", + Uid: 101, + }, + Hostname: "stale primary", + Keyspace: "ks1", + Shard: "-", + Type: topodatapb.TabletType_MASTER, + MasterTermStartTime: logutil.TimeToProto(time.Date(2006, time.January, 2, 14, 4, 5, 0, time.UTC)), + }, + { + Alias: &topodatapb.TabletAlias{ + Cell: "cell1", + Uid: 103, + }, + Hostname: "true primary", + Keyspace: "ks1", + Shard: "-", + Type: topodatapb.TabletType_MASTER, + MasterTermStartTime: logutil.TimeToProto(time.Date(2006, time.January, 2, 16, 4, 5, 0, time.UTC)), + }, + }, + req: &vtctldatapb.GetTabletsRequest{}, + expected: []*topodatapb.Tablet{ + { + Alias: &topodatapb.TabletAlias{ + Cell: "cell1", + Uid: 100, + }, + Keyspace: "ks1", + Shard: "-", + Hostname: "slightly less stale", + Type: topodatapb.TabletType_UNKNOWN, + MasterTermStartTime: logutil.TimeToProto(time.Date(2006, time.January, 2, 15, 4, 5, 0, time.UTC)), + }, + { + Alias: &topodatapb.TabletAlias{ + Cell: "cell1", + Uid: 101, + }, + Hostname: "stale primary", + Keyspace: "ks1", + Shard: "-", + Type: topodatapb.TabletType_UNKNOWN, + MasterTermStartTime: logutil.TimeToProto(time.Date(2006, time.January, 2, 14, 4, 5, 0, time.UTC)), + }, + { + Alias: &topodatapb.TabletAlias{ + Cell: "cell1", + Uid: 103, + }, + Hostname: "true primary", + Keyspace: "ks1", + Shard: "-", + Type: topodatapb.TabletType_MASTER, + MasterTermStartTime: logutil.TimeToProto(time.Date(2006, time.January, 2, 16, 4, 5, 0, time.UTC)), + }, + }, + shouldErr: false, + }, + { + name: "keyspace and shard filter - error", + cells: []string{"cell1"}, + tablets: []*topodatapb.Tablet{}, + req: &vtctldatapb.GetTabletsRequest{ + Keyspace: "ks1", + Shard: "-", + }, + expected: []*topodatapb.Tablet{}, + shouldErr: true, + }, + { + name: "cells filter", + cells: []string{"cell1", "cell2", "cell3"}, + tablets: []*topodatapb.Tablet{ + { + Alias: &topodatapb.TabletAlias{ + Cell: "cell1", + Uid: 100, + }, + }, + { + Alias: &topodatapb.TabletAlias{ + Cell: "cell2", + Uid: 200, + }, + }, + { + Alias: &topodatapb.TabletAlias{ + Cell: "cell3", + Uid: 300, + }, + }, + }, + req: &vtctldatapb.GetTabletsRequest{ + Cells: []string{"cell1", "cell3"}, + }, + expected: []*topodatapb.Tablet{ + { + Alias: &topodatapb.TabletAlias{ + Cell: "cell1", + Uid: 100, + }, + }, + { + Alias: &topodatapb.TabletAlias{ + Cell: "cell3", + Uid: 300, + }, + }, + }, + shouldErr: false, + }, + { + name: "cells filter - error", + cells: []string{"cell1"}, + tablets: []*topodatapb.Tablet{ + { + Alias: &topodatapb.TabletAlias{ + Cell: "cell1", + Uid: 100, + }, + Keyspace: "ks1", + Shard: "-", + }, + }, + req: &vtctldatapb.GetTabletsRequest{ + Cells: []string{"cell1", "doesnotexist"}, + }, + expected: []*topodatapb.Tablet{}, + shouldErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctx := context.Background() + ts := memorytopo.NewServer(tt.cells...) + vtctld := NewVtctldServer(ts) + + for _, tablet := range tt.tablets { + testutil.AddTablet(ctx, t, ts, tablet) + } + + resp, err := vtctld.GetTablets(ctx, tt.req) + if tt.shouldErr { + assert.Error(t, err) + return + } + + assert.NoError(t, err) + assert.ElementsMatch(t, tt.expected, resp.Tablets) + }) + } +} + +func TestGetVSchema(t *testing.T) { + ctx := context.Background() + ts := memorytopo.NewServer("zone1") + vtctld := NewVtctldServer(ts) + + err := ts.SaveVSchema(ctx, "testkeyspace", &vschemapb.Keyspace{ + Sharded: true, + Vindexes: map[string]*vschemapb.Vindex{ + "v1": { + Type: "hash", + }, + }, + }) + require.NoError(t, err) + + expected := &vtctldatapb.GetVSchemaResponse{ + VSchema: &vschemapb.Keyspace{ + Sharded: true, + Vindexes: map[string]*vschemapb.Vindex{ + "v1": { + Type: "hash", + }, + }, + }, + } + + resp, err := vtctld.GetVSchema(ctx, &vtctldatapb.GetVSchemaRequest{ + Keyspace: "testkeyspace", + }) + assert.NoError(t, err) + assert.Equal(t, expected, resp) + + t.Run("not found", func(t *testing.T) { + _, err := vtctld.GetVSchema(ctx, &vtctldatapb.GetVSchemaRequest{ + Keyspace: "doesnotexist", + }) + assert.Error(t, err) + }) +} + +func TestRemoveKeyspaceCell(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + keyspace *vtctldatapb.Keyspace + shards []*vtctldatapb.Shard + topoError error + topoIsLocked bool + srvKeyspaceDoesNotExist bool + req *vtctldatapb.RemoveKeyspaceCellRequest + expected *vtctldatapb.RemoveKeyspaceCellResponse + shouldErr bool + }{ + { + name: "success", + keyspace: nil, + shards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + }, + topoError: nil, + topoIsLocked: false, + srvKeyspaceDoesNotExist: false, + req: &vtctldatapb.RemoveKeyspaceCellRequest{ + Keyspace: "testkeyspace", + Cell: "zone1", + }, + expected: &vtctldatapb.RemoveKeyspaceCellResponse{}, + shouldErr: false, + }, + { + name: "success/empty keyspace", + keyspace: &vtctldatapb.Keyspace{ + Name: "testkeyspace", + Keyspace: &topodatapb.Keyspace{}, + }, + shards: nil, + topoError: nil, + topoIsLocked: false, + srvKeyspaceDoesNotExist: false, + req: &vtctldatapb.RemoveKeyspaceCellRequest{ + Keyspace: "testkeyspace", + Cell: "zone1", + }, + expected: &vtctldatapb.RemoveKeyspaceCellResponse{}, + shouldErr: false, + }, + { + name: "keyspace not found", + keyspace: &vtctldatapb.Keyspace{ + Name: "otherkeyspace", + Keyspace: &topodatapb.Keyspace{}, + }, + shards: nil, + topoError: nil, + topoIsLocked: false, + srvKeyspaceDoesNotExist: false, + req: &vtctldatapb.RemoveKeyspaceCellRequest{ + Keyspace: "testkeyspace", + Cell: "zone1", + }, + expected: nil, + shouldErr: true, + }, + { + name: "topo is down", + keyspace: nil, + shards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + }, + topoError: assert.AnError, + topoIsLocked: false, + srvKeyspaceDoesNotExist: false, + req: &vtctldatapb.RemoveKeyspaceCellRequest{ + Keyspace: "testkeyspace", + Cell: "zone1", + }, + expected: nil, + shouldErr: true, + }, + { + name: "topo is locked", + keyspace: nil, + shards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + }, + topoError: nil, + topoIsLocked: true, + srvKeyspaceDoesNotExist: false, + req: &vtctldatapb.RemoveKeyspaceCellRequest{ + Keyspace: "testkeyspace", + Cell: "zone1", + }, + expected: nil, + shouldErr: true, + }, + { + name: "srvkeyspace already deleted", + keyspace: nil, + shards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + }, + topoError: nil, + topoIsLocked: false, + srvKeyspaceDoesNotExist: true, + req: &vtctldatapb.RemoveKeyspaceCellRequest{ + Keyspace: "testkeyspace", + Cell: "zone1", + }, + expected: nil, + shouldErr: true, + }, + } + + for _, tt := range tests { + tt := tt + + t.Run(tt.name, func(t *testing.T) { + cells := []string{"zone1", "zone2", "zone3"} + + ctx := context.Background() + ts, topofactory := memorytopo.NewServerAndFactory(cells...) + vtctld := NewVtctldServer(ts) + + // Setup topo + if tt.keyspace != nil { + testutil.AddKeyspace(ctx, t, ts, tt.keyspace) + } + + testutil.AddShards(ctx, t, ts, tt.shards...) + + // For certain tests, we don't actually create the SrvKeyspace + // object. + if !tt.srvKeyspaceDoesNotExist { + updateSrvKeyspace := func(keyspace string) { + for _, cell := range cells { + err := ts.UpdateSrvKeyspace(ctx, cell, keyspace, &topodatapb.SrvKeyspace{}) + require.NoError(t, err, "could not create empty SrvKeyspace for keyspace %s in cell %s", tt.req.Keyspace, cell) + } + } + + updateSrvKeyspace(tt.req.Keyspace) + if tt.keyspace != nil { + updateSrvKeyspace(tt.keyspace.Name) + } + } + + // Set errors and locks + if tt.topoError != nil { + topofactory.SetError(tt.topoError) + } + + if tt.topoIsLocked { + lctx, unlock, err := ts.LockKeyspace(ctx, tt.req.Keyspace, "testing locked keyspace") + require.NoError(t, err, "cannot lock keyspace %s", tt.req.Keyspace) + defer unlock(&err) + + ctx = lctx + } + + resp, err := vtctld.RemoveKeyspaceCell(ctx, tt.req) + if tt.shouldErr { + assert.Error(t, err) + return + } + + assert.NoError(t, err) + assert.Equal(t, tt.expected, resp) + }) + } +} + +func TestRemoveShardCell(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + servingCells []string + shards []*vtctldatapb.Shard + replicationGraphs []*topo.ShardReplicationInfo + topoError error + topoIsLocked bool + req *vtctldatapb.RemoveShardCellRequest + expected *vtctldatapb.RemoveShardCellResponse + shouldErr bool + }{ + { + name: "success", + shards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + }, + replicationGraphs: []*topo.ShardReplicationInfo{ + topo.NewShardReplicationInfo(&topodatapb.ShardReplication{ + Nodes: []*topodatapb.ShardReplication_Node{ + { + TabletAlias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + }, + }, + }, "zone1", "testkeyspace", "-"), + topo.NewShardReplicationInfo(&topodatapb.ShardReplication{ + Nodes: []*topodatapb.ShardReplication_Node{ + { + TabletAlias: &topodatapb.TabletAlias{ + Cell: "zone2", + Uid: 200, + }, + }, + }, + }, "zone2", "testkeyspace", "-"), + topo.NewShardReplicationInfo(&topodatapb.ShardReplication{ + Nodes: []*topodatapb.ShardReplication_Node{ + { + TabletAlias: &topodatapb.TabletAlias{ + Cell: "zone3", + Uid: 300, + }, + }, + }, + }, "zone3", "testkeyspace", "-"), + }, + req: &vtctldatapb.RemoveShardCellRequest{ + Keyspace: "testkeyspace", + ShardName: "-", + Cell: "zone2", + Recursive: true, + }, + expected: &vtctldatapb.RemoveShardCellResponse{}, + shouldErr: false, + }, + { + name: "success/no tablets", + shards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + }, + req: &vtctldatapb.RemoveShardCellRequest{ + Keyspace: "testkeyspace", + ShardName: "-", + Cell: "zone2", + }, + expected: &vtctldatapb.RemoveShardCellResponse{}, + shouldErr: false, + }, + { + name: "nonexistent shard", + shards: nil, + replicationGraphs: nil, + req: &vtctldatapb.RemoveShardCellRequest{ + Keyspace: "testkeyspace", + ShardName: "-", + Cell: "zone2", + }, + expected: nil, + shouldErr: true, + }, + { + name: "cell does not exist", + shards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + }, + req: &vtctldatapb.RemoveShardCellRequest{ + Keyspace: "testkeyspace", + ShardName: "-", + Cell: "fourthzone", + }, + expected: nil, + shouldErr: true, + }, + { + name: "cell not in serving list", + shards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + }, + servingCells: []string{"zone1"}, + replicationGraphs: nil, + req: &vtctldatapb.RemoveShardCellRequest{ + Keyspace: "testkeyspace", + ShardName: "-", + Cell: "zone2", + }, + expected: nil, + shouldErr: true, + }, + { + name: "tablets/non-recursive", + shards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + }, + replicationGraphs: []*topo.ShardReplicationInfo{ + topo.NewShardReplicationInfo(&topodatapb.ShardReplication{ + Nodes: []*topodatapb.ShardReplication_Node{ + { + TabletAlias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + }, + }, + }, "zone1", "testkeyspace", "-"), + topo.NewShardReplicationInfo(&topodatapb.ShardReplication{ + Nodes: []*topodatapb.ShardReplication_Node{ + { + TabletAlias: &topodatapb.TabletAlias{ + Cell: "zone2", + Uid: 200, + }, + }, + }, + }, "zone2", "testkeyspace", "-"), + topo.NewShardReplicationInfo(&topodatapb.ShardReplication{ + Nodes: []*topodatapb.ShardReplication_Node{ + { + TabletAlias: &topodatapb.TabletAlias{ + Cell: "zone3", + Uid: 300, + }, + }, + }, + }, "zone3", "testkeyspace", "-"), + }, + req: &vtctldatapb.RemoveShardCellRequest{ + Keyspace: "testkeyspace", + ShardName: "-", + Cell: "zone2", + Recursive: false, // non-recursive + replication graph = failure + }, + expected: nil, + shouldErr: true, + }, + { + name: "topo server down", + shards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + }, + replicationGraphs: nil, + req: &vtctldatapb.RemoveShardCellRequest{ + Keyspace: "testkeyspace", + ShardName: "-", + Cell: "zone2", + }, + topoError: assert.AnError, + topoIsLocked: false, + expected: nil, + shouldErr: true, + }, + // Not sure how to set up this test case. + // { + // name: "topo server down for replication check/no force", + // }, + // Not sure how to set up this test case. + // { + // name: "topo server down for replication check/force", + // }, + { + name: "cannot lock keyspace", + shards: []*vtctldatapb.Shard{ + { + Keyspace: "testkeyspace", + Name: "-", + }, + }, + replicationGraphs: nil, + req: &vtctldatapb.RemoveShardCellRequest{ + Keyspace: "testkeyspace", + ShardName: "-", + Cell: "zone2", + }, + topoError: nil, + topoIsLocked: true, + expected: nil, + shouldErr: true, + }, + // Not sure how to set up this test case. + // { + // name: "cannot delete srvkeyspace partition", + // }, + } + + for _, tt := range tests { + tt := tt + + t.Run(tt.name, func(t *testing.T) { + cells := []string{"zone1", "zone2", "zone3"} + + ctx := context.Background() + ts, topofactory := memorytopo.NewServerAndFactory(cells...) + vtctld := NewVtctldServer(ts) + + // Setup shard topos and replication graphs. + testutil.AddShards(ctx, t, ts, tt.shards...) + testutil.SetupReplicationGraphs(ctx, t, ts, tt.replicationGraphs...) + + // Set up srvkeyspace partitions; a little gross. + servingCells := tt.servingCells + if servingCells == nil { // we expect an explicit empty list to have a shard with no serving cells + servingCells = cells + } + + for _, shard := range tt.shards { + lctx, unlock, lerr := ts.LockKeyspace(ctx, shard.Keyspace, "initializing serving graph for test") + require.NoError(t, lerr, "cannot lock keyspace %s to initialize serving graph", shard.Keyspace) + + for _, cell := range servingCells { + + err := ts.UpdateSrvKeyspace(lctx, cell, shard.Keyspace, &topodatapb.SrvKeyspace{ + Partitions: []*topodatapb.SrvKeyspace_KeyspacePartition{ + { + ServedType: topodatapb.TabletType_REPLICA, + ShardReferences: []*topodatapb.ShardReference{ + { + Name: shard.Name, + }, + }, + }, + }, + }) + require.NoError(t, err, "cannot update srvkeyspace for %s/%s in cell %v", shard.Keyspace, shard.Name, cell) + } + + unlock(&lerr) + } + + // Set errors and locks. + if tt.topoError != nil { + topofactory.SetError(tt.topoError) + } + + if tt.topoIsLocked { + lctx, unlock, err := ts.LockKeyspace(ctx, tt.req.Keyspace, "testing locked keyspace") + require.NoError(t, err, "cannot lock keyspace %s", tt.req.Keyspace) + defer unlock(&err) + + // Need to use the lock ctx in the RPC call so we fail when + // attempting to lock the keyspace rather than waiting forever + // for the lock. Explicitly setting a deadline would be another + // way to achieve this. + ctx = lctx + } + + // Make the RPC and assert things about it. + resp, err := vtctld.RemoveShardCell(ctx, tt.req) + if tt.shouldErr { + assert.Error(t, err) + return + } + + assert.NoError(t, err) + assert.Equal(t, tt.expected, resp) + }) + } +} diff --git a/go/vt/vtctl/grpcvtctldserver/testutil/proto_compare.go b/go/vt/vtctl/grpcvtctldserver/testutil/proto_compare.go new file mode 100644 index 00000000000..dd06ec80d84 --- /dev/null +++ b/go/vt/vtctl/grpcvtctldserver/testutil/proto_compare.go @@ -0,0 +1,46 @@ +/* +Copyright 2021 The Vitess Authors. + +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 testutil + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" +) + +// AssertKeyspacesEqual is a convenience function to assert that two +// vtctldatapb.Keyspace objects are equal, after clearing out any reserved +// proto XXX_ fields. +func AssertKeyspacesEqual(t *testing.T, expected *vtctldatapb.Keyspace, actual *vtctldatapb.Keyspace, msgAndArgs ...interface{}) { + t.Helper() + + for _, ks := range []*vtctldatapb.Keyspace{expected, actual} { + if ks.Keyspace != nil { + ks.Keyspace.XXX_sizecache = 0 + ks.Keyspace.XXX_unrecognized = nil + } + + if ks.Keyspace.SnapshotTime != nil { + ks.Keyspace.SnapshotTime.XXX_sizecache = 0 + ks.Keyspace.SnapshotTime.XXX_unrecognized = nil + } + } + + assert.Equal(t, expected, actual, msgAndArgs...) +} diff --git a/go/vt/vtctl/grpcvtctldserver/testutil/test_backupstorage.go b/go/vt/vtctl/grpcvtctldserver/testutil/test_backupstorage.go new file mode 100644 index 00000000000..a871cbfdbf7 --- /dev/null +++ b/go/vt/vtctl/grpcvtctldserver/testutil/test_backupstorage.go @@ -0,0 +1,92 @@ +/* +Copyright 2021 The Vitess Authors. + +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 testutil + +import ( + "context" + "sort" + + "vitess.io/vitess/go/vt/mysqlctl/backupstorage" +) + +type backupStorage struct { + backupstorage.BackupStorage + + // Backups is a mapping of directory to list of backup names stored in that + // directory. + Backups map[string][]string + // ListBackupsError is returned from ListBackups when it is non-nil. + ListBackupsError error +} + +// ListBackups is part of the backupstorage.BackupStorage interface. +func (bs *backupStorage) ListBackups(ctx context.Context, dir string) ([]backupstorage.BackupHandle, error) { + if bs.ListBackupsError != nil { + return nil, bs.ListBackupsError + } + + handles := []backupstorage.BackupHandle{} + + for k, v := range bs.Backups { + if k == dir { + for _, name := range v { + handles = append(handles, &backupHandle{directory: k, name: name}) + } + } + } + + sort.Sort(handlesByName(handles)) + + return handles, nil +} + +// Close is part of the backupstorage.BackupStorage interface. +func (bs *backupStorage) Close() error { return nil } + +// backupHandle implements a subset of the backupstorage.backupHandle interface. +type backupHandle struct { + backupstorage.BackupHandle + + directory string + name string +} + +func (bh *backupHandle) Directory() string { return bh.directory } +func (bh *backupHandle) Name() string { return bh.name } + +// handlesByName implements the sort interface for backup handles by Name(). +type handlesByName []backupstorage.BackupHandle + +func (a handlesByName) Len() int { return len(a) } +func (a handlesByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a handlesByName) Less(i, j int) bool { return a[i].Name() < a[j].Name() } + +// BackupStorageImplementation is the name this package registers its test +// backupstorage.BackupStorage implementation as. Users should set +// *backupstorage.BackupStorageImplementation to this value before use. +const BackupStorageImplementation = "grpcvtctldserver.testutil" + +// BackupStorage is the singleton test backupstorage.BackupStorage intastnce. It +// is public and singleton to allow tests to both mutate and assert against its +// state. +var BackupStorage = &backupStorage{ + Backups: map[string][]string{}, +} + +func init() { + backupstorage.BackupStorageMap[BackupStorageImplementation] = BackupStorage +} diff --git a/go/vt/vtctl/grpcvtctldserver/testutil/test_tmclient.go b/go/vt/vtctl/grpcvtctldserver/testutil/test_tmclient.go new file mode 100644 index 00000000000..20aa3959e75 --- /dev/null +++ b/go/vt/vtctl/grpcvtctldserver/testutil/test_tmclient.go @@ -0,0 +1,585 @@ +/* +Copyright 2021 The Vitess Authors. + +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 testutil + +import ( + "context" + "fmt" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/topo" + "vitess.io/vitess/go/vt/topo/topoproto" + "vitess.io/vitess/go/vt/topotools" + "vitess.io/vitess/go/vt/vttablet/tmclient" + + querypb "vitess.io/vitess/go/vt/proto/query" + replicationdatapb "vitess.io/vitess/go/vt/proto/replicationdata" + tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" + topodatapb "vitess.io/vitess/go/vt/proto/topodata" + vtctlservicepb "vitess.io/vitess/go/vt/proto/vtctlservice" + "vitess.io/vitess/go/vt/proto/vttime" +) + +var ( + tmclientLock sync.Mutex + tmclientFactoryLock sync.Mutex + tmclients = map[string]tmclient.TabletManagerClient{} + tmclientFactories = map[string]func() tmclient.TabletManagerClient{} +) + +// NewVtctldServerWithTabletManagerClient returns a new +// grpcvtctldserver.VtctldServer configured with the given topo server and +// tmclient.TabletManagerClient implementation for testing. +// +// It synchronizes on private locks to prevent multiple goroutines from stepping +// on each other during VtctldServer initialization, but still run the rest of +// the test in parallel. +// +// NOTE, THE FIRST: It is only safe to use in parallel with other tests using +// this method of creating a VtctldServer, or with tests that do not depend on a +// VtctldServer's tmclient.TabletManagerClient implementation. +// +// NOTE, THE SECOND: It needs to register a unique name to the tmclient factory +// registry, so we keep a shadow map of factories registered for "protocols" by +// this function. That way, if we happen to have multiple tests with the same +// name, we can swap out the return value for the factory and allow both tests +// to run, rather than the second test failing when it attempts to register a +// second factory for the same "protocol" name. +// +// NOTE, THE THIRD: we take a "new" func to produce a valid +// vtctlservicepb.VtctldServer implementation, rather than constructing directly +// ourselves with grpcvtctldserver.NewVtctldServer. This is to prevent an import +// cycle between this package and package grpcvtctldserver. Further, because the +// return type of NewVtctldServer is the struct type +// (*grpcvtctldserver.VtctldServer) and not the interface type +// vtctlservicepb.VtctldServer, tests will need to indirect that call through an +// extra layer rather than passing the function identifier directly, e.g.: +// +// vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &testutil.TabletManagerClient{ +// ... +// }, func(ts *topo.Server) vtctlservicepb.VtctldServer { return NewVtctldServer(ts) }) +// +func NewVtctldServerWithTabletManagerClient(t *testing.T, ts *topo.Server, tmc tmclient.TabletManagerClient, newVtctldServerFn func(ts *topo.Server) vtctlservicepb.VtctldServer) vtctlservicepb.VtctldServer { + tmclientFactoryLock.Lock() + defer tmclientFactoryLock.Unlock() + + protocol := t.Name() + + if _, alreadyRegisteredFactory := tmclientFactories[protocol]; !alreadyRegisteredFactory { + factory := func() tmclient.TabletManagerClient { + tmclientLock.Lock() + defer tmclientLock.Unlock() + + client, ok := tmclients[protocol] + if !ok { + t.Fatal("Test managed to register a factory for a client value that never got set; this should be impossible") + } + + return client + } + + tmclient.RegisterTabletManagerClientFactory(protocol, factory) + tmclientFactories[protocol] = factory + } + + // Always swap in the new client return value for the given protocol name. + // We cannot defer the unlock here, because grpcvtctldserver.NewVtctldServer + // eventually will call into the factory we registered above, and we will + // deadlock ourselves. + tmclientLock.Lock() + tmclients[protocol] = tmc + tmclientLock.Unlock() + + // Be (mostly, we can't help concurrent goroutines not using this function) + // atomic with our mutation of the global TabletManagerProtocol pointer. + oldProto := *tmclient.TabletManagerProtocol + defer func() { *tmclient.TabletManagerProtocol = oldProto }() + + *tmclient.TabletManagerProtocol = protocol + + return newVtctldServerFn(ts) +} + +// TabletManagerClient implements the tmclient.TabletManagerClient interface +// with mock delays and response values, for use in unit tests. +type TabletManagerClient struct { + tmclient.TabletManagerClient + // TopoServer is used for certain TabletManagerClient rpcs that update topo + // information, e.g. ChangeType. To force an error result for those rpcs in + // a test, set tmc.TopoServer = nil. + TopoServer *topo.Server + // keyed by tablet alias. + DemoteMasterDelays map[string]time.Duration + // keyed by tablet alias. + DemoteMasterResults map[string]struct { + Status *replicationdatapb.MasterStatus + Error error + } + // keyed by tablet alias. + GetSchemaDelays map[string]time.Duration + // keyed by tablet alias. + GetSchemaResults map[string]struct { + Schema *tabletmanagerdatapb.SchemaDefinition + Error error + } + // keyed by tablet alias. + MasterPositionDelays map[string]time.Duration + // keyed by tablet alias. + MasterPositionResults map[string]struct { + Position string + Error error + } + // keyed by tablet alias. + PopulateReparentJournalDelays map[string]time.Duration + // keyed by tablet alias + PopulateReparentJournalResults map[string]error + // keyed by tablet alias. + PromoteReplicaDelays map[string]time.Duration + // keyed by tablet alias. injects a sleep to the end of the function + // regardless of parent context timeout or error result. + PromoteReplicaPostDelays map[string]time.Duration + // keyed by tablet alias. + PromoteReplicaResults map[string]struct { + Result string + Error error + } + ReplicationStatusResults map[string]struct { + Position *replicationdatapb.Status + Error error + } + // keyed by tablet alias. + SetMasterDelays map[string]time.Duration + // keyed by tablet alias. + SetMasterResults map[string]error + // keyed by tablet alias. + SetReadWriteDelays map[string]time.Duration + // keyed by tablet alias. + SetReadWriteResults map[string]error + // keyed by tablet alias. + StopReplicationAndGetStatusDelays map[string]time.Duration + // keyed by tablet alias. + StopReplicationAndGetStatusResults map[string]struct { + Status *replicationdatapb.Status + StopStatus *replicationdatapb.StopReplicationStatus + Error error + } + // keyed by tablet alias. + UndoDemoteMasterDelays map[string]time.Duration + // keyed by tablet alias + UndoDemoteMasterResults map[string]error + // tablet alias => duration + VReplicationExecDelays map[string]time.Duration + // tablet alias => query string => result + VReplicationExecResults map[string]map[string]struct { + Result *querypb.QueryResult + Error error + } + // keyed by tablet alias. + WaitForPositionDelays map[string]time.Duration + // keyed by tablet alias. injects a sleep to the end of the function + // regardless of parent context timeout or error result. + WaitForPositionPostDelays map[string]time.Duration + // WaitForPosition(tablet *topodatapb.Tablet, position string) error, so we + // key by tablet alias and then by position. + WaitForPositionResults map[string]map[string]error +} + +// ChangeType is part of the tmclient.TabletManagerClient interface. +func (fake *TabletManagerClient) ChangeType(ctx context.Context, tablet *topodatapb.Tablet, newType topodatapb.TabletType) error { + if fake.TopoServer == nil { + return assert.AnError + } + + _, err := topotools.ChangeType(ctx, fake.TopoServer, tablet.Alias, newType, &vttime.Time{}) + return err +} + +// DemoteMaster is part of the tmclient.TabletManagerClient interface. +func (fake *TabletManagerClient) DemoteMaster(ctx context.Context, tablet *topodatapb.Tablet) (*replicationdatapb.MasterStatus, error) { + if fake.DemoteMasterResults == nil { + return nil, assert.AnError + } + + if tablet.Alias == nil { + return nil, assert.AnError + } + + key := topoproto.TabletAliasString(tablet.Alias) + + if fake.DemoteMasterDelays != nil { + if delay, ok := fake.DemoteMasterDelays[key]; ok { + select { + case <-ctx.Done(): + return nil, ctx.Err() + case <-time.After(delay): + // proceed to results + } + } + } + + if result, ok := fake.DemoteMasterResults[key]; ok { + return result.Status, result.Error + } + + return nil, assert.AnError +} + +// GetSchema is part of the tmclient.TabletManagerClient interface. +func (fake *TabletManagerClient) GetSchema(ctx context.Context, tablet *topodatapb.Tablet, tablets []string, excludeTables []string, includeViews bool) (*tabletmanagerdatapb.SchemaDefinition, error) { + if fake.GetSchemaResults == nil { + return nil, assert.AnError + } + + if tablet.Alias == nil { + return nil, assert.AnError + } + + key := topoproto.TabletAliasString(tablet.Alias) + + if fake.GetSchemaDelays != nil { + if delay, ok := fake.GetSchemaDelays[key]; ok { + select { + case <-ctx.Done(): + return nil, ctx.Err() + case <-time.After(delay): + // proceed to results + } + } + } + + if result, ok := fake.GetSchemaResults[key]; ok { + return result.Schema, result.Error + } + + return nil, fmt.Errorf("%w: no schemas for %s", assert.AnError, key) +} + +// MasterPosition is part of the tmclient.TabletManagerClient interface. +func (fake *TabletManagerClient) MasterPosition(ctx context.Context, tablet *topodatapb.Tablet) (string, error) { + if fake.MasterPositionResults == nil { + return "", assert.AnError + } + + if tablet.Alias == nil { + return "", assert.AnError + } + + key := topoproto.TabletAliasString(tablet.Alias) + + if fake.MasterPositionDelays != nil { + if delay, ok := fake.MasterPositionDelays[key]; ok { + select { + case <-ctx.Done(): + return "", ctx.Err() + case <-time.After(delay): + // proceed to results + } + } + } + + if result, ok := fake.MasterPositionResults[key]; ok { + return result.Position, result.Error + } + + return "", assert.AnError +} + +// PopulateReparentJournal is part of the tmclient.TabletManagerClient +// interface. +func (fake *TabletManagerClient) PopulateReparentJournal(ctx context.Context, tablet *topodatapb.Tablet, timeCreatedNS int64, actionName string, primaryAlias *topodatapb.TabletAlias, pos string) error { + if fake.PopulateReparentJournalResults == nil { + return assert.AnError + } + + key := topoproto.TabletAliasString(tablet.Alias) + + if fake.PopulateReparentJournalDelays != nil { + if delay, ok := fake.PopulateReparentJournalDelays[key]; ok { + select { + case <-ctx.Done(): + return ctx.Err() + case <-time.After(delay): + // proceed to results + } + } + } + if result, ok := fake.PopulateReparentJournalResults[key]; ok { + return result + } + + return assert.AnError +} + +// PromoteReplica is part of the tmclient.TabletManagerClient interface. +func (fake *TabletManagerClient) PromoteReplica(ctx context.Context, tablet *topodatapb.Tablet) (string, error) { + if fake.PromoteReplicaResults == nil { + return "", assert.AnError + } + + key := topoproto.TabletAliasString(tablet.Alias) + + defer func() { + if fake.PromoteReplicaPostDelays == nil { + return + } + + if delay, ok := fake.PromoteReplicaPostDelays[key]; ok { + time.Sleep(delay) + } + }() + + if fake.PromoteReplicaDelays != nil { + if delay, ok := fake.PromoteReplicaDelays[key]; ok { + select { + case <-ctx.Done(): + return "", ctx.Err() + case <-time.After(delay): + // proceed to results + } + } + } + + if result, ok := fake.PromoteReplicaResults[key]; ok { + return result.Result, result.Error + } + + return "", assert.AnError +} + +// ReplicationStatus is part of the tmclient.TabletManagerClient interface. +func (fake *TabletManagerClient) ReplicationStatus(ctx context.Context, tablet *topodatapb.Tablet) (*replicationdatapb.Status, error) { + if fake.ReplicationStatusResults == nil { + return nil, assert.AnError + } + + key := topoproto.TabletAliasString(tablet.Alias) + + if result, ok := fake.ReplicationStatusResults[key]; ok { + return result.Position, result.Error + } + + return nil, assert.AnError +} + +// SetMaster is part of the tmclient.TabletManagerClient interface. +func (fake *TabletManagerClient) SetMaster(ctx context.Context, tablet *topodatapb.Tablet, parent *topodatapb.TabletAlias, timeCreatedNS int64, waitPosition string, forceStartReplication bool) error { + if fake.SetMasterResults == nil { + return assert.AnError + } + + key := topoproto.TabletAliasString(tablet.Alias) + + if fake.SetMasterDelays != nil { + if delay, ok := fake.SetMasterDelays[key]; ok { + select { + case <-ctx.Done(): + return ctx.Err() + case <-time.After(delay): + // proceed to results + } + } + } + + if result, ok := fake.SetMasterResults[key]; ok { + return result + } + + return assert.AnError +} + +// SetReadWrite is part of the tmclient.TabletManagerClient interface. +func (fake *TabletManagerClient) SetReadWrite(ctx context.Context, tablet *topodatapb.Tablet) error { + if fake.SetReadWriteResults == nil { + return assert.AnError + } + + if tablet.Alias == nil { + return assert.AnError + } + + key := topoproto.TabletAliasString(tablet.Alias) + + if fake.SetReadWriteDelays != nil { + if delay, ok := fake.SetReadWriteDelays[key]; ok { + select { + case <-ctx.Done(): + return ctx.Err() + case <-time.After(delay): + // proceed to results + } + } + } + + if err, ok := fake.SetReadWriteResults[key]; ok { + return err + } + + return assert.AnError +} + +// StopReplicationAndGetStatus is part of the tmclient.TabletManagerClient +// interface. +func (fake *TabletManagerClient) StopReplicationAndGetStatus(ctx context.Context, tablet *topodatapb.Tablet, mode replicationdatapb.StopReplicationMode) (*replicationdatapb.Status, *replicationdatapb.StopReplicationStatus, error) { + if fake.StopReplicationAndGetStatusResults == nil { + return nil, nil, assert.AnError + } + + if tablet.Alias == nil { + return nil, nil, assert.AnError + } + + key := topoproto.TabletAliasString(tablet.Alias) + + if fake.StopReplicationAndGetStatusDelays != nil { + if delay, ok := fake.StopReplicationAndGetStatusDelays[key]; ok { + select { + case <-ctx.Done(): + return nil, nil, ctx.Err() + case <-time.After(delay): + // proceed to results + } + } + } + + if result, ok := fake.StopReplicationAndGetStatusResults[key]; ok { + return result.Status, result.StopStatus, result.Error + } + + return nil, nil, assert.AnError +} + +// WaitForPosition is part of the tmclient.TabletManagerClient interface. +func (fake *TabletManagerClient) WaitForPosition(ctx context.Context, tablet *topodatapb.Tablet, position string) error { + tabletKey := topoproto.TabletAliasString(tablet.Alias) + + defer func() { + if fake.WaitForPositionPostDelays == nil { + return + } + + if delay, ok := fake.WaitForPositionPostDelays[tabletKey]; ok { + time.Sleep(delay) + } + }() + + if fake.WaitForPositionDelays != nil { + if delay, ok := fake.WaitForPositionDelays[tabletKey]; ok { + select { + case <-ctx.Done(): + return ctx.Err() + case <-time.After(delay): + // proceed to results + } + } + } + + if fake.WaitForPositionResults == nil { + return assert.AnError + } + + tabletResultsByPosition, ok := fake.WaitForPositionResults[tabletKey] + if !ok { + return assert.AnError + } + + result, ok := tabletResultsByPosition[position] + if !ok { + return assert.AnError + } + + return result +} + +// UndoDemoteMaster is part of the tmclient.TabletManagerClient interface. +func (fake *TabletManagerClient) UndoDemoteMaster(ctx context.Context, tablet *topodatapb.Tablet) error { + if fake.UndoDemoteMasterResults == nil { + return assert.AnError + } + + if tablet.Alias == nil { + return assert.AnError + } + + key := topoproto.TabletAliasString(tablet.Alias) + + if fake.UndoDemoteMasterDelays != nil { + if delay, ok := fake.UndoDemoteMasterDelays[key]; ok { + select { + case <-ctx.Done(): + return ctx.Err() + case <-time.After(delay): + // proceed to results + } + } + } + + if result, ok := fake.UndoDemoteMasterResults[key]; ok { + return result + } + + return assert.AnError +} + +// VReplicationExec is part of the tmclient.TabletManagerCLient interface. +func (fake *TabletManagerClient) VReplicationExec(ctx context.Context, tablet *topodatapb.Tablet, query string) (*querypb.QueryResult, error) { + if fake.VReplicationExecResults == nil { + return nil, assert.AnError + } + + if tablet.Alias == nil { + return nil, assert.AnError + } + + key := topoproto.TabletAliasString(tablet.Alias) + + if fake.VReplicationExecDelays != nil { + if delay, ok := fake.VReplicationExecDelays[key]; ok { + select { + case <-ctx.Done(): + return nil, ctx.Err() + case <-time.After(delay): + // proceed to results + } + } + } + + if resultsForTablet, ok := fake.VReplicationExecResults[key]; ok { + // Round trip the expected query both to ensure it's valid and to + // standardize on capitalization and formatting. + stmt, err := sqlparser.Parse(query) + if err != nil { + return nil, err + } + + buf := sqlparser.NewTrackedBuffer(nil) + buf.Myprintf("%v", stmt) + + parsedQuery := buf.ParsedQuery().Query + + // Now do the map lookup. + if result, ok := resultsForTablet[parsedQuery]; ok { + return result.Result, result.Error + } + } + + return nil, assert.AnError +} diff --git a/go/vt/vtctl/grpcvtctldserver/testutil/util.go b/go/vt/vtctl/grpcvtctldserver/testutil/util.go new file mode 100644 index 00000000000..c6298c86556 --- /dev/null +++ b/go/vt/vtctl/grpcvtctldserver/testutil/util.go @@ -0,0 +1,157 @@ +/* +Copyright 2021 The Vitess Authors. + +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 testutil contains utility functions for writing tests for the +// grpcvtctldserver. +package testutil + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + "golang.org/x/net/nettest" + "google.golang.org/grpc" + + "vitess.io/vitess/go/vt/topo" + "vitess.io/vitess/go/vt/vtctl/vtctldclient" + + topodatapb "vitess.io/vitess/go/vt/proto/topodata" + vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" + vtctlservicepb "vitess.io/vitess/go/vt/proto/vtctlservice" +) + +// WithTestServer creates a gRPC server listening locally with the given RPC +// implementation, then runs the test func with a client created to point at +// that server. +func WithTestServer( + t *testing.T, + server vtctlservicepb.VtctldServer, + test func(t *testing.T, client vtctldclient.VtctldClient), +) { + lis, err := nettest.NewLocalListener("tcp") + require.NoError(t, err, "cannot create local listener") + + defer lis.Close() + + s := grpc.NewServer() + vtctlservicepb.RegisterVtctldServer(s, server) + + go s.Serve(lis) + defer s.Stop() + + client, err := vtctldclient.New("grpc", lis.Addr().String()) + require.NoError(t, err, "cannot create vtctld client") + + test(t, client) +} + +// AddKeyspace adds a keyspace to a topology, failing a test if that keyspace +// could not be added. It shallow copies the proto struct to prevent XXX_ fields +// from changing in the marshalling. +func AddKeyspace(ctx context.Context, t *testing.T, ts *topo.Server, ks *vtctldatapb.Keyspace) { + in := *ks.Keyspace // take a copy to avoid XXX_ fields changing. + + err := ts.CreateKeyspace(ctx, ks.Name, &in) + require.NoError(t, err) +} + +// AddKeyspaces adds a list of keyspaces to the topology, failing a test if any +// of those keyspaces cannot be added. See AddKeyspace for details. +func AddKeyspaces(ctx context.Context, t *testing.T, ts *topo.Server, keyspaces ...*vtctldatapb.Keyspace) { + for _, keyspace := range keyspaces { + AddKeyspace(ctx, t, ts, keyspace) + } +} + +// AddTablet adds a tablet to the topology, failing a test if that tablet record +// could not be created. It shallow copies to prevent XXX_ fields from changing, +// including nested proto message fields. +// +// AddTablet also optionally adds empty keyspace and shard records to the +// topology, if they are set on the tablet record and they cannot be retrieved +// from the topo server without error. +func AddTablet(ctx context.Context, t *testing.T, ts *topo.Server, tablet *topodatapb.Tablet) { + in := *tablet + alias := *tablet.Alias + in.Alias = &alias + + err := ts.CreateTablet(ctx, &in) + require.NoError(t, err, "CreateTablet(%+v)", &in) + + if tablet.Keyspace != "" { + if _, err := ts.GetKeyspace(ctx, tablet.Keyspace); err != nil { + err := ts.CreateKeyspace(ctx, tablet.Keyspace, &topodatapb.Keyspace{}) + require.NoError(t, err, "CreateKeyspace(%s)", tablet.Keyspace) + } + + if tablet.Shard != "" { + if _, err := ts.GetShard(ctx, tablet.Keyspace, tablet.Shard); err != nil { + err := ts.CreateShard(ctx, tablet.Keyspace, tablet.Shard) + require.NoError(t, err, "CreateShard(%s, %s)", tablet.Keyspace, tablet.Shard) + } + } + } +} + +// AddTablets adds a list of tablets to the topology. See AddTablet for more +// details. +func AddTablets(ctx context.Context, t *testing.T, ts *topo.Server, tablets ...*topodatapb.Tablet) { + for _, tablet := range tablets { + AddTablet(ctx, t, ts, tablet) + } +} + +// AddShards adds a list of shards to the topology, failing a test if any of the +// shard records could not be created. It also ensures that every shard's +// keyspace exists, or creates an empty keyspace if that shard's keyspace does +// not exist. +func AddShards(ctx context.Context, t *testing.T, ts *topo.Server, shards ...*vtctldatapb.Shard) { + for _, shard := range shards { + if shard.Keyspace != "" { + if _, err := ts.GetKeyspace(ctx, shard.Keyspace); err != nil { + err := ts.CreateKeyspace(ctx, shard.Keyspace, &topodatapb.Keyspace{}) + require.NoError(t, err, "CreateKeyspace(%s)", shard.Keyspace) + } + } + + err := ts.CreateShard(ctx, shard.Keyspace, shard.Name) + require.NoError(t, err, "CreateShard(%s/%s)", shard.Keyspace, shard.Name) + } +} + +// SetupReplicationGraphs creates a set of ShardReplication objects in the topo, +// failing the test if any of the records could not be created. +func SetupReplicationGraphs(ctx context.Context, t *testing.T, ts *topo.Server, replicationGraphs ...*topo.ShardReplicationInfo) { + for _, graph := range replicationGraphs { + err := ts.UpdateShardReplicationFields(ctx, graph.Cell(), graph.Keyspace(), graph.Shard(), func(sr *topodatapb.ShardReplication) error { + sr.Nodes = graph.Nodes + return nil + }) + require.NoError(t, err, "could not save replication graph for %s/%s in cell %v", graph.Keyspace(), graph.Shard(), graph.Cell()) + } +} + +// UpdateSrvKeyspaces updates a set of SrvKeyspace records, grouped by cell and +// then by keyspace. It fails the test if any records cannot be updated. +func UpdateSrvKeyspaces(ctx context.Context, t *testing.T, ts *topo.Server, srvkeyspacesByCellByKeyspace map[string]map[string]*topodatapb.SrvKeyspace) { + for cell, srvKeyspacesByKeyspace := range srvkeyspacesByCellByKeyspace { + for keyspace, srvKeyspace := range srvKeyspacesByKeyspace { + err := ts.UpdateSrvKeyspace(ctx, cell, keyspace, srvKeyspace) + require.NoError(t, err, "UpdateSrvKeyspace(%v, %v, %v)", cell, keyspace, srvKeyspace) + } + } +} diff --git a/go/vt/vtctl/grpcvtctldserver/topo.go b/go/vt/vtctl/grpcvtctldserver/topo.go new file mode 100644 index 00000000000..7de161bcd22 --- /dev/null +++ b/go/vt/vtctl/grpcvtctldserver/topo.go @@ -0,0 +1,292 @@ +/* +Copyright 2021 The Vitess Authors. + +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 grpcvtctldserver + +import ( + "context" + "fmt" + + "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/vt/topo" + "vitess.io/vitess/go/vt/topo/topoproto" + "vitess.io/vitess/go/vt/topotools" + "vitess.io/vitess/go/vt/vterrors" + + topodatapb "vitess.io/vitess/go/vt/proto/topodata" + "vitess.io/vitess/go/vt/proto/vtrpc" +) + +func deleteShard(ctx context.Context, ts *topo.Server, keyspace string, shard string, recursive bool, evenIfServing bool) error { + // Read the Shard object. If it's not in the topo, try to clean up the topo + // anyway. + shardInfo, err := ts.GetShard(ctx, keyspace, shard) + if err != nil { + if topo.IsErrType(err, topo.NoNode) { + log.Infof("Shard %v/%v doesn't seem to exist; cleaning up any potential leftover topo data", keyspace, shard) + + return ts.DeleteShard(ctx, keyspace, shard) + } + + return err + } + + servingCells, err := ts.GetShardServingCells(ctx, shardInfo) + if err != nil { + return err + } + + // We never want to remove a potentially serving shard unless someone + // explicitly requested it. + if len(servingCells) > 0 && !evenIfServing { + return vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "shard %v/%v is still serving; cannot delete it; use EvenIfServing = true to delete anyway", keyspace, shard) + } + + cells, err := ts.GetCellInfoNames(ctx) + if err != nil { + return err + } + + for _, cell := range cells { + if err := deleteShardCell(ctx, ts, keyspace, shard, cell, recursive); err != nil { + return err + } + } + + // Try to remove the replication and serving graphs from each cell, + // regardless of whether they exist. + for _, cell := range cells { + if err := ts.DeleteShardReplication(ctx, cell, keyspace, shard); err != nil && !topo.IsErrType(err, topo.NoNode) { + log.Warningf("Cannot delete ShardReplication in cell %v for %v/%v: %w", cell, keyspace, shard, err) + } + } + + return ts.DeleteShard(ctx, keyspace, shard) +} + +// deleteShardCell is the per-cell helper function for deleteShard, and is +// distinct from the RemoveShardCell rpc. Despite having similar names, they are +// **not** the same! +func deleteShardCell(ctx context.Context, ts *topo.Server, keyspace string, shard string, cell string, recursive bool) error { + var aliases []*topodatapb.TabletAlias + + // Get the ShardReplication object for the cell. Collect all the tablets + // that belong to the shard. + sri, err := ts.GetShardReplication(ctx, cell, keyspace, shard) + switch { + case topo.IsErrType(err, topo.NoNode): + // No ShardReplication object means that the topo is inconsistent. + // Therefore we read all the tablets for that cell, and if we find any + // in our shard, we'll either abort or try to delete them, depending on + // whether recursive=true. + aliases, err = ts.GetTabletsByCell(ctx, cell) + if err != nil { + return fmt.Errorf("GetTabletsByCell(%v) failed: %w", cell, err) + } + case err == nil: + // If a ShardReplication object exists, we trust it to have all the + // tablet records for the shard in that cell. + aliases = make([]*topodatapb.TabletAlias, len(sri.Nodes)) + + for i, node := range sri.Nodes { + aliases[i] = node.TabletAlias + } + default: + return fmt.Errorf("GetShardReplication(%v, %v, %v) failed: %w", cell, keyspace, shard, err) + } + + // Get all the tablet records for the aliases we've collected. Note that + // GetTabletMap ignores ErrNoNode, which is convenient for our purpose; it + // means a tablet was deleted but is still referenced. + tabletMap, err := ts.GetTabletMap(ctx, aliases) + if err != nil { + return fmt.Errorf("GetTabletMap() failed: %w", err) + } + + // In the case where no ShardReplication object exists, we collect the + // aliases of every tablet in the cell, so we'll need to filter + // out anything not in our shard. + for alias, ti := range tabletMap { + if !(ti.Keyspace == keyspace && ti.Shard == shard) { + delete(tabletMap, alias) + } + } + + // If there are any tablets in the shard in the cell, delete them. + if len(tabletMap) > 0 { + if !recursive { + return vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "Shard %v/%v still hase %v tablets in cell %v; use Recursive = true or remove them manually", keyspace, shard, len(tabletMap), cell) + } + + log.Infof("Deleting all %d tablets in shard %v/%v cell %v", len(tabletMap), keyspace, shard, cell) + for alias, tablet := range tabletMap { + // We don't care about updating the ShardReplication object, because + // later we're going to delete the entire object. + log.Infof("Deleting tablet %v", alias) + if err := ts.DeleteTablet(ctx, tablet.Alias); err != nil && !topo.IsErrType(err, topo.NoNode) { + // We don't want to continue if a DeleteTablet fails for any + // reason other than a missing tablet (in which case it's just + // topo server inconsistency, which we can ignore). If we were + // to continue and delete the replication graph, the tablet + // record would become orphaned, since we'd no longer know that + // it belongs to this shard. + // + // If the problem is temporary, or resolved externally, + // re-running DeleteShard will skip over tablets that were + // already deleted. + return fmt.Errorf("cannot delete tablet %v: %w", alias, err) + } + } + } + + return nil +} + +func deleteTablet(ctx context.Context, ts *topo.Server, alias *topodatapb.TabletAlias, allowPrimary bool) (err error) { + tablet, err := ts.GetTablet(ctx, alias) + if err != nil { + return err + } + + isPrimary, err := topotools.IsPrimaryTablet(ctx, ts, tablet) + if err != nil { + return err + } + + if isPrimary && !allowPrimary { + return vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "cannot delete tablet %v as it is a master, pass AllowPrimary = true", topoproto.TabletAliasString(alias)) + } + + // Update the Shard object if the master was scrapped. We do this before + // calling DeleteTablet so that the operation can be retried in case of + // failure. + if isPrimary { + lockCtx, unlock, lockErr := ts.LockShard(ctx, tablet.Keyspace, tablet.Shard, fmt.Sprintf("DeleteTablet(%v)", topoproto.TabletAliasString(alias))) + if lockErr != nil { + return lockErr + } + + defer unlock(&err) + + if _, err := ts.UpdateShardFields(lockCtx, tablet.Keyspace, tablet.Shard, func(si *topo.ShardInfo) error { + if !topoproto.TabletAliasEqual(si.MasterAlias, alias) { + log.Warningf( + "Deleting master %v from shard %v/%v but master in Shard object was %v", + topoproto.TabletAliasString(alias), tablet.Keyspace, tablet.Shard, topoproto.TabletAliasString(si.MasterAlias), + ) + + return topo.NewError(topo.NoUpdateNeeded, si.Keyspace()+"/"+si.ShardName()) + } + + si.MasterAlias = nil + + return nil + }); err != nil { + return err + } + } + + // Remove the tablet record and its replication graph entry. + if err := topotools.DeleteTablet(ctx, ts, tablet.Tablet); err != nil { + return err + } + + // Return any error from unlocking the keyspace. + return err +} + +func removeShardCell(ctx context.Context, ts *topo.Server, cell string, keyspace string, shardName string, recursive bool, force bool) error { + shard, err := ts.GetShard(ctx, keyspace, shardName) + if err != nil { + return err + } + + servingCells, err := ts.GetShardServingCells(ctx, shard) + if err != nil { + return err + } + + if !topo.InCellList(cell, servingCells) { + return vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "shard %v/%v does not have serving cell %v", keyspace, shardName, cell) + } + + if shard.MasterAlias != nil && shard.MasterAlias.Cell == cell { + return vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "cannot remove cell %v; shard master %v is in cell", cell, topoproto.TabletAliasString(shard.MasterAlias)) + } + + replication, err := ts.GetShardReplication(ctx, cell, keyspace, shardName) + switch { + case err == nil: + // We have tablets in the shard in this cell. + if recursive { + log.Infof("Deleting all tablets in cell %v in shard %v/%v", cell, keyspace, shardName) + for _, node := range replication.Nodes { + // We don't care about scraping our updating the replication + // graph, because we're about to delete the entire replication + // graph. + log.Infof("Deleting tablet %v", topoproto.TabletAliasString(node.TabletAlias)) + if err := ts.DeleteTablet(ctx, node.TabletAlias); err != nil && !topo.IsErrType(err, topo.NoNode) { + return fmt.Errorf("cannot delete tablet %v: %w", topoproto.TabletAliasString(node.TabletAlias), err) + } + } + } else if len(replication.Nodes) > 0 { + return vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "cell %v has %v possible tablets in replication graph", cell, len(replication.Nodes)) + } + + // Remove the empty replication graph. + if err := ts.DeleteShardReplication(ctx, cell, keyspace, shardName); err != nil && !topo.IsErrType(err, topo.NoNode) { + return fmt.Errorf("error deleting ShardReplication object in cell %v: %w", cell, err) + } + case topo.IsErrType(err, topo.NoNode): + // No ShardReplication object. This is the expected path when there are + // no tablets in the shard in that cell. + err = nil + default: + // If we can't get the replication object out of the local topo, we + // assume the topo server is down in that cell, so we'll only continue + // if Force was specified. + if !force { + return err + } + + log.Warningf("Cannot get ShardReplication from cell %v; assuming cell topo server is down and forcing removal", cell) + } + + // Finally, update the shard. + + log.Infof("Removing cell %v from SrvKeyspace %v/%v", cell, keyspace, shardName) + + ctx, unlock, lockErr := ts.LockKeyspace(ctx, keyspace, "Locking keyspace to remove shard from SrvKeyspace") + if lockErr != nil { + return lockErr + } + + defer unlock(&err) + + if err := ts.DeleteSrvKeyspacePartitions(ctx, keyspace, []*topo.ShardInfo{shard}, topodatapb.TabletType_RDONLY, []string{cell}); err != nil { + return err + } + + if err := ts.DeleteSrvKeyspacePartitions(ctx, keyspace, []*topo.ShardInfo{shard}, topodatapb.TabletType_REPLICA, []string{cell}); err != nil { + return err + } + + if err := ts.DeleteSrvKeyspacePartitions(ctx, keyspace, []*topo.ShardInfo{shard}, topodatapb.TabletType_MASTER, []string{cell}); err != nil { + return err + } + + return err +} diff --git a/go/vt/vtctl/vtctl.go b/go/vt/vtctl/vtctl.go index 2c9f7333954..4b9d89f9f47 100644 --- a/go/vt/vtctl/vtctl.go +++ b/go/vt/vtctl/vtctl.go @@ -730,19 +730,6 @@ func parseTabletType(param string, types []topodatapb.TabletType) (topodatapb.Ta return tabletType, nil } -// parseServingTabletType3 parses the tablet type into the enum, -// and makes sure the enum is of serving type (MASTER, REPLICA, RDONLY/BATCH) -func parseServingTabletType3(param string) (topodatapb.TabletType, error) { - servedType, err := topoproto.ParseTabletType(param) - if err != nil { - return topodatapb.TabletType_UNKNOWN, err - } - if !topo.IsInServingGraph(servedType) { - return topodatapb.TabletType_UNKNOWN, fmt.Errorf("served_type has to be in the serving graph, not %v", param) - } - return servedType, nil -} - func commandInitTablet(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error { dbNameOverride := subFlags.String("db_name_override", "", "Overrides the name of the database that the vttablet uses") allowUpdate := subFlags.Bool("allow_update", false, "Use this flag to force initialization if a tablet with the same name already exists. Use with caution.") @@ -1116,7 +1103,7 @@ func commandWaitForDrain(ctx context.Context, wr *wrangler.Wrangler, subFlags *f if err != nil { return err } - servedType, err := parseServingTabletType3(subFlags.Arg(1)) + servedType, err := topo.ParseServingTabletType(subFlags.Arg(1)) if err != nil { return err } @@ -1397,7 +1384,7 @@ func commandUpdateSrvKeyspacePartition(ctx context.Context, wr *wrangler.Wrangle if err != nil { return err } - tabletType, err := parseServingTabletType3(subFlags.Arg(1)) + tabletType, err := topo.ParseServingTabletType(subFlags.Arg(1)) if err != nil { return err } @@ -1429,7 +1416,7 @@ func commandSetShardTabletControl(ctx context.Context, wr *wrangler.Wrangler, su if err != nil { return err } - tabletType, err := parseServingTabletType3(subFlags.Arg(1)) + tabletType, err := topo.ParseServingTabletType(subFlags.Arg(1)) if err != nil { return err } @@ -1686,7 +1673,7 @@ func commandCreateKeyspace(ctx context.Context, wr *wrangler.Wrangler, subFlags } if len(servedFrom) > 0 { for name, value := range servedFrom { - tt, err := parseServingTabletType3(name) + tt, err := topo.ParseServingTabletType(name) if err != nil { return err } @@ -1773,20 +1760,29 @@ func commandGetKeyspace(ctx context.Context, wr *wrangler.Wrangler, subFlags *fl } keyspace := subFlags.Arg(0) - keyspaceInfo, err := wr.TopoServer().GetKeyspace(ctx, keyspace) + + keyspaceInfo, err := wr.VtctldServer().GetKeyspace(ctx, &vtctldatapb.GetKeyspaceRequest{ + Keyspace: keyspace, + }) if err != nil { return err } // Pass the embedded proto directly or jsonpb will panic. - return printJSON(wr.Logger(), keyspaceInfo.Keyspace) + return printJSON(wr.Logger(), keyspaceInfo.Keyspace.Keyspace) } func commandGetKeyspaces(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error { - keyspaces, err := wr.TopoServer().GetKeyspaces(ctx) + resp, err := wr.VtctldServer().GetKeyspaces(ctx, &vtctldatapb.GetKeyspacesRequest{}) if err != nil { return err } - wr.Logger().Printf("%v\n", strings.Join(keyspaces, "\n")) + + names := make([]string, len(resp.Keyspaces)) + for i, ks := range resp.Keyspaces { + names[i] = ks.Name + } + + wr.Logger().Printf("%v\n", strings.Join(names, "\n")) return nil } @@ -2040,7 +2036,7 @@ func commandMigrateServedTypes(ctx context.Context, wr *wrangler.Wrangler, subFl if err != nil { return err } - servedType, err := parseServingTabletType3(subFlags.Arg(1)) + servedType, err := topo.ParseServingTabletType(subFlags.Arg(1)) if err != nil { return err } @@ -2224,11 +2220,21 @@ func commandFindAllShardsInKeyspace(ctx context.Context, wr *wrangler.Wrangler, } keyspace := subFlags.Arg(0) - result, err := wr.TopoServer().FindAllShardsInKeyspace(ctx, keyspace) + result, err := wr.VtctldServer().FindAllShardsInKeyspace(ctx, &vtctldatapb.FindAllShardsInKeyspaceRequest{ + Keyspace: keyspace, + }) if err != nil { return err } - return printJSON(wr.Logger(), result) + + // reformat data into structure of old interface + legacyShardMap := make(map[string]*topodatapb.Shard, len(result.Shards)) + + for _, shard := range result.Shards { + legacyShardMap[shard.Name] = shard.Shard + } + + return printJSON(wr.Logger(), legacyShardMap) } func commandValidate(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error { @@ -2292,6 +2298,8 @@ func commandGetSchema(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag excludeTables := subFlags.String("exclude_tables", "", "Specifies a comma-separated list of tables to exclude. Each is either an exact match, or a regular expression of the form /regexp/") includeViews := subFlags.Bool("include-views", false, "Includes views in the output") tableNamesOnly := subFlags.Bool("table_names_only", false, "Only displays table names that match") + tableSizesOnly := subFlags.Bool("table_sizes_only", false, "Only displays size information for tables. Ignored if -table_names_only is passed.") + if err := subFlags.Parse(args); err != nil { return err } @@ -2311,17 +2319,26 @@ func commandGetSchema(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag excludeTableArray = strings.Split(*excludeTables, ",") } - sd, err := wr.GetSchema(ctx, tabletAlias, tableArray, excludeTableArray, *includeViews) + resp, err := wr.VtctldServer().GetSchema(ctx, &vtctldatapb.GetSchemaRequest{ + TabletAlias: tabletAlias, + Tables: tableArray, + ExcludeTables: excludeTableArray, + IncludeViews: *includeViews, + TableNamesOnly: *tableNamesOnly, + TableSizesOnly: *tableSizesOnly, + }) if err != nil { return err } + if *tableNamesOnly { - for _, td := range sd.TableDefinitions { + for _, td := range resp.Schema.TableDefinitions { wr.Logger().Printf("%v\n", td.Name) } return nil } - return printJSON(wr.Logger(), sd) + + return printJSON(wr.Logger(), resp.Schema) } func commandReloadSchema(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error { diff --git a/go/vt/vtctl/vtctldclient/client.go b/go/vt/vtctl/vtctldclient/client.go new file mode 100644 index 00000000000..e064b8bd9ae --- /dev/null +++ b/go/vt/vtctl/vtctldclient/client.go @@ -0,0 +1,49 @@ +// Package vtctldclient contains the generic client side of the remote vtctld +// protocol. +package vtctldclient + +import ( + "fmt" + "log" + + vtctlservicepb "vitess.io/vitess/go/vt/proto/vtctlservice" +) + +// VtctldClient augments the vtctlservicepb.VtctlClient interface with io.Closer. +type VtctldClient interface { + vtctlservicepb.VtctldClient + Close() error +} + +// Factory is a function that creates new VtctldClients. +type Factory func(addr string) (VtctldClient, error) + +var registry = map[string]Factory{} + +// Register adds a VtctldClient factory for the given name (protocol). +// Attempting to register mulitple factories for the same protocol is a fatal +// error. +func Register(name string, factory Factory) { + if _, ok := registry[name]; ok { + log.Fatalf("Register: %s already registered", name) + } + + registry[name] = factory +} + +// New returns a VtctldClient for the given protocol, connected to a +// VtctldServer on the given addr. This function returns an error if no client +// factory was registered for the given protocol. +// +// This is a departure from vtctlclient's New, which relies on a flag in the +// global namespace to determine the protocol to use. Instead, we require +// users to specify their own flag in their own (hopefully not global) namespace +// to determine the protocol to pass into here. +func New(protocol string, addr string) (VtctldClient, error) { + factory, ok := registry[protocol] + if !ok { + return nil, fmt.Errorf("unknown vtctld client protocol: %s", protocol) + } + + return factory(addr) +} diff --git a/go/vt/vtctl/workflow/doc.go b/go/vt/vtctl/workflow/doc.go new file mode 100644 index 00000000000..c334470320f --- /dev/null +++ b/go/vt/vtctl/workflow/doc.go @@ -0,0 +1,45 @@ +/* +Copyright 2021 The Vitess Authors. + +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 workflow defines types and functions for working with Vitess workflows. + +This is still a very rough sketch, far from a final API, but I want to document +some things here as I go: + +(1) The lines between package workflow and package workflow/vexec are, uh, + blurry at best, and definitely need serious thinking and refinement. Maybe + there shouldn't even be two separate packages at all. The reason I have the + two packages right now is because I'm operating under the assumption that + there are workflows that are vexec, and then there are other workflows. If + it's true that all workflows are vexec workflows, then probably one single + package could make more sense. For now, two packages seems the way to go, + but like I said, the boundaries are blurry, and things that belong in one + package are in the other, because I haven't gone back and moved things + around. +(2) I'm aiming for this to be a drop-in replacement (more or less) for the + function calls in go/vt/wrangler. However, I'd rather define a better + abstraction if it means having to rewrite even significant portions of the + existing wrangler code to adapt to it, than make a subpar API in the name of + backwards compatibility. I'm not sure if that's a tradeoff I'll even need to + consider in the future, but I'm putting a stake in the ground on which side + of that tradeoff I intend to fall, should it come to it. +(3) Eventually we'll need to consider how the online schema migration workflows + fit into this. I'm trying to at least be somewhat abstract in the + vexec / queryplanner APIs to fit with the QueryParams thing that wrangler + uses, which _should_ work, but who knows?? Time will tell. +*/ +package workflow diff --git a/go/vt/vtctl/workflow/server.go b/go/vt/vtctl/workflow/server.go new file mode 100644 index 00000000000..86c02718d4f --- /dev/null +++ b/go/vt/vtctl/workflow/server.go @@ -0,0 +1,341 @@ +/* +Copyright 2021 The Vitess Authors. + +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 workflow + +import ( + "context" + "errors" + "fmt" + "strings" + "time" + + "github.com/golang/protobuf/proto" + "k8s.io/apimachinery/pkg/util/sets" + + "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/topo" + "vitess.io/vitess/go/vt/vtctl/workflow/vexec" + "vitess.io/vitess/go/vt/vtgate/evalengine" + "vitess.io/vitess/go/vt/vttablet/tmclient" + + binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" + vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" + "vitess.io/vitess/go/vt/proto/vttime" +) + +var ( + // ErrInvalidWorkflow is a catchall error type for conditions that should be + // impossible when operating on a workflow. + ErrInvalidWorkflow = errors.New("invalid workflow") + // ErrMultipleSourceKeyspaces occurs when a workflow somehow has multiple + // source keyspaces across different shard primaries. This should be + // impossible. + ErrMultipleSourceKeyspaces = errors.New("multiple source keyspaces for a single workflow") + // ErrMultipleTargetKeyspaces occurs when a workflow somehow has multiple + // target keyspaces across different shard primaries. This should be + // impossible. + ErrMultipleTargetKeyspaces = errors.New("multiple target keyspaces for a single workflow") +) + +// Server provides an API to work with Vitess workflows, like vreplication +// workflows (MoveTables, Reshard, etc) and schema migration workflows. +// +// NB: This is in alpha, and you probably don't want to depend on it (yet!). +// Currently, it provides only a read-only API to vreplication workflows. Write +// actions on vreplication workflows, and schema migration workflows entirely, +// are not yet supported, but planned. +type Server struct { + ts *topo.Server + tmc tmclient.TabletManagerClient +} + +// NewServer returns a new server instance with the given topo.Server and +// TabletManagerClient. +func NewServer(ts *topo.Server, tmc tmclient.TabletManagerClient) *Server { + return &Server{ + ts: ts, + tmc: tmc, + } +} + +// GetWorkflows returns a list of all workflows that exist in a given keyspace, +// with some additional filtering depending on the request parameters (for +// example, ActiveOnly=true restricts the search to only workflows that are +// currently running). +// +// It has the same signature as the vtctlservicepb.VtctldServer's GetWorkflows +// rpc, and grpcvtctldserver delegates to this function. +func (s *Server) GetWorkflows(ctx context.Context, req *vtctldatapb.GetWorkflowsRequest) (*vtctldatapb.GetWorkflowsResponse, error) { + where := "" + if req.ActiveOnly { + where = "WHERE state <> 'Stopped'" + } + + query := fmt.Sprintf(` + SELECT + id, + workflow, + source, + pos, + stop_pos, + max_replication_lag, + state, + db_name, + time_updated, + transaction_timestamp, + message + FROM + _vt.vreplication + %s`, + where, + ) + + vx := vexec.NewVExec(req.Keyspace, "", s.ts, s.tmc) + results, err := vx.QueryContext(ctx, query) + if err != nil { + return nil, err + } + + workflowsMap := make(map[string]*vtctldatapb.Workflow, len(results)) + sourceKeyspaceByWorkflow := make(map[string]string, len(results)) + sourceShardsByWorkflow := make(map[string]sets.String, len(results)) + targetKeyspaceByWorkflow := make(map[string]string, len(results)) + targetShardsByWorkflow := make(map[string]sets.String, len(results)) + maxVReplicationLagByWorkflow := make(map[string]float64, len(results)) + + // We guarantee the following invariants when this function is called for a + // given workflow: + // - workflow.Name != "" (more precisely, ".Name is set 'properly'") + // - workflowsMap[workflow.Name] == workflow + // - sourceShardsByWorkflow[workflow.Name] != nil + // - targetShardsByWorkflow[workflow.Name] != nil + // - workflow.ShardStatuses != nil + scanWorkflow := func(ctx context.Context, workflow *vtctldatapb.Workflow, row []sqltypes.Value, tablet *topo.TabletInfo) error { + id, err := evalengine.ToInt64(row[0]) + if err != nil { + return err + } + + var bls binlogdatapb.BinlogSource + if err := proto.UnmarshalText(row[2].ToString(), &bls); err != nil { + return err + } + + pos := row[3].ToString() + stopPos := row[4].ToString() + state := row[6].ToString() + dbName := row[7].ToString() + + timeUpdatedSeconds, err := evalengine.ToInt64(row[8]) + if err != nil { + return err + } + + transactionTimeSeconds, err := evalengine.ToInt64(row[9]) + if err != nil { + return err + } + + message := row[10].ToString() + + stream := &vtctldatapb.Workflow_Stream{ + Id: id, + Shard: tablet.Shard, + Tablet: tablet.Alias, + BinlogSource: &bls, + Position: pos, + StopPosition: stopPos, + State: state, + DbName: dbName, + TransactionTimestamp: &vttime.Time{ + Seconds: transactionTimeSeconds, + }, + TimeUpdated: &vttime.Time{ + Seconds: timeUpdatedSeconds, + }, + Message: message, + } + + stream.CopyStates, err = s.getWorkflowCopyStates(ctx, tablet, id) + if err != nil { + return err + } + + switch { + case strings.Contains(strings.ToLower(stream.Message), "error"): + stream.State = "Error" + case stream.State == "Running" && len(stream.CopyStates) > 0: + stream.State = "Copying" + case stream.State == "Running" && int64(time.Now().Second())-timeUpdatedSeconds > 10: + stream.State = "Lagging" + } + + shardStreamKey := fmt.Sprintf("%s/%s", tablet.Shard, tablet.AliasString()) + shardStream, ok := workflow.ShardStreams[shardStreamKey] + if !ok { + ctx, cancel := context.WithTimeout(ctx, *topo.RemoteOperationTimeout) + defer cancel() + + si, err := s.ts.GetShard(ctx, req.Keyspace, tablet.Shard) + if err != nil { + return err + } + + shardStream = &vtctldatapb.Workflow_ShardStream{ + Streams: nil, + TabletControls: si.TabletControls, + IsPrimaryServing: si.IsMasterServing, + } + + workflow.ShardStreams[shardStreamKey] = shardStream + } + + shardStream.Streams = append(shardStream.Streams, stream) + sourceShardsByWorkflow[workflow.Name].Insert(stream.BinlogSource.Shard) + targetShardsByWorkflow[workflow.Name].Insert(tablet.Shard) + + if ks, ok := sourceKeyspaceByWorkflow[workflow.Name]; ok && ks != stream.BinlogSource.Keyspace { + return fmt.Errorf("%w: workflow = %v, ks1 = %v, ks2 = %v", ErrMultipleSourceKeyspaces, workflow.Name, ks, stream.BinlogSource.Keyspace) + } + + sourceKeyspaceByWorkflow[workflow.Name] = stream.BinlogSource.Keyspace + + if ks, ok := targetKeyspaceByWorkflow[workflow.Name]; ok && ks != tablet.Keyspace { + return fmt.Errorf("%w: workflow = %v, ks1 = %v, ks2 = %v", ErrMultipleTargetKeyspaces, workflow.Name, ks, tablet.Keyspace) + } + + targetKeyspaceByWorkflow[workflow.Name] = tablet.Keyspace + + timeUpdated := time.Unix(timeUpdatedSeconds, 0) + vreplicationLag := time.Since(timeUpdated) + + if currentMaxLag, ok := maxVReplicationLagByWorkflow[workflow.Name]; ok { + if vreplicationLag.Seconds() > currentMaxLag { + maxVReplicationLagByWorkflow[workflow.Name] = vreplicationLag.Seconds() + } + } else { + maxVReplicationLagByWorkflow[workflow.Name] = vreplicationLag.Seconds() + } + + return nil + } + + for tablet, result := range results { + qr := sqltypes.Proto3ToResult(result) + + // In the old implementation, we knew we had at most one (0 <= N <= 1) + // workflow for each shard primary we queried. There might be multiple + // rows (streams) comprising that workflow, so we would aggregate the + // rows for a given primary into a single value ("the workflow", + // ReplicationStatusResult in the old types). + // + // In this version, we have many (N >= 0) workflows for each shard + // primary we queried, so we need to determine if each row corresponds + // to a workflow we're already aggregating, or if it's a workflow we + // haven't seen yet for that shard primary. We use the workflow name to + // dedupe for this. + for _, row := range qr.Rows { + workflowName := row[1].ToString() + workflow, ok := workflowsMap[workflowName] + if !ok { + workflow = &vtctldatapb.Workflow{ + Name: workflowName, + ShardStreams: map[string]*vtctldatapb.Workflow_ShardStream{}, + } + + workflowsMap[workflowName] = workflow + sourceShardsByWorkflow[workflowName] = sets.NewString() + targetShardsByWorkflow[workflowName] = sets.NewString() + } + + if err := scanWorkflow(ctx, workflow, row, tablet); err != nil { + return nil, err + } + } + } + + workflows := make([]*vtctldatapb.Workflow, 0, len(workflowsMap)) + + for name, workflow := range workflowsMap { + sourceShards, ok := sourceShardsByWorkflow[name] + if !ok { + return nil, fmt.Errorf("%w: %s has no source shards", ErrInvalidWorkflow, name) + } + + sourceKeyspace, ok := sourceKeyspaceByWorkflow[name] + if !ok { + return nil, fmt.Errorf("%w: %s has no source keyspace", ErrInvalidWorkflow, name) + } + + targetShards, ok := targetShardsByWorkflow[name] + if !ok { + return nil, fmt.Errorf("%w: %s has no target shards", ErrInvalidWorkflow, name) + } + + targetKeyspace, ok := targetKeyspaceByWorkflow[name] + if !ok { + return nil, fmt.Errorf("%w: %s has no target keyspace", ErrInvalidWorkflow, name) + } + + maxVReplicationLag, ok := maxVReplicationLagByWorkflow[name] + if !ok { + return nil, fmt.Errorf("%w: %s has no tracked vreplication lag", ErrInvalidWorkflow, name) + } + + workflow.Source = &vtctldatapb.Workflow_ReplicationLocation{ + Keyspace: sourceKeyspace, + Shards: sourceShards.List(), + } + + workflow.Target = &vtctldatapb.Workflow_ReplicationLocation{ + Keyspace: targetKeyspace, + Shards: targetShards.List(), + } + + workflow.MaxVReplicationLag = int64(maxVReplicationLag) + + workflows = append(workflows, workflow) + } + + return &vtctldatapb.GetWorkflowsResponse{ + Workflows: workflows, + }, nil +} + +func (s *Server) getWorkflowCopyStates(ctx context.Context, tablet *topo.TabletInfo, id int64) ([]*vtctldatapb.Workflow_Stream_CopyState, error) { + query := fmt.Sprintf("select table_name, lastpk from _vt.copy_state where vrepl_id = %d", id) + qr, err := s.tmc.VReplicationExec(ctx, tablet.Tablet, query) + if err != nil { + return nil, err + } + + result := sqltypes.Proto3ToResult(qr) + if result == nil { + return nil, nil + } + + copyStates := make([]*vtctldatapb.Workflow_Stream_CopyState, len(result.Rows)) + for i, row := range result.Rows { + // These fields are technically varbinary, but this is close enough. + copyStates[i] = &vtctldatapb.Workflow_Stream_CopyState{ + Table: row[0].ToString(), + LastPk: row[1].ToString(), + } + } + + return copyStates, nil +} diff --git a/go/vt/vtctl/workflow/vexec/query_plan.go b/go/vt/vtctl/workflow/vexec/query_plan.go new file mode 100644 index 00000000000..f71e124b786 --- /dev/null +++ b/go/vt/vtctl/workflow/vexec/query_plan.go @@ -0,0 +1,116 @@ +/* +Copyright 2021 The Vitess Authors. + +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 vexec + +import ( + "context" + "fmt" + "sync" + + "vitess.io/vitess/go/vt/concurrency" + "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/topo" + "vitess.io/vitess/go/vt/vterrors" + "vitess.io/vitess/go/vt/vttablet/tmclient" + + querypb "vitess.io/vitess/go/vt/proto/query" +) + +// QueryPlan wraps a planned query produced by a QueryPlanner. It is safe to +// execute a QueryPlan repeatedly and in multiple goroutines. +type QueryPlan struct { + ParsedQuery *sqlparser.ParsedQuery + + workflow string + tmc tmclient.TabletManagerClient +} + +// Execute executes a QueryPlan on a single target. +func (qp *QueryPlan) Execute(ctx context.Context, target *topo.TabletInfo) (qr *querypb.QueryResult, err error) { + if qp.ParsedQuery == nil { + return nil, fmt.Errorf("%w: call PlanQuery on a query planner first", ErrUnpreparedQuery) + } + + targetAliasStr := target.AliasString() + + log.Infof("Running %v on %v", qp.ParsedQuery.Query, targetAliasStr) + defer func() { + if err != nil { + log.Warningf("Result on %v: %v", targetAliasStr, err) + + return + } + + log.Infof("Result on %v: %v", targetAliasStr, qr) + }() + + qr, err = qp.tmc.VReplicationExec(ctx, target.Tablet, qp.ParsedQuery.Query) + if err != nil { + return nil, err + } + + if qr.RowsAffected == 0 { + log.Infof("no matching streams found for workflows %s, tablet %s, query %s", qp.workflow, targetAliasStr, qp.ParsedQuery.Query) + } + + return qr, nil +} + +// ExecuteScatter executes a QueryPlan on multiple targets concurrently, +// returning a mapping of target tablet to querypb.QueryResult. Errors from +// individual targets are aggregated into a singular error. +func (qp *QueryPlan) ExecuteScatter(ctx context.Context, targets ...*topo.TabletInfo) (map[*topo.TabletInfo]*querypb.QueryResult, error) { + if qp.ParsedQuery == nil { + // This check is an "optimization" on error handling. We check here, + // even though we will check this during the individual Execute calls, + // so that we return one error, rather than the same error aggregated + // len(targets) times. + return nil, fmt.Errorf("%w: call PlanQuery on a query planner first", ErrUnpreparedQuery) + } + + var ( + m sync.Mutex + wg sync.WaitGroup + rec concurrency.AllErrorRecorder + results = make(map[*topo.TabletInfo]*querypb.QueryResult, len(targets)) + ) + + for _, target := range targets { + wg.Add(1) + + go func(ctx context.Context, target *topo.TabletInfo) { + defer wg.Done() + + qr, err := qp.Execute(ctx, target) + if err != nil { + rec.RecordError(err) + + return + } + + m.Lock() + defer m.Unlock() + + results[target] = qr + }(ctx, target) + } + + wg.Wait() + + return results, rec.AggrError(vterrors.Aggregate) +} diff --git a/go/vt/vtctl/workflow/vexec/query_plan_test.go b/go/vt/vtctl/workflow/vexec/query_plan_test.go new file mode 100644 index 00000000000..ec4f6fab95d --- /dev/null +++ b/go/vt/vtctl/workflow/vexec/query_plan_test.go @@ -0,0 +1,332 @@ +/* +Copyright 2021 The Vitess Authors. + +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 vexec + +import ( + "context" + "errors" + "testing" + + "github.com/stretchr/testify/assert" + + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/topo" + "vitess.io/vitess/go/vt/vtctl/grpcvtctldserver/testutil" + + querypb "vitess.io/vitess/go/vt/proto/query" + topodatapb "vitess.io/vitess/go/vt/proto/topodata" +) + +func TestQueryPlanExecute(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + plan QueryPlan + target *topo.TabletInfo + expected *querypb.QueryResult + shouldErr bool + errKind error + }{ + { + name: "success", + plan: QueryPlan{ + ParsedQuery: &sqlparser.ParsedQuery{ + Query: "SELECT id FROM _vt.vreplication", + }, + tmc: &testutil.TabletManagerClient{ + VReplicationExecResults: map[string]map[string]struct { + Result *querypb.QueryResult + Error error + }{ + "zone1-0000000100": { + "select id from _vt.vreplication": { + Result: &querypb.QueryResult{ + RowsAffected: 1, + }, + }, + }, + }, + }, + }, + target: &topo.TabletInfo{ + Tablet: &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + }, + }, + expected: &querypb.QueryResult{ + RowsAffected: 1, + }, + shouldErr: false, + }, + { + name: "no rows affected", + plan: QueryPlan{ + ParsedQuery: &sqlparser.ParsedQuery{ + Query: "SELECT id FROM _vt.vreplication", + }, + tmc: &testutil.TabletManagerClient{ + VReplicationExecResults: map[string]map[string]struct { + Result *querypb.QueryResult + Error error + }{ + "zone1-0000000100": { + "select id from _vt.vreplication": { + Result: &querypb.QueryResult{ + RowsAffected: 0, + }, + }, + }, + }, + }, + }, + target: &topo.TabletInfo{ + Tablet: &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + }, + }, + expected: &querypb.QueryResult{ + RowsAffected: 0, + }, + shouldErr: false, + }, + { + name: "error", + plan: QueryPlan{ + ParsedQuery: &sqlparser.ParsedQuery{ + Query: "SELECT id FROM _vt.vreplication", + }, + tmc: &testutil.TabletManagerClient{ + VReplicationExecResults: map[string]map[string]struct { + Result *querypb.QueryResult + Error error + }{ + "zone1-0000000100": { + "select id from _vt.vreplication": { + Error: assert.AnError, + }, + }, + }, + }, + }, + target: &topo.TabletInfo{ + Tablet: &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + }, + }, + expected: nil, + shouldErr: true, + }, + { + name: "unprepared query", + plan: QueryPlan{ + ParsedQuery: nil, + }, + shouldErr: true, + errKind: ErrUnpreparedQuery, + }, + } + + for _, tt := range tests { + tt := tt + + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + qr, err := tt.plan.Execute(ctx, tt.target) + if tt.shouldErr { + assert.Error(t, err) + + if tt.errKind != nil { + assert.True(t, errors.Is(err, tt.errKind), "expected error kind (= %v), got = %v", tt.errKind, err) + } + + return + } + + assert.NoError(t, err) + assert.Equal(t, tt.expected, qr) + }) + } +} + +func TestQueryPlanExecuteScatter(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + plan QueryPlan + targets []*topo.TabletInfo + // This is different from our actual return type because guaranteeing + // exact pointers in this table-driven style is a bit tough. + expected map[string]*querypb.QueryResult + shouldErr bool + errKind error + }{ + { + name: "success", + plan: QueryPlan{ + ParsedQuery: &sqlparser.ParsedQuery{ + Query: "SELECT id FROM _vt.vreplication", + }, + tmc: &testutil.TabletManagerClient{ + VReplicationExecResults: map[string]map[string]struct { + Result *querypb.QueryResult + Error error + }{ + "zone1-0000000100": { + "select id from _vt.vreplication": { + Result: &querypb.QueryResult{ + RowsAffected: 10, + }, + }, + }, + "zone1-0000000101": { + "select id from _vt.vreplication": { + Result: &querypb.QueryResult{ + RowsAffected: 5, + }, + }, + }, + }, + }, + }, + targets: []*topo.TabletInfo{ + { + Tablet: &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + }, + }, + { + Tablet: &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 101, + }, + }, + }, + }, + expected: map[string]*querypb.QueryResult{ + "zone1-0000000100": { + RowsAffected: 10, + }, + "zone1-0000000101": { + RowsAffected: 5, + }, + }, + shouldErr: false, + }, + { + name: "some targets fail", + plan: QueryPlan{ + ParsedQuery: &sqlparser.ParsedQuery{ + Query: "SELECT id FROM _vt.vreplication", + }, + tmc: &testutil.TabletManagerClient{ + VReplicationExecResults: map[string]map[string]struct { + Result *querypb.QueryResult + Error error + }{ + "zone1-0000000100": { + "select id from _vt.vreplication": { + Error: assert.AnError, + }, + }, + "zone1-0000000101": { + "select id from _vt.vreplication": { + Result: &querypb.QueryResult{ + RowsAffected: 5, + }, + }, + }, + }, + }, + }, + targets: []*topo.TabletInfo{ + { + Tablet: &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 100, + }, + }, + }, + { + Tablet: &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: "zone1", + Uid: 101, + }, + }, + }, + }, + shouldErr: true, + }, + { + name: "unprepared query", + plan: QueryPlan{ + ParsedQuery: nil, + }, + shouldErr: true, + errKind: ErrUnpreparedQuery, + }, + } + + for _, tt := range tests { + tt := tt + + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + results, err := tt.plan.ExecuteScatter(ctx, tt.targets...) + if tt.shouldErr { + assert.Error(t, err) + + if tt.errKind != nil { + assert.True(t, errors.Is(err, tt.errKind), "expected error kind (= %v), got = %v", tt.errKind, err) + } + + return + } + + assert.NoError(t, err) + + resultsByAlias := make(map[string]*querypb.QueryResult, len(results)) + for tablet, qr := range results { + resultsByAlias[tablet.AliasString()] = qr + } + + assert.Equal(t, tt.expected, resultsByAlias) + }) + } +} diff --git a/go/vt/vtctl/workflow/vexec/query_planner.go b/go/vt/vtctl/workflow/vexec/query_planner.go new file mode 100644 index 00000000000..a850cc9816a --- /dev/null +++ b/go/vt/vtctl/workflow/vexec/query_planner.go @@ -0,0 +1,326 @@ +/* +Copyright 2021 The Vitess Authors. + +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 vexec + +import ( + "errors" + "fmt" + + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vttablet/tmclient" +) + +var ( // Query planning errors. + // ErrCannotUpdateImmutableColumn is returned when attempting to plan a + // query that updates a column that should be treated as immutable. + ErrCannotUpdateImmutableColumn = errors.New("cannot update immutable column") + // ErrUnsupportedQueryConstruct is returned when a particular query + // construct is unsupported by a QueryPlanner, despite the more general kind + // of query being supported. + // + // For example, VReplication supports DELETEs, but does not support DELETEs + // with LIMIT clauses, so planning a "DELETE ... LIMIT" will return + // ErrUnsupportedQueryConstruct rather than a "CREATE TABLE", which would + // return an ErrUnsupportedQuery. + ErrUnsupportedQueryConstruct = errors.New("unsupported query construct") +) + +var ( // Query execution errors. + // ErrUnpreparedQuery is returned when attempting to execute an unprepared + // QueryPlan. + ErrUnpreparedQuery = errors.New("attempted to execute unprepared query") +) + +// QueryPlanner defines the interface that VExec uses to build QueryPlans for +// various vexec workflows. A given vexec table, which is to say a table in the +// "_vt" database, will have at most one QueryPlanner implementation, which is +// responsible for defining both what queries are supported for that table, as +// well as how to build plans for those queries. +// +// VReplicationQueryPlanner is a good example implementation to refer to. +type QueryPlanner interface { + // (NOTE:@ajm188) I don't think this method fits on the query planner. To + // me, especially given that it's only implemented by the vrep query planner + // in the old implementation (the schema migration query planner no-ops this + // method), this fits better on our workflow.Manager struct, probably as a + // method called something like "VReplicationExec(ctx, query, Options{DryRun: true})" + // DryRun(ctx context.Context) error + + // PlanQuery constructs and returns a QueryPlan for a given statement. The + // resulting QueryPlan is suitable for repeated, concurrent use. + PlanQuery(stmt sqlparser.Statement) (*QueryPlan, error) + // QueryParams returns a struct of column parameters the QueryPlanner uses. + // It is used primarily to abstract the adding of default WHERE clauses to + // queries by a private function of this package, and may be removed from + // the interface later. + QueryParams() QueryParams +} + +// QueryParams is a struct that QueryPlanner implementations can provide to +// control the addition of default WHERE clauses to their queries. +type QueryParams struct { + // DBName is the value that the column referred to by DBNameColumn should + // equal in a WHERE clause, if set. + DBName string + // DBNameColumn is the name of the column that DBName should equal in a + // WHERE clause, if set. + DBNameColumn string + // Workflow is the value that the column referred to by WorkflowColumn + // should equal in a WHERE clause, if set. + Workflow string + // WorkflowColumn is the name of the column that Workflow should equal in a + // WHERE clause, if set. + WorkflowColumn string +} + +// VReplicationQueryPlanner implements the QueryPlanner interface for queries on +// the _vt.vreplication table. +type VReplicationQueryPlanner struct { + tmc tmclient.TabletManagerClient + + dbname string + workflow string +} + +// NewVReplicationQueryPlanner returns a new VReplicationQueryPlanner. It is +// valid to pass empty strings for both the dbname and workflow parameters. +func NewVReplicationQueryPlanner(tmc tmclient.TabletManagerClient, workflow string, dbname string) *VReplicationQueryPlanner { + return &VReplicationQueryPlanner{ + tmc: tmc, + dbname: dbname, + workflow: workflow, + } +} + +// PlanQuery is part of the QueryPlanner interface. +// +// For vreplication query planners, only SELECT, UPDATE, and DELETE queries are +// supported. +// +// For UPDATE queries, ORDER BY and LIMIT clauses are not supported. Attempting +// to update vreplication.id is an error. +// +// For DELETE queries, USING, PARTITION, ORDER BY, and LIMIT clauses are not +// supported. +func (planner *VReplicationQueryPlanner) PlanQuery(stmt sqlparser.Statement) (plan *QueryPlan, err error) { + switch stmt := stmt.(type) { + case *sqlparser.Select: + plan, err = planner.planSelect(stmt) + case *sqlparser.Insert: + err = ErrUnsupportedQuery + case *sqlparser.Update: + plan, err = planner.planUpdate(stmt) + case *sqlparser.Delete: + plan, err = planner.planDelete(stmt) + default: + err = ErrUnsupportedQuery + } + + if err != nil { + return nil, fmt.Errorf("%w: %s", err, sqlparser.String(stmt)) + } + + return plan, nil +} + +// QueryParams is part of the QueryPlanner interface. A VReplicationQueryPlanner +// will attach the following WHERE clauses iff (a) DBName, Workflow are set, +// respectively, and (b) db_name and workflow do not appear in the original +// query's WHERE clause: +// +// WHERE (db_name = {{ .DBName }} AND)? (workflow = {{ .Workflow }} AND)? {{ .OriginalWhere }} +func (planner *VReplicationQueryPlanner) QueryParams() QueryParams { + return QueryParams{ + DBName: planner.dbname, + DBNameColumn: "db_name", + Workflow: planner.workflow, + WorkflowColumn: "workflow", + } +} + +func (planner *VReplicationQueryPlanner) planDelete(del *sqlparser.Delete) (*QueryPlan, error) { + if del.Targets != nil { + return nil, fmt.Errorf( + "%w: DELETE must not have USING clause (have: %v): %v", + ErrUnsupportedQueryConstruct, + del.Targets, + sqlparser.String(del), + ) + } + + if del.Partitions != nil { + return nil, fmt.Errorf( + "%w: DELETE must not have explicit partitions (have: %v): %v", + ErrUnsupportedQueryConstruct, + del.Partitions, + sqlparser.String(del), + ) + } + + if del.OrderBy != nil || del.Limit != nil { + return nil, fmt.Errorf( + "%w: DELETE must not have explicit ordering (have: %v) or limit clauses (have: %v): %v", + ErrUnsupportedQueryConstruct, + del.OrderBy, + del.Limit, + sqlparser.String(del), + ) + } + + del.Where = addDefaultWheres(planner, del.Where) + + buf := sqlparser.NewTrackedBuffer(nil) + buf.Myprintf("%v", del) + + return &QueryPlan{ + ParsedQuery: buf.ParsedQuery(), + workflow: planner.workflow, + tmc: planner.tmc, + }, nil +} + +func (planner *VReplicationQueryPlanner) planSelect(sel *sqlparser.Select) (*QueryPlan, error) { + sel.Where = addDefaultWheres(planner, sel.Where) + + buf := sqlparser.NewTrackedBuffer(nil) + buf.Myprintf("%v", sel) + + return &QueryPlan{ + ParsedQuery: buf.ParsedQuery(), + workflow: planner.workflow, + tmc: planner.tmc, + }, nil +} + +func (planner *VReplicationQueryPlanner) planUpdate(upd *sqlparser.Update) (*QueryPlan, error) { + if upd.OrderBy != nil || upd.Limit != nil { + return nil, fmt.Errorf( + "%w: UPDATE must not have explicit ordering (have: %v) or limit clauses (have: %v): %v", + ErrUnsupportedQueryConstruct, + upd.OrderBy, + upd.Limit, + sqlparser.String(upd), + ) + } + + // For updates on the _vt.vreplication table, we ban updates to the `id` + // column, and allow updates to all other columns. + for _, expr := range upd.Exprs { + if expr.Name.Name.EqualString("id") { + return nil, fmt.Errorf( + "%w %+v: %v", + ErrCannotUpdateImmutableColumn, + expr.Name.Name, + sqlparser.String(expr), + ) + } + } + + upd.Where = addDefaultWheres(planner, upd.Where) + + buf := sqlparser.NewTrackedBuffer(nil) + buf.Myprintf("%v", upd) + + return &QueryPlan{ + ParsedQuery: buf.ParsedQuery(), + workflow: planner.workflow, + tmc: planner.tmc, + }, nil +} + +func addDefaultWheres(planner QueryPlanner, where *sqlparser.Where) *sqlparser.Where { + cols := extractWhereComparisonColumns(where) + + params := planner.QueryParams() + hasDBNameCol := false + hasWorkflowCol := false + + for _, col := range cols { + switch col { + case params.DBNameColumn: + hasDBNameCol = true + case params.WorkflowColumn: + hasWorkflowCol = true + } + } + + newWhere := where + + if !hasDBNameCol { + expr := &sqlparser.ComparisonExpr{ + Left: &sqlparser.ColName{ + Name: sqlparser.NewColIdent(params.DBNameColumn), + }, + Operator: sqlparser.EqualOp, + Right: sqlparser.NewStrLiteral([]byte(params.DBName)), + } + + switch newWhere { + case nil: + newWhere = &sqlparser.Where{ + Type: sqlparser.WhereClause, + Expr: expr, + } + default: + newWhere.Expr = &sqlparser.AndExpr{ + Left: newWhere.Expr, + Right: expr, + } + } + } + + if !hasWorkflowCol && params.Workflow != "" { + expr := &sqlparser.ComparisonExpr{ + Left: &sqlparser.ColName{ + Name: sqlparser.NewColIdent(params.WorkflowColumn), + }, + Operator: sqlparser.EqualOp, + Right: sqlparser.NewStrLiteral([]byte(params.Workflow)), + } + + newWhere.Expr = &sqlparser.AndExpr{ + Left: newWhere.Expr, + Right: expr, + } + } + + return newWhere +} + +// extractWhereComparisonColumns extracts the column names used in AND-ed +// comparison expressions in a where clause, given the following assumptions: +// - (1) The column name is always the left-hand side of the comparison. +// - (2) There are no compound expressions within the where clause involving OR. +func extractWhereComparisonColumns(where *sqlparser.Where) []string { + if where == nil { + return nil + } + + exprs := sqlparser.SplitAndExpression(nil, where.Expr) + cols := make([]string, 0, len(exprs)) + + for _, expr := range exprs { + switch expr := expr.(type) { + case *sqlparser.ComparisonExpr: + if qualifiedName, ok := expr.Left.(*sqlparser.ColName); ok { + cols = append(cols, qualifiedName.Name.String()) + } + } + } + + return cols +} diff --git a/go/vt/vtctl/workflow/vexec/query_planner_test.go b/go/vt/vtctl/workflow/vexec/query_planner_test.go new file mode 100644 index 00000000000..a63fbb96a65 --- /dev/null +++ b/go/vt/vtctl/workflow/vexec/query_planner_test.go @@ -0,0 +1,244 @@ +/* +Copyright 2021 The Vitess Authors. + +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 vexec + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/assert" + + "vitess.io/vitess/go/vt/vtctl/workflow/vexec/testutil" +) + +func TestVReplicationQueryPlanner_PlanQuery(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + query string + err error + }{ + { + name: "basic select", + query: "SELECT id FROM _vt.vreplication", + err: nil, + }, + { + name: "insert not supported", + query: "INSERT INTO _vt.vreplication (id) VALUES (1)", + err: ErrUnsupportedQuery, + }, + { + name: "basic update", + query: "UPDATE _vt.vreplication SET workflow = 'my workflow'", + err: nil, + }, + { + name: "basic delete", + query: "DELETE FROM _vt.vreplication", + err: nil, + }, + { + name: "other query", + query: "CREATE TABLE foo (id INT(11) PRIMARY KEY NOT NULL) ENGINE=InnoDB", + err: ErrUnsupportedQuery, + }, + } + + planner := NewVReplicationQueryPlanner(nil, "", "") + + for _, tt := range tests { + tt := tt + + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + stmt := testutil.StatementFromString(t, tt.query) + + _, err := planner.PlanQuery(stmt) + if tt.err != nil { + assert.True(t, errors.Is(err, tt.err), "expected err of type %v, got %v", tt.err, err) + + return + } + + assert.NoError(t, err) + }) + } +} + +func TestVReplicationQueryPlanner_planSelect(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + query string + expectedPlannedQuery string + }{ + { + name: "simple select", + query: "SELECT id FROM _vt.vreplication WHERE id > 10", + expectedPlannedQuery: "SELECT id FROM _vt.vreplication WHERE id > 10 AND db_name = 'vt_testkeyspace' AND workflow = 'testworkflow'", + }, + { + name: "select with workflow and dbname columns already in WHERE", + query: "SELECT id FROM _vt.vreplication WHERE id > 10 AND db_name = 'vt_testkeyspace' AND workflow = 'testworkflow'", + expectedPlannedQuery: "SELECT id FROM _vt.vreplication WHERE id > 10 AND db_name = 'vt_testkeyspace' AND workflow = 'testworkflow'", + }, + { + // In this case, the QueryParams for the planner (which have + // workflow = "testworkflow"; db_name = "vt_testkeyspace") are + // ignored because the WHERE clause was explicit. + name: "select with workflow and dbname columns with different values", + query: "SELECT id FROM _vt.vreplication WHERE id > 10 AND db_name = 'different_keyspace' AND workflow = 'otherworkflow'", + expectedPlannedQuery: "SELECT id FROM _vt.vreplication WHERE id > 10 AND db_name = 'different_keyspace' AND workflow = 'otherworkflow'", + }, + } + + planner := NewVReplicationQueryPlanner(nil, "testworkflow", "vt_testkeyspace") + + for _, tt := range tests { + tt := tt + + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + stmt := testutil.StatementFromString(t, tt.query) + qp, err := planner.PlanQuery(stmt) + + assert.NoError(t, err) + assert.Equal(t, testutil.ParsedQueryFromString(t, tt.expectedPlannedQuery), qp.ParsedQuery) + }) + } +} + +func TestVReplicationQueryPlanner_planUpdate(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + planner *VReplicationQueryPlanner + query string + expectedPlannedQuery string + expectedErr error + }{ + { + name: "simple update", + planner: NewVReplicationQueryPlanner(nil, "testworkflow", "vt_testkeyspace"), + query: "UPDATE _vt.vreplication SET state = 'Running'", + expectedPlannedQuery: "UPDATE _vt.vreplication SET state = 'Running' WHERE db_name = 'vt_testkeyspace' AND workflow = 'testworkflow'", + expectedErr: nil, + }, + { + name: "including an ORDER BY is an error", + planner: NewVReplicationQueryPlanner(nil, "", ""), + query: "UPDATE _vt.vreplication SET state = 'Running' ORDER BY id DESC", + expectedErr: ErrUnsupportedQueryConstruct, + }, + { + name: "including a LIMIT is an error", + planner: NewVReplicationQueryPlanner(nil, "", ""), + query: "UPDATE _vt.vreplication SET state = 'Running' LIMIT 5", + expectedErr: ErrUnsupportedQueryConstruct, + }, + { + name: "cannot update id column", + planner: NewVReplicationQueryPlanner(nil, "", "vt_testkeyspace"), + query: "UPDATE _vt.vreplication SET id = 5", + expectedErr: ErrCannotUpdateImmutableColumn, + }, + } + + for _, tt := range tests { + tt := tt + + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + stmt := testutil.StatementFromString(t, tt.query) + + qp, err := tt.planner.PlanQuery(stmt) + if tt.expectedErr != nil { + assert.True(t, errors.Is(err, tt.expectedErr), "expected err of type %q, got %q", tt.expectedErr, err) + + return + } + + assert.Equal(t, testutil.ParsedQueryFromString(t, tt.expectedPlannedQuery), qp.ParsedQuery) + }) + } +} + +func TestVReplicationQueryPlanner_planDelete(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + query string + expectedPlannedQuery string + expectedErr error + }{ + { + name: "simple delete", + query: "DELETE FROM _vt.vreplication WHERE id = 1", + expectedPlannedQuery: "DELETE FROM _vt.vreplication WHERE id = 1 AND db_name = 'vt_testkeyspace'", + expectedErr: nil, + }, + { + name: "DELETE with USING clause is not supported", + query: "DELETE FROM _vt.vreplication, _vt.schema_migrations USING _vt.vreplication INNER JOIN _vt.schema_migrations", + expectedErr: ErrUnsupportedQueryConstruct, + }, + { + name: "DELETE with a PARTITION clause is not supported", + query: "DELETE FROM _vt.vreplication PARTITION (p1)", + expectedErr: ErrUnsupportedQueryConstruct, + }, + { + name: "DELETE with ORDER BY is not supported", + query: "DELETE FROM _vt.vreplication ORDER BY id DESC", + expectedErr: ErrUnsupportedQueryConstruct, + }, + { + name: "DELETE with LIMIT is not supported", + query: "DELETE FROM _vt.vreplication LIMIT 5", + expectedErr: ErrUnsupportedQueryConstruct, + }, + } + + planner := NewVReplicationQueryPlanner(nil, "", "vt_testkeyspace") + + for _, tt := range tests { + tt := tt + + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + stmt := testutil.StatementFromString(t, tt.query) + + qp, err := planner.PlanQuery(stmt) + if tt.expectedErr != nil { + assert.True(t, errors.Is(err, tt.expectedErr), "expected err of type %q, got %q", tt.expectedErr, err) + + return + } + + assert.Equal(t, testutil.ParsedQueryFromString(t, tt.expectedPlannedQuery), qp.ParsedQuery) + }) + } +} diff --git a/go/vt/vtctl/workflow/vexec/testutil/query.go b/go/vt/vtctl/workflow/vexec/testutil/query.go new file mode 100644 index 00000000000..3988f7a112f --- /dev/null +++ b/go/vt/vtctl/workflow/vexec/testutil/query.go @@ -0,0 +1,48 @@ +/* +Copyright 2021 The Vitess Authors. + +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 testutil + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/vt/sqlparser" +) + +// ParsedQueryFromString is a test helper that returns a *sqlparser.ParsedQuery +// from a plain string. It marks the test as a failure if the query cannot be +// parsed. +func ParsedQueryFromString(t *testing.T, query string) *sqlparser.ParsedQuery { + t.Helper() + + buf := sqlparser.NewTrackedBuffer(nil) + buf.Myprintf("%v", StatementFromString(t, query)) + + return buf.ParsedQuery() +} + +// StatementFromString is a test helper that returns a sqlparser.Statement from +// a plain string. It marks the test as a failure if the query cannot be parsed. +func StatementFromString(t *testing.T, query string) sqlparser.Statement { + t.Helper() + + stmt, err := sqlparser.Parse(query) + require.NoError(t, err, "could not parse query %v", query) + + return stmt +} diff --git a/go/vt/vtctl/workflow/vexec/vexec.go b/go/vt/vtctl/workflow/vexec/vexec.go new file mode 100644 index 00000000000..d61bd16ab31 --- /dev/null +++ b/go/vt/vtctl/workflow/vexec/vexec.go @@ -0,0 +1,235 @@ +/* +Copyright 2021 The Vitess Authors. + +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 vexec + +import ( + "context" + "errors" + "fmt" + + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/topo" + "vitess.io/vitess/go/vt/topo/topoproto" + "vitess.io/vitess/go/vt/vttablet/tmclient" + + querypb "vitess.io/vitess/go/vt/proto/query" +) + +const ( + // VExecTableQualifier is the qualifier that all tables supported by vexec + // are prefixed by. + VExecTableQualifier = "_vt" + + // SchemaMigrationsTableName is the unqualified name of the schema + // migrations table supported by vexec. + SchemaMigrationsTableName = "schema_migrations" + // VReplicationTableName is the unqualified name of the vreplication table + // supported by vexec. + VReplicationTableName = "vreplication" +) + +var ( // Topo lookup errors. + // ErrNoShardPrimary occurs when a shard is found with no serving + // primary. + ErrNoShardPrimary = errors.New("no primary found for shard") + // ErrNoShardsForKeyspace occurs when attempting to run a vexec on an empty + // keyspace. + ErrNoShardsForKeyspace = errors.New("no shards found in keyspace") +) + +var ( // Query parsing and planning errors. + // ErrUnsupportedQuery occurs when attempting to run an unsupported query + // through vexec. + ErrUnsupportedQuery = errors.New("query not supported by vexec") + // ErrUnsupportedTable occurs when attempting to run vexec on an unsupported + // table. At the time of writing, this occurs when attempting to query any + // table other than _vt.vreplication. + ErrUnsupportedTable = errors.New("table not supported by vexec") +) + +// VExec provides the main interface to planning and executing vexec queries +// (normally, queries on tables in the `_vt` database). It currently supports +// some limited vreplication queries; this set of supported behavior will expand +// over time. It may be extended to support schema_migrations queries as well. +type VExec struct { + ts *topo.Server + tmc tmclient.TabletManagerClient + + keyspace string + workflow string + + // (TODO:@ajm188) Consider renaming this field to "targets", and then + // support different Strategy functions for loading target tablets from a + // topo.Server. + // + // For this, I'm currently thinking: + // type TargetStrategy func(ts *topo.Server) ([]*topo.TabletInfo, error) + // + // We _may_ want this if we ever want a vexec query to target anything other + // than "all of the shard primaries in a given keyspace", and I'm not sure + // about potential future usages yet. + primaries []*topo.TabletInfo + // (TODO:@ajm188) Similar to supporting a TargetStrategy for controlling how + // a VExec picks which tablets to query, we may also want an + // ExecutionStrategy (I'm far less sure about whether we would want this at + // all, or what its type definition might look like, than TargetStrategy), + // to support running in modes like: + // - Execute serially rather than concurrently. + // - Only return error if greater than some percentage of the targets fail. +} + +// NewVExec returns a new instance suitable for making vexec queries to a given +// keyspace (required) and workflow (optional, omit by providing the empty +// string). The provided topo server is used to look up target tablets for +// queries. A given instance will discover targets exactly once for its +// lifetime, so to force a refresh, create another instance. +func NewVExec(keyspace string, workflow string, ts *topo.Server, tmc tmclient.TabletManagerClient) *VExec { + return &VExec{ + ts: ts, + tmc: tmc, + keyspace: keyspace, + workflow: workflow, + } +} + +// QueryContext executes the given vexec query, returning a mapping of tablet +// to querypb.QueryResult. +// +// On first use, QueryContext will also cause the VExec instance to discover +// target tablets from the topo; that target list will be reused for all future +// queries made by this instance. +// +// For details on query parsing and planning, see GetPlanner and the +// QueryPlanner interface. +func (vx *VExec) QueryContext(ctx context.Context, query string) (map[*topo.TabletInfo]*querypb.QueryResult, error) { + if vx.primaries == nil { + if err := vx.initialize(ctx); err != nil { + return nil, err + } + } + + stmt, err := sqlparser.Parse(query) + if err != nil { + return nil, err + } + + table, err := extractTableName(stmt) + if err != nil { + return nil, err + } + + planner, err := vx.GetPlanner(ctx, table) + if err != nil { + return nil, err + } + + qp, err := planner.PlanQuery(stmt) + if err != nil { + return nil, err + } + + return qp.ExecuteScatter(ctx, vx.primaries...) +} + +func (vx *VExec) initialize(ctx context.Context) error { + vx.primaries = nil + + getShardsCtx, getShardsCancel := context.WithTimeout(ctx, *topo.RemoteOperationTimeout) + defer getShardsCancel() + + shards, err := vx.ts.GetShardNames(getShardsCtx, vx.keyspace) + if err != nil { + return err + } + + if len(shards) == 0 { + return fmt.Errorf("%w %s", ErrNoShardsForKeyspace, vx.keyspace) + } + + primaries := make([]*topo.TabletInfo, 0, len(shards)) + + for _, shard := range shards { + ctx, cancel := context.WithTimeout(ctx, *topo.RemoteOperationTimeout) + defer cancel() + + si, err := vx.ts.GetShard(ctx, vx.keyspace, shard) + if err != nil { + return err + } + + if si.MasterAlias == nil { + return fmt.Errorf("%w %s/%s", ErrNoShardPrimary, vx.keyspace, shard) + } + + primary, err := vx.ts.GetTablet(ctx, si.MasterAlias) + if err != nil { + return err + } + + if primary == nil { + return fmt.Errorf("%w %s/%s: tablet %v not found", ErrNoShardPrimary, vx.keyspace, shard, topoproto.TabletAliasString(si.MasterAlias)) + } + + primaries = append(primaries, primary) + } + + vx.primaries = primaries + + return nil +} + +// GetPlanner returns an appropriate implementation of a QueryPlanner, depending +// on the table being queried. +// +// On first use, GetPlanner will also cause the VExec instance to discover +// target tablets from the topo; that target list will be reused for all future +// queries made by this instance. +func (vx *VExec) GetPlanner(ctx context.Context, table string) (QueryPlanner, error) { // TODO: private? + if vx.primaries == nil { + if err := vx.initialize(ctx); err != nil { + return nil, fmt.Errorf("error while initializing target list: %w", err) + } + } + + switch table { + case qualifiedTableName(VReplicationTableName): + return NewVReplicationQueryPlanner(vx.tmc, vx.workflow, vx.primaries[0].DbName()), nil + case qualifiedTableName(SchemaMigrationsTableName): + return nil, errors.New("Schema Migrations not yet supported in new workflow package") + default: + return nil, fmt.Errorf("%w: %v", ErrUnsupportedTable, table) + } +} + +func extractTableName(stmt sqlparser.Statement) (string, error) { + switch stmt := stmt.(type) { + case *sqlparser.Update: + return sqlparser.String(stmt.TableExprs), nil + case *sqlparser.Delete: + return sqlparser.String(stmt.TableExprs), nil + case *sqlparser.Insert: + return sqlparser.String(stmt.Table), nil + case *sqlparser.Select: + return sqlparser.String(stmt.From), nil + } + + return "", fmt.Errorf("%w: %+v", ErrUnsupportedQuery, sqlparser.String(stmt)) +} + +func qualifiedTableName(name string) string { + return fmt.Sprintf("%s.%s", VExecTableQualifier, name) +} diff --git a/go/vt/wrangler/tablet.go b/go/vt/wrangler/tablet.go index 77d112a450e..3bd61f59fbf 100644 --- a/go/vt/wrangler/tablet.go +++ b/go/vt/wrangler/tablet.go @@ -229,22 +229,5 @@ func (wr *Wrangler) GenericVExec(ctx context.Context, tabletAlias *topodatapb.Ta // the system is in transition (a reparenting event is in progress and parts of // the topo have not yet been updated). func (wr *Wrangler) isMasterTablet(ctx context.Context, ti *topo.TabletInfo) (bool, error) { - // Tablet record claims to be non-master, we believe it - if ti.Type != topodatapb.TabletType_MASTER { - return false, nil - } - si, err := wr.ts.GetShard(ctx, ti.Keyspace, ti.Shard) - if err != nil { - // strictly speaking it isn't correct to return false here, the tablet status is unknown - return false, err - } - // Tablet record claims to be master, and shard record matches - if topoproto.TabletAliasEqual(si.MasterAlias, ti.Tablet.Alias) { - return true, nil - } - // Shard record has another tablet as master, so check MasterTermStartTime - // If tablet record's MasterTermStartTime is later than the one in the shard record, then tablet is master - tabletMTST := ti.GetMasterTermStartTime() - shardMTST := si.GetMasterTermStartTime() - return tabletMTST.After(shardMTST), nil + return topotools.IsPrimaryTablet(ctx, wr.TopoServer(), ti) } diff --git a/go/vt/wrangler/wrangler.go b/go/vt/wrangler/wrangler.go index 83672b0f880..787f5008fc7 100644 --- a/go/vt/wrangler/wrangler.go +++ b/go/vt/wrangler/wrangler.go @@ -21,7 +21,10 @@ package wrangler import ( "vitess.io/vitess/go/vt/logutil" "vitess.io/vitess/go/vt/topo" + "vitess.io/vitess/go/vt/vtctl/grpcvtctldserver" "vitess.io/vitess/go/vt/vttablet/tmclient" + + vtctlservicepb "vitess.io/vitess/go/vt/proto/vtctlservice" ) var ( @@ -42,6 +45,7 @@ type Wrangler struct { logger logutil.Logger ts *topo.Server tmc tmclient.TabletManagerClient + vtctld vtctlservicepb.VtctldServer } // New creates a new Wrangler object. @@ -50,6 +54,7 @@ func New(logger logutil.Logger, ts *topo.Server, tmc tmclient.TabletManagerClien logger: logger, ts: ts, tmc: tmc, + vtctld: grpcvtctldserver.NewVtctldServer(ts), } } @@ -64,6 +69,12 @@ func (wr *Wrangler) TabletManagerClient() tmclient.TabletManagerClient { return wr.tmc } +// VtctldServer returns the vtctlservicepb.VtctldServer implementation this +// wrangler is using. +func (wr *Wrangler) VtctldServer() vtctlservicepb.VtctldServer { + return wr.vtctld +} + // SetLogger can be used to change the current logger. Not synchronized, // no calls to this wrangler should be in progress. func (wr *Wrangler) SetLogger(logger logutil.Logger) { diff --git a/proto/mysqlctl.proto b/proto/mysqlctl.proto index 274f82e74c6..adcd921d004 100644 --- a/proto/mysqlctl.proto +++ b/proto/mysqlctl.proto @@ -54,3 +54,9 @@ service MysqlCtl { rpc ReinitConfig(ReinitConfigRequest) returns (ReinitConfigResponse) {}; rpc RefreshConfig(RefreshConfigRequest) returns (RefreshConfigResponse) {}; } + +// BackupInfo is the read-only attributes of a mysqlctl/backupstorage.BackupHandle. +message BackupInfo { + string name = 1; + string directory = 2; +} diff --git a/proto/vtctldata.proto b/proto/vtctldata.proto index 0e8fa1a2553..c44da978bb1 100644 --- a/proto/vtctldata.proto +++ b/proto/vtctldata.proto @@ -22,7 +22,13 @@ option go_package = "vitess.io/vitess/go/vt/proto/vtctldata"; package vtctldata; +import "binlogdata.proto"; import "logutil.proto"; +import "mysqlctl.proto"; +import "tabletmanagerdata.proto"; +import "topodata.proto"; +import "vschema.proto"; +import "vttime.proto"; // ExecuteVtctlCommandRequest is the payload for ExecuteVtctlCommand. // timeouts are in nanoseconds. @@ -59,4 +65,368 @@ message MaterializeSettings { // optional parameters. string cell = 6; string tablet_types = 7; + // ExternalCluster is the name of the mounted cluster which has the source keyspace/db for this workflow + // it is of the type + string external_cluster = 8; + +} + +/* Data types for VtctldServer */ + +message Keyspace { + string name = 1; + topodata.Keyspace keyspace = 2; +} + +message Shard { + string keyspace = 1; + string name = 2; + topodata.Shard shard = 3; +} + +// TODO: comment the hell out of this. +message Workflow { + string name = 1; + ReplicationLocation source = 2; + ReplicationLocation target = 3; + int64 max_v_replication_lag = 4; + map shard_streams = 5; + + message ReplicationLocation { + string keyspace = 1; + repeated string shards = 2; + } + + message ShardStream { + repeated Stream streams = 1; + repeated topodata.Shard.TabletControl tablet_controls = 2; + bool is_primary_serving = 3; + } + + message Stream { + int64 id = 1; + string shard = 2; + topodata.TabletAlias tablet = 3; + binlogdata.BinlogSource binlog_source = 4; + string position = 5; + string stop_position = 6; + string state = 7; + string db_name = 8; + vttime.Time transaction_timestamp = 9; + vttime.Time time_updated = 10; + string message = 11; + repeated CopyState copy_states = 12; + + message CopyState { + string table = 1; + string last_pk = 2; + } + } +} + +/* Request/response types for VtctldServer */ + +message ChangeTabletTypeRequest { + topodata.TabletAlias tablet_alias = 1; + topodata.TabletType db_type = 2; + bool dry_run = 3; +} + +message ChangeTabletTypeResponse { + topodata.Tablet before_tablet = 1; + topodata.Tablet after_tablet = 2; + bool was_dry_run = 3; +} + +message CreateKeyspaceRequest { + // Name is the name of the keyspace. + string name = 1; + // Force proceeds with the request even if the keyspace already exists. + bool force = 2; + // AllowEmptyVSchema allows a keyspace to be created with no vschema. + bool allow_empty_v_schema = 3; + + // ShardingColumnName specifies the column to use for sharding operations. + string sharding_column_name = 4; + // ShardingColumnType specifies the type of the column to use for sharding + // operations. + topodata.KeyspaceIdType sharding_column_type = 5; + + // ServedFroms specifies a set of db_type:keyspace pairs used to serve + // traffic for the keyspace. + repeated topodata.Keyspace.ServedFrom served_froms = 6; + + // Type is the type of the keyspace to create. + topodata.KeyspaceType type = 7; + // BaseKeyspace specifies the base keyspace for SNAPSHOT keyspaces. It is + // required to create a SNAPSHOT keyspace. + string base_keyspace = 8; + // SnapshotTime specifies the snapshot time for this keyspace. It is required + // to create a SNAPSHOT keyspace. + vttime.Time snapshot_time = 9; +} + +message CreateKeyspaceResponse { + // Keyspace is the newly-created keyspace. + Keyspace keyspace = 1; +} + +message CreateShardRequest { + // Keyspace is the name of the keyspace to create the shard in. + string keyspace = 1; + // ShardName is the name of the shard to create. E.g. "-" or "-80". + string shard_name = 2; + // Force treats an attempt to create a shard that already exists as a + // non-error. + bool force = 3; + // IncludeParent creates the parent keyspace as an empty BASE keyspace, if it + // doesn't already exist. + bool include_parent = 4; +} + +message CreateShardResponse { + // Keyspace is the created keyspace. It is set only if IncludeParent was + // specified in the request and the parent keyspace needed to be created. + Keyspace keyspace = 1; + // Shard is the newly-created shard object. + Shard shard = 2; + // ShardAlreadyExists is set if Force was specified in the request and the + // shard already existed. + bool shard_already_exists = 3; +} + +message DeleteKeyspaceRequest { + // Keyspace is the name of the keyspace to delete. + string keyspace = 1; + // Recursive causes all shards in the keyspace to be recursively deleted + // before deleting the keyspace. It is an error to call DeleteKeyspace on a + // non-empty keyspace without also specifying Recursive. + bool recursive = 2; +} + +message DeleteKeyspaceResponse { +} + +message DeleteShardsRequest { + // Shards is the list of shards to delete. The nested topodatapb.Shard field + // is not required for DeleteShard, but the Keyspace and Shard fields are. + repeated Shard shards = 1; + // Recursive also deletes all tablets belonging to the shard(s). It is an + // error to call DeleteShard on a non-empty shard without also specificying + // Recursive. + bool recursive = 2; + // EvenIfServing allows a shard to be deleted even if it is serving, which is + // normally an error. Use with caution. + bool even_if_serving = 4; +} + +message DeleteShardsResponse { +} + +message DeleteTabletsRequest { + // TabletAliases is the list of tablets to delete. + repeated topodata.TabletAlias tablet_aliases = 1; + // AllowPrimary allows for the master/primary tablet of a shard to be deleted. + // Use with caution. + bool allow_primary = 2; +} + +message DeleteTabletsResponse { +} + +message FindAllShardsInKeyspaceRequest { + string keyspace = 1; +} + +message FindAllShardsInKeyspaceResponse { + map shards = 1; +} + +message GetBackupsRequest { + string keyspace = 1; + string shard = 2; +} + +message GetBackupsResponse { + repeated mysqlctl.BackupInfo backups = 1; +} + +message GetCellInfoNamesRequest { +} + +message GetCellInfoNamesResponse { + repeated string names = 1; +} + +message GetCellInfoRequest { + string cell = 1; +} + +message GetCellInfoResponse { + topodata.CellInfo cell_info = 1; +} + +message GetCellsAliasesRequest { +} + +message GetCellsAliasesResponse { + map aliases = 1; +} + +message GetKeyspacesRequest { +} + +message GetKeyspacesResponse { + repeated Keyspace keyspaces = 1; +} + +message GetKeyspaceRequest { + string keyspace = 1; +} + +message GetKeyspaceResponse { + Keyspace keyspace = 1; +} + +message GetSchemaRequest { + topodata.TabletAlias tablet_alias = 1; + // Tables is a list of tables for which we should gather information. Each is + // either an exact match, or a regular expression of the form /regexp/. + repeated string tables = 2; + // ExcludeTables is a list of tables to exclude from the result. Each is + // either an exact match, or a regular expression of the form /regexp/. + repeated string exclude_tables = 3; + // IncludeViews specifies whether to include views in the result. + bool include_views = 4; + // TableNamesOnly specifies whether to limit the results to just table names, + // rather than full schema information for each table. + bool table_names_only = 5; + // TableSizesOnly specifies whether to limit the results to just table sizes, + // rather than full schema information for each table. It is ignored if + // TableNamesOnly is set to true. + bool table_sizes_only = 6; +} + +message GetSchemaResponse { + tabletmanagerdata.SchemaDefinition schema = 1; +} + +message GetShardRequest { + string keyspace = 1; + string shard_name = 2; +} + +message GetShardResponse { + Shard shard = 1; +} + +message GetSrvVSchemaRequest { + string cell = 1; +} + +message GetSrvVSchemaResponse { + vschema.SrvVSchema srv_v_schema = 1; +} + +message GetTabletRequest { + topodata.TabletAlias tablet_alias = 1; +} + +message GetTabletResponse { + topodata.Tablet tablet = 1; +} + +message GetTabletsRequest { + // Keyspace is the name of the keyspace to return tablets for. Omit to return + // all tablets. + string keyspace = 1; + // Shard is the name of the shard to return tablets for. This field is ignored + // if Keyspace is not set. + string shard = 2; + // Cells is an optional set of cells to return tablets for. + repeated string cells = 3; +} + +message GetTabletsResponse { + repeated topodata.Tablet tablets = 1; +} + +message GetVSchemaRequest { + string keyspace = 1; +} + +message GetVSchemaResponse { + vschema.Keyspace v_schema = 1; +} + +message GetWorkflowsRequest { + string keyspace = 1; + bool active_only = 2; +} + +message GetWorkflowsResponse { + repeated Workflow workflows = 1; +} + +message RemoveKeyspaceCellRequest { + string keyspace = 1; + string cell = 2; + // Force proceeds even if the cell's topology server cannot be reached. This + // should only be set if a cell has been shut down entirely, and the global + // topology data just needs to be updated. + bool force = 3; + // Recursive also deletes all tablets in that cell belonging to the specified + // keyspace. + bool recursive = 4; +} + +message RemoveKeyspaceCellResponse { + // (TODO:@amason) Consider including the deleted SrvKeyspace object and any + // deleted Tablet objects here. +} + +message RemoveShardCellRequest { + string keyspace = 1; + string shard_name = 2; + string cell = 3; + // Force proceeds even if the cell's topology server cannot be reached. This + // should only be set if a cell has been shut down entirely, and the global + // topology data just needs to be updated. + bool force = 4; + // Recursive also deletes all tablets in that cell belonging to the specified + // keyspace and shard. + bool recursive = 5; +} + +message RemoveShardCellResponse { + // (TODO:@amason) Consider including the deleted SrvKeyspacePartitions objects + // and any deleted Tablet objects here. +} + +message ReparentTabletRequest { + // Tablet is the alias of the tablet that should be reparented under the + // current shard primary. + topodata.TabletAlias tablet = 1; +} + +message ReparentTabletResponse { + // Keyspace is the name of the keyspace the tablet was reparented in. + string keyspace = 1; + // Shard is the name of the shard the tablet was reparented in. + string shard = 2; + // Primary is the alias of the tablet that the tablet was reparented under. + topodata.TabletAlias primary = 3; +} + +message TabletExternallyReparentedRequest { + // Tablet is the alias of the tablet that was promoted externally and should + // be updated to the shard primary in the topo. + topodata.TabletAlias tablet = 1; +} + +message TabletExternallyReparentedResponse { + string keyspace = 1; + string shard = 2; + topodata.TabletAlias new_primary = 3; + topodata.TabletAlias old_primary = 4; } diff --git a/proto/vtctlservice.proto b/proto/vtctlservice.proto index 07cd70b3c3a..54cab4a31d4 100644 --- a/proto/vtctlservice.proto +++ b/proto/vtctlservice.proto @@ -28,3 +28,70 @@ import "vtctldata.proto"; service Vtctl { rpc ExecuteVtctlCommand (vtctldata.ExecuteVtctlCommandRequest) returns (stream vtctldata.ExecuteVtctlCommandResponse) {}; } + +// Service Vtctld exposes gRPC endpoints for each vt command. +service Vtctld { + // ChangeTabletType changes the db type for the specified tablet, if possible. + // This is used primarily to arrange replicas, and it will not convert a + // primary. For that, use InitShardPrimary. + // + // NOTE: This command automatically updates the serving graph. + rpc ChangeTabletType(vtctldata.ChangeTabletTypeRequest) returns (vtctldata.ChangeTabletTypeResponse) {}; + // CreateKeyspace creates the specified keyspace in the topology. For a + // SNAPSHOT keyspace, the request must specify the name of a base keyspace, + // as well as a snapshot time. + rpc CreateKeyspace(vtctldata.CreateKeyspaceRequest) returns (vtctldata.CreateKeyspaceResponse) {}; + // CreateShard creates the specified shard in the topology. + rpc CreateShard(vtctldata.CreateShardRequest) returns (vtctldata.CreateShardResponse) {}; + // DeleteKeyspace deletes the specified keyspace from the topology. In + // recursive mode, it also recursively deletes all shards in the keyspace. + // Otherwise, the keyspace must be empty (have no shards), or DeleteKeyspace + // returns an error. + rpc DeleteKeyspace(vtctldata.DeleteKeyspaceRequest) returns (vtctldata.DeleteKeyspaceResponse) {}; + // DeleteShards deletes the specified shards from the topology. In recursive + // mode, it also deletes all tablets belonging to the shard. Otherwise, the + // shard must be empty (have no tablets) or DeleteShards returns an error for + // that shard. + rpc DeleteShards(vtctldata.DeleteShardsRequest) returns (vtctldata.DeleteShardsResponse) {}; + // DeleteTablets deletes one or more tablets from the topology. + rpc DeleteTablets(vtctldata.DeleteTabletsRequest) returns (vtctldata.DeleteTabletsResponse) {}; + // FindAllShardsInKeyspace returns a map of shard names to shard references + // for a given keyspace. + rpc FindAllShardsInKeyspace(vtctldata.FindAllShardsInKeyspaceRequest) returns (vtctldata.FindAllShardsInKeyspaceResponse) {}; + // GetBackups returns all the backups for a shard. + rpc GetBackups(vtctldata.GetBackupsRequest) returns (vtctldata.GetBackupsResponse) {}; + // GetCellInfoNames returns all the cells for which we have a CellInfo object, + // meaning we have a topology service registered. + rpc GetCellInfoNames(vtctldata.GetCellInfoNamesRequest) returns (vtctldata.GetCellInfoNamesResponse) {}; + // GetCellInfo returns the information for a cell. + rpc GetCellInfo(vtctldata.GetCellInfoRequest) returns (vtctldata.GetCellInfoResponse) {}; + // GetCellsAliases returns a mapping of cell alias to cells identified by that + // alias. + rpc GetCellsAliases(vtctldata.GetCellsAliasesRequest) returns (vtctldata.GetCellsAliasesResponse) {}; + // GetKeyspace reads the given keyspace from the topo and returns it. + rpc GetKeyspace(vtctldata.GetKeyspaceRequest) returns (vtctldata.GetKeyspaceResponse) {}; + // GetKeyspaces returns the keyspace struct of all keyspaces in the topo. + rpc GetKeyspaces(vtctldata.GetKeyspacesRequest) returns (vtctldata.GetKeyspacesResponse) {}; + // GetSchema returns the schema for a tablet, or just the schema for the + // specified tables in that tablet. + rpc GetSchema(vtctldata.GetSchemaRequest) returns (vtctldata.GetSchemaResponse) {}; + // GetShard returns information about a shard in the topology. + rpc GetShard(vtctldata.GetShardRequest) returns (vtctldata.GetShardResponse) {}; + // GetSrvVSchema returns a the SrvVSchema for a cell. + rpc GetSrvVSchema(vtctldata.GetSrvVSchemaRequest) returns (vtctldata.GetSrvVSchemaResponse) {}; + // GetTablet returns information about a tablet. + rpc GetTablet(vtctldata.GetTabletRequest) returns (vtctldata.GetTabletResponse) {}; + // GetTablets returns tablets, optionally filtered by keyspace and shard. + rpc GetTablets(vtctldata.GetTabletsRequest) returns (vtctldata.GetTabletsResponse) {}; + // GetVSchema returns the vschema for a keyspace. + rpc GetVSchema(vtctldata.GetVSchemaRequest) returns (vtctldata.GetVSchemaResponse) {}; + // GetWorkflows returns a list of workflows for the given keyspace. + rpc GetWorkflows(vtctldata.GetWorkflowsRequest) returns (vtctldata.GetWorkflowsResponse) {}; + // RemoveKeyspaceCell removes the specified cell from the Cells list for all + // shards in the specified keyspace, as well as from the SrvKeyspace for that + // keyspace in that cell. + rpc RemoveKeyspaceCell(vtctldata.RemoveKeyspaceCellRequest) returns (vtctldata.RemoveKeyspaceCellResponse) {}; + // RemoveShardCell removes the specified cell from the specified shard's Cells + // list. + rpc RemoveShardCell(vtctldata.RemoveShardCellRequest) returns (vtctldata.RemoveShardCellResponse) {}; +}