diff --git a/cmd/admin/admin.go b/cmd/admin/admin.go new file mode 100644 index 00000000..c2cc8f51 --- /dev/null +++ b/cmd/admin/admin.go @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 admin + +import ( + "context" + "fmt" + "os" + "path/filepath" +) + +import ( + "github.com/spf13/cobra" +) + +import ( + "github.com/arana-db/arana/cmd/cmds" + "github.com/arana-db/arana/pkg/admin" + _ "github.com/arana-db/arana/pkg/admin/router" + "github.com/arana-db/arana/pkg/boot" + "github.com/arana-db/arana/pkg/constants" + "github.com/arana-db/arana/pkg/util/log" +) + +const ( + _keyPort = "port" + _defaultPort = 8080 +) + +func init() { + cmd := &cobra.Command{ + Use: "admin", + Short: "admin", + Example: "arana admin -c bootstrap.yaml -p 8080", + RunE: run, + } + cmd.PersistentFlags(). + StringP(constants.ConfigPathKey, "c", os.Getenv(constants.EnvBootstrapPath), "bootstrap configuration file path") + cmd.PersistentFlags(). + Uint16P(_keyPort, "p", _defaultPort, "listen port") + + cmds.Handle(func(root *cobra.Command) { + root.AddCommand(cmd) + }) +} + +func Run(bootstrapPath string, addr string) error { + discovery := boot.NewDiscovery(bootstrapPath) + if err := discovery.Init(context.Background()); err != nil { + log.Fatal("start admin api server failed: %v", err) + return err + } + adminServer := admin.New(discovery) + return adminServer.Listen(addr) +} + +func run(cmd *cobra.Command, args []string) error { + _ = args + btPath, _ := cmd.PersistentFlags().GetString(constants.ConfigPathKey) + port, _ := cmd.PersistentFlags().GetUint16("port") + if len(btPath) < 1 { + // search bootstrap yaml + for _, path := range constants.GetConfigSearchPathList() { + btPath = filepath.Join(path, "bootstrap.yaml") + if _, err := os.Stat(btPath); err == nil { + break + } + btPath = filepath.Join(path, "bootstrap.yml") + if _, err := os.Stat(btPath); err == nil { + break + } + } + } + + return Run(btPath, fmt.Sprintf(":%d", port)) +} diff --git a/cmd/start/start.go b/cmd/start/start.go index 28d23214..6ece435b 100644 --- a/cmd/start/start.go +++ b/cmd/start/start.go @@ -73,13 +73,13 @@ func Run(bootstrapConfigPath string) { // print slogan fmt.Printf("\033[92m%s\033[0m\n", slogan) // 92m: light green - provider := boot.NewProvider(bootstrapConfigPath) - if err := boot.Boot(context.Background(), provider); err != nil { + discovery := boot.NewDiscovery(bootstrapConfigPath) + if err := boot.Boot(context.Background(), discovery); err != nil { log.Fatal("start failed: %v", err) return } - filters, err := provider.ListFilters(context.Background()) + filters, err := discovery.ListFilters(context.Background()) if err != nil { log.Fatal("start failed: %v", err) return @@ -99,7 +99,7 @@ func Run(bootstrapConfigPath string) { propeller := server.NewServer() - listenersConf, err := provider.ListListeners(context.Background()) + listenersConf, err := discovery.ListListeners(context.Background()) if err != nil { log.Fatal("start failed: %v", err) return diff --git a/cmd/tools/tools.go b/cmd/tools/tools.go index 195da51f..ce2b9209 100644 --- a/cmd/tools/tools.go +++ b/cmd/tools/tools.go @@ -61,8 +61,8 @@ func init() { func Run(cmd *cobra.Command, args []string) { _, _ = cmd, args - provider := boot.NewProvider(importBootConfPath) - if err := provider.Init(context.Background()); err != nil { + discovery := boot.NewDiscovery(importBootConfPath) + if err := discovery.Init(context.Background()); err != nil { log.Fatal("init failed: %+v", err) return } @@ -73,7 +73,7 @@ func Run(cmd *cobra.Command, args []string) { return } - c := provider.GetConfigCenter() + c := discovery.GetConfigCenter() if err := c.ImportConfiguration(cfg); err != nil { log.Fatal("persist config to config.store failed: %+v", err) diff --git a/example/admin_server/main.go b/example/admin_server/main.go new file mode 100644 index 00000000..3079347a --- /dev/null +++ b/example/admin_server/main.go @@ -0,0 +1,11 @@ +package main + +import ( + "github.com/arana-db/arana/cmd/admin" + "github.com/arana-db/arana/testdata" +) + +func main() { + bootstrap := testdata.Path("../conf/bootstrap.yaml") + _ = admin.Run(bootstrap, ":8080") +} diff --git a/go.mod b/go.mod index f3ffabaa..48e21cf0 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,8 @@ require ( github.com/cespare/xxhash/v2 v2.1.2 github.com/dop251/goja v0.0.0-20220422102209-3faab1d8f20e github.com/dubbogo/gost v1.12.3 - github.com/go-playground/validator/v10 v10.10.1 + github.com/gin-gonic/gin v1.8.1 + github.com/go-playground/validator/v10 v10.11.0 github.com/go-sql-driver/mysql v1.6.0 github.com/golang/mock v1.5.0 github.com/hashicorp/golang-lru v0.5.4 @@ -19,7 +20,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.11.0 github.com/spf13/cobra v1.2.1 - github.com/stretchr/testify v1.7.1 + github.com/stretchr/testify v1.7.2 github.com/testcontainers/testcontainers-go v0.12.0 github.com/tidwall/gjson v1.14.0 go.etcd.io/etcd/api/v3 v3.5.1 @@ -32,7 +33,7 @@ require ( golang.org/x/exp v0.0.0-20220613132600-b0d781184e0d golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/text v0.3.7 - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b + gopkg.in/yaml.v3 v3.0.1 ) require ( @@ -55,12 +56,14 @@ require ( github.com/docker/go-units v0.4.0 // indirect github.com/dustin/go-humanize v1.0.0 // indirect github.com/form3tech-oss/jwt-go v3.2.2+incompatible // indirect + github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-errors/errors v1.0.1 // indirect github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-playground/locales v0.14.0 // indirect github.com/go-playground/universal-translator v0.18.0 // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect + github.com/goccy/go-json v0.9.10 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect @@ -74,20 +77,22 @@ require ( github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jonboulle/clockwork v0.2.2 // indirect - github.com/json-iterator/go v1.1.11 // indirect + github.com/json-iterator/go v1.1.12 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/magiconair/properties v1.8.5 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect github.com/mattn/go-runewidth v0.0.9 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/moby/sys/mount v0.2.0 // indirect github.com/moby/sys/mountinfo v0.5.0 // indirect github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.1 // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.1 // indirect github.com/opencontainers/runc v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.0.2 // indirect github.com/pingcap/errors v0.11.5-0.20210425183316-da1aaba5fb63 // indirect github.com/pingcap/log v0.0.0-20210625125904-98ed8e2eb1c7 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect @@ -100,6 +105,7 @@ require ( github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 // indirect + github.com/ugorji/go/codec v1.2.7 // indirect github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect go.etcd.io/bbolt v1.3.5 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.0 // indirect @@ -108,13 +114,13 @@ require ( go.etcd.io/etcd/raft/v3 v3.5.0-alpha.0 // indirect go.opencensus.io v0.23.0 // indirect go.uber.org/multierr v1.6.0 // indirect - golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 // indirect - golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect - golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27 // indirect + golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect + golang.org/x/net v0.0.0-20220725212005-46097bf591d3 // indirect + golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect google.golang.org/genproto v0.0.0-20211104193956-4c6863e31247 // indirect google.golang.org/grpc v1.42.0 // indirect - google.golang.org/protobuf v1.27.1 // indirect + google.golang.org/protobuf v1.28.0 // indirect gopkg.in/ini.v1 v1.62.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index c3cc775e..341ffa65 100644 --- a/go.sum +++ b/go.sum @@ -335,6 +335,10 @@ github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JY github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= +github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -369,8 +373,8 @@ github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= -github.com/go-playground/validator/v10 v10.10.1 h1:uA0+amWMiglNZKZ9FJRKUAe9U3RX91eVn1JYXMWt7ig= -github.com/go-playground/validator/v10 v10.10.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= +github.com/go-playground/validator/v10 v10.11.0 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw= +github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= @@ -379,6 +383,8 @@ github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/goccy/go-json v0.9.10 h1:hCeNmprSNLB8B8vQKWl6DpuH0t60oEs+TAk9a7CScKc= +github.com/goccy/go-json v0.9.10/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= @@ -555,8 +561,9 @@ 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/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= @@ -610,6 +617,8 @@ github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= @@ -644,8 +653,9 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c h1:nXxl5PrvVm2L/wCy8dQu6DMTwH4oIuGN8GJDAlqDdVE= github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= @@ -726,6 +736,8 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml/v2 v2.0.2 h1:+jQXlF3scKIcSEKkdHzXhCTDLPFi5r1wnK6yPS+49Gw= +github.com/pelletier/go-toml/v2 v2.0.2/go.mod h1:MovirKjgVRESsAvNZlAjtFwV867yGuwRkXbG66OzopI= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= @@ -867,8 +879,9 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= @@ -893,6 +906,9 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966/go.mod h1 github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 h1:uruHq4dN7GR16kFc5fp3d1RIYzJW5onx8Ybykw2YQFA= github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3/go.mod h1:QDlpd3qS71vYtakd2hmdpqhJ9nwv6mD6A30bQ1BPBFE= +github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= +github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= @@ -996,8 +1012,9 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20181106170214-d68db9428509/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1090,8 +1107,9 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211108170745-6635138e15ea/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220725212005-46097bf591d3 h1:2yWTtPWWRcISTw3/o+s/Y4UOMnQL71DWyToOANFusCg= +golang.org/x/net v0.0.0-20220725212005-46097bf591d3/go.mod h1:AaygXjzTFtRAg2ttMY5RMuhpJ3cNnI0XpyFJD1iQRSM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1204,13 +1222,15 @@ golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211109184856-51b60fd695b3/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27 h1:XDXtA5hveEEV8JB2l7nhMTp3t3cHp9ZpwcdjqyEWLlo= golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1420,8 +1440,9 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1460,8 +1481,9 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= diff --git a/pkg/admin/admin.api.yaml b/pkg/admin/admin.api.yaml new file mode 100644 index 00000000..8c8dda9a --- /dev/null +++ b/pkg/admin/admin.api.yaml @@ -0,0 +1,361 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +openapi: 3.0.3 +info: + title: Arana + description: Arana + version: 1.0.0 +servers: + - url: 'http://127.0.0.1:8080/' +paths: + /tenants: + get: + operationId: listTenants + summary: List all tenants + responses: + '200': + description: All Tenants + content: + application/json: + schema: + $ref: '#/components/schemas/Tenants' + + post: + operationId: createTenant + summary: Create a tenant + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Tenant' + responses: + '201': + description: OK + + /tenants/{tenantName}: + get: + operationId: getTenant + summary: Get a tenant + responses: + '200': + description: Single Tenant + content: + application/json: + schema: + $ref: '#/components/schemas/Tenant' + delete: + operationId: deleteTenant + summary: Delete a tenant + responses: + '204': + description: NONE + put: + operationId: putTenant + summary: Update a tenant + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Tenant' + responses: + '200': + description: OK + + /tenants/{tenantName}/nodes: + get: + operationId: listNodes + summary: List mysql nodes + responses: + '200': + description: All MySQL Nodes + content: + application/json: + schema: + $ref: '#/components/schemas/Nodes' + + post: + operationId: createNode + summary: Create mysql node + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Node' + responses: + '200': + description: OK + + /tenants/{tenantName}/nodes/{nodeName}: + get: + operationId: getNode + summary: Get a mysql node + responses: + '200': + description: Single MySQL Node + content: + application/json: + schema: + $ref: '#/components/schemas/Node' + delete: + operationId: deleteNode + summary: Delete a mysql node + responses: + '204': + description: NONE + + put: + operationId: putNode + summary: Update a mysql node + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Node' + responses: + '200': + description: OK + + /tenants/{tenantName}/groups: + post: + operationId: createGroup + summary: Create a DB group + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Group' + responses: + '201': + description: OK + + get: + operationId: listGroups + summary: List all DB groups + responses: + '200': + description: All groups + content: + application/json: + schema: + $ref: '#/components/schemas/Groups' + + /tenants/{tenantName}/groups/{groupName}: + get: + operationId: getGroup + summary: Get a DB group + responses: + '200': + description: Single DB group + content: + application/json: + schema: + $ref: '#/components/schemas/Group' + put: + operationId: putGroup + summary: Update a DB group + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Group' + responses: + '200': + description: OK + + delete: + operationId: deleteGroup + summary: Delete a DB group + responses: + '204': + description: NONE + + /tenants/{tenantName}/clusters: + post: + operationId: createCluster + summary: Create a cluster + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Cluster' + responses: + '200': + description: OK + + get: + operationId: listClusters + summary: List all clusters + responses: + '200': + description: All Clusters + content: + application/json: + schema: + $ref: '#/components/schemas/Clusters' + + /tenants/{tenantName}/clusters/{clusterName}: + get: + operationId: getCluster + summary: Get a cluster + responses: + '200': + description: Single Cluster + content: + application/json: + schema: + $ref: '#/components/schemas/Cluster' + + put: + operationId: putCluster + summary: Update a cluster + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Cluster' + responses: + '200': + description: OK + + delete: + operationId: deleteCluster + summary: Delete a cluster + responses: + '204': + description: NONE + +components: + schemas: + Tenant: + type: object + properties: + name: + type: string + users: + type: array + items: + type: object + properties: + username: + type: string + password: + type: string + example: + name: "foobar" + users: + - username: "tom" + password: "12345678" + - username: "john" + password: "12345678" + Tenants: + type: array + items: + $ref: '#/components/schemas/Tenant' + + Node: + type: object + required: + - name + - host + - username + - password + - database + - weight + properties: + name: + type: string + host: + type: string + port: + type: integer + username: + type: string + password: + type: string + database: + type: string + weight: + type: string + parameters: + type: object + example: + name: mysql-axfwq87 + host: 1.2.3.4 + port: 3306 + username: root + password: 12345678 + database: employees_0000 + weight: r10w10 + + Nodes: + type: array + items: + $ref: '#/components/schemas/Node' + + Group: + type: object + required: + - name + - nodes + properties: + name: + type: string + nodes: + type: array + items: + type: string + example: + name: employees_0000 + nodes: + - mysql-fwijfo8 + - mysql-we7nvil + - mysql-vjm24if + + Groups: + type: array + items: + $ref: '#/components/schemas/Group' + + Cluster: + type: object + properties: + name: + type: string + type: + type: string + groups: + type: array + items: + type: string + example: + name: employees + type: mysql + groups: + - employees_0000 + - employees_0001 + - employees_0002 + - employees_0003 + - employees_0004 + - employees_0005 + - employees_0006 + - employees_0007 + + Clusters: + type: array + items: + $ref: '#/components/schemas/Cluster' \ No newline at end of file diff --git a/pkg/admin/admin.go b/pkg/admin/admin.go new file mode 100644 index 00000000..0951329c --- /dev/null +++ b/pkg/admin/admin.go @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 admin + +import ( + "context" + "errors" + "io" + "net" + "net/http" + "os" + "strings" +) + +import ( + "github.com/gin-gonic/gin" + + perrors "github.com/pkg/errors" + + uatomic "go.uber.org/atomic" +) + +import ( + "github.com/arana-db/arana/pkg/boot" + "github.com/arana-db/arana/pkg/constants" +) + +const K = "ARANA_ADMIN_SERVICE" + +var NotFoundError = errors.New("resource not found") + +var _hooks []Hook + +type Hook func(router gin.IRoutes) + +func Register(hook Hook) { + _hooks = append(_hooks, hook) +} + +func init() { + switch strings.ToLower(os.Getenv(constants.EnvDevelopEnvironment)) { + case "1", "true", "yes", "on": + gin.SetMode(gin.DebugMode) + default: + gin.SetMode(gin.ReleaseMode) + } +} + +type Service = boot.ConfigProvider + +type Server struct { + l net.Listener + engine *gin.Engine + service Service + started uatomic.Bool +} + +func New(service Service) *Server { + return &Server{ + service: service, + engine: gin.New(), + } +} + +func (srv *Server) Close() error { + if srv.l != nil { + return srv.l.Close() + } + return nil +} + +func (srv *Server) Listen(addr string) error { + if !srv.started.CAS(false, true) { + return io.EOF + } + + var ( + c net.ListenConfig + err error + ) + + srv.engine.Use(func(c *gin.Context) { + c.Set(K, srv.service) + c.Next() + }) + srv.engine.Use(gin.Logger()) + srv.engine.Use(gin.Recovery()) + srv.engine.Use( + ErrorHandler( + Map(NotFoundError). + ToResponse(func(c *gin.Context, err error) { + c.Status(http.StatusNotFound) + _, _ = c.Writer.WriteString(err.Error()) + }), + )) + + for _, hook := range _hooks { + hook(srv.engine) + } + + if srv.l, err = c.Listen(context.Background(), "tcp", addr); err != nil { + return perrors.WithStack(err) + } + return srv.engine.RunListener(srv.l) +} + +// GetService returns Service from gin context. +func GetService(c *gin.Context) Service { + v, _ := c.Get(K) + return v.(Service) +} diff --git a/pkg/admin/middleware.go b/pkg/admin/middleware.go new file mode 100644 index 00000000..8282d69a --- /dev/null +++ b/pkg/admin/middleware.go @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 admin + +import ( + "reflect" +) + +import ( + "github.com/gin-gonic/gin" +) + +// ErrorHandler is middleware that enables you to configure error handling from a centralized place via its fluent API. +// Copyright: https://github.com/josephwoodward/gin-errorhandling +func ErrorHandler(errMap ...*errorMapping) gin.HandlerFunc { + return func(context *gin.Context) { + context.Next() + + lastErr := context.Errors.Last() + if lastErr == nil { + return + } + + for _, e := range errMap { + for _, e2 := range e.fromErrors { + if lastErr.Err == e2 { + e.toResponse(context, lastErr.Err) + } else if isType(lastErr.Err, e2) { + e.toResponse(context, lastErr.Err) + } + } + } + } +} + +func isType(a, b interface{}) bool { + return reflect.TypeOf(a) == reflect.TypeOf(b) +} + +type errorMapping struct { + fromErrors []error + toStatusCode int + toResponse func(ctx *gin.Context, err error) +} + +// ToStatusCode specifies the status code returned to a caller when the error is handled. +func (r *errorMapping) ToStatusCode(statusCode int) *errorMapping { + r.toStatusCode = statusCode + r.toResponse = func(ctx *gin.Context, err error) { + ctx.Status(statusCode) + } + return r +} + +// ToResponse provides more control over the returned response when an error is matched. +func (r *errorMapping) ToResponse(response func(ctx *gin.Context, err error)) *errorMapping { + r.toResponse = response + return r +} + +// Map enables you to map errors to a given response status code or response body. +func Map(err ...error) *errorMapping { + return &errorMapping{ + fromErrors: err, + } +} diff --git a/pkg/admin/router/tenants.go b/pkg/admin/router/tenants.go new file mode 100644 index 00000000..c13e8983 --- /dev/null +++ b/pkg/admin/router/tenants.go @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 router + +import ( + "context" +) + +import ( + "github.com/gin-gonic/gin" +) + +import ( + "github.com/arana-db/arana/pkg/admin" +) + +func init() { + admin.Register(func(router gin.IRoutes) { + router.GET("/tenants", ListTenants) + }) +} + +func ListTenants(c *gin.Context) { + service := admin.GetService(c) + tenants, err := service.ListTenants(context.Background()) + if err != nil { + _ = c.Error(err) + return + } + c.JSON(200, tenants) +} diff --git a/pkg/boot/discovery.go b/pkg/boot/discovery.go index a3a0e9d0..0866def5 100644 --- a/pkg/boot/discovery.go +++ b/pkg/boot/discovery.go @@ -74,42 +74,53 @@ type Cluster struct { Type config.DataSourceType } -type Discovery interface { - // Init init discovery with context - Init(ctx context.Context) error +type ConfigProvider interface { // ListTenants list tenants name ListTenants(ctx context.Context) ([]string, error) + // GetTenant returns the tenant info GetTenant(ctx context.Context, tenant string) (*config.Tenant, error) - // ListListeners lists the listener names - ListListeners(ctx context.Context) ([]*config.Listener, error) - // ListFilters list the filter names - ListFilters(ctx context.Context) ([]*config.Filter, error) - // ListClusters lists the cluster names. ListClusters(ctx context.Context) ([]string, error) - // GetClusterObject returns the dataSourceCluster object + + // GetDataSourceCluster returns the dataSourceCluster object GetDataSourceCluster(ctx context.Context, cluster string) (*config.DataSourceCluster, error) + // GetCluster returns the cluster info GetCluster(ctx context.Context, cluster string) (*Cluster, error) + // ListGroups lists the group names. ListGroups(ctx context.Context, cluster string) ([]string, error) // ListNodes lists the node names. ListNodes(ctx context.Context, cluster, group string) ([]string, error) + // GetNode returns the node info. GetNode(ctx context.Context, cluster, group, node string) (*config.Node, error) // ListTables lists the table names. ListTables(ctx context.Context, cluster string) ([]string, error) + // GetTable returns the table info. GetTable(ctx context.Context, cluster, table string) (*rule.VTable, error) - // GetConfigCenter + // GetConfigCenter returns the config center. GetConfigCenter() *config.Center } +type Discovery interface { + ConfigProvider + // ListListeners lists the listener names + ListListeners(ctx context.Context) ([]*config.Listener, error) + + // ListFilters list the filter names + ListFilters(ctx context.Context) ([]*config.Filter, error) + + // Init initializes discovery with context + Init(ctx context.Context) error +} + type discovery struct { path string options *BootOptions @@ -598,7 +609,7 @@ func parseTable(input string) (db, tbl string, err error) { return } -func NewProvider(path string) Discovery { +func NewDiscovery(path string) Discovery { return &discovery{ path: path, } diff --git a/pkg/boot/discovery_test.go b/pkg/boot/discovery_test.go index a618eb15..472c9164 100644 --- a/pkg/boot/discovery_test.go +++ b/pkg/boot/discovery_test.go @@ -31,7 +31,7 @@ import ( ) func TestFileProvider(t *testing.T) { - provider := NewProvider(testdata.Path("fake_bootstrap.yaml")) + provider := NewDiscovery(testdata.Path("fake_bootstrap.yaml")) err := Boot(context.Background(), provider) assert.NoError(t, err, "should init ok") diff --git a/pkg/config/api_test.go b/pkg/config/api_test.go index 0b6c2e53..b193df11 100644 --- a/pkg/config/api_test.go +++ b/pkg/config/api_test.go @@ -36,7 +36,7 @@ import ( func TestGetStoreOperate(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - //mockStore := NewMockStoreOperate(ctrl) + // mockStore := NewMockStoreOperate(ctrl) tests := []struct { name string want config.StoreOperate diff --git a/pkg/config/config.go b/pkg/config/config.go index e1343975..4200b30c 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -104,13 +104,15 @@ func NewCenter(options ConfigOptions) (*Center, error) { } return &Center{ - confHolder: atomic.Value{}, - lock: sync.RWMutex{}, storeOperate: operate, observers: make([]Observer, 0, 2), }, nil } +func (c *Center) GetStoreOperate() StoreOperate { + return c.storeOperate +} + func (c *Center) Close() error { if err := c.storeOperate.Close(); err != nil { return err diff --git a/pkg/config/file/file_test.go b/pkg/config/file/file_test.go index 987f65c9..16a69c4d 100644 --- a/pkg/config/file/file_test.go +++ b/pkg/config/file/file_test.go @@ -27,8 +27,10 @@ import ( "github.com/arana-db/arana/testdata" ) -var FakeConfigPath = testdata.Path("fake_config.yaml") -var EmptyConfigPath = testdata.Path("fake_empty_config.yaml") +var ( + FakeConfigPath = testdata.Path("fake_config.yaml") + EmptyConfigPath = testdata.Path("fake_empty_config.yaml") +) var jsonConfig = `{ "kind":"ConfigMap", diff --git a/pkg/config/model_test.go b/pkg/config/model_test.go index 6afb63db..74b74cc7 100644 --- a/pkg/config/model_test.go +++ b/pkg/config/model_test.go @@ -77,8 +77,8 @@ func TestDataSourceClustersConf(t *testing.T) { assert.Equal(t, "123456", node.Password) assert.Equal(t, "employees_0000", node.Database) assert.Equal(t, "r10w10", node.Weight) - //assert.Len(t, node.Labels, 1) - //assert.NotNil(t, node.ConnProps) + // assert.Len(t, node.Labels, 1) + // assert.NotNil(t, node.ConnProps) } func TestShardingRuleConf(t *testing.T) { @@ -102,8 +102,8 @@ func TestShardingRuleConf(t *testing.T) { assert.Equal(t, "employees_${0000..0003}", table.Topology.DbPattern) assert.Equal(t, "student_${0000..0031}", table.Topology.TblPattern) - //assert.Equal(t, "employee_0000", table.ShadowTopology.DbPattern) - //assert.Equal(t, "__test_student_${0000...0007}", table.ShadowTopology.TblPattern) + // assert.Equal(t, "employee_0000", table.ShadowTopology.DbPattern) + // assert.Equal(t, "__test_student_${0000...0007}", table.ShadowTopology.TblPattern) assert.Len(t, table.Attributes, 2) } diff --git a/pkg/sequence/snowflake/snowflake.go b/pkg/sequence/snowflake/snowflake.go index 8e724dd8..d208c848 100644 --- a/pkg/sequence/snowflake/snowflake.go +++ b/pkg/sequence/snowflake/snowflake.go @@ -23,8 +23,9 @@ import ( "sync" "sync/atomic" "time" +) - "github.com/arana-db/arana/pkg/util/log" +import ( "go.uber.org/zap" ) @@ -33,6 +34,7 @@ import ( "github.com/arana-db/arana/pkg/runtime" rcontext "github.com/arana-db/arana/pkg/runtime/context" "github.com/arana-db/arana/pkg/util/identity" + "github.com/arana-db/arana/pkg/util/log" ) const SequencePluginName = "snowflake"