diff --git a/.github/workflows/github-actions.yml b/.github/workflows/github-actions.yml index 8b64eb047..f0cff48a1 100644 --- a/.github/workflows/github-actions.yml +++ b/.github/workflows/github-actions.yml @@ -35,7 +35,7 @@ jobs: # If you want to matrix build , you can append the following list. matrix: go_version: - - 1.14 + - 1.15 os: - ubuntu-latest diff --git a/.travis.yml b/.travis.yml index 2fbb7a870..118731ac1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,7 +19,7 @@ language: go go: - - "1.14" + - "1.15" env: - GO111MODULE=on diff --git a/cmd/admin/admin.go b/cmd/admin/admin.go deleted file mode 100644 index 5ac09c4b0..000000000 --- a/cmd/admin/admin.go +++ /dev/null @@ -1,193 +0,0 @@ -/* - * 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 main - -import ( - "io/ioutil" - "net/http" - "os" - "strconv" - "strings" - "time" -) - -import ( - fc "github.com/dubbogo/dubbo-go-pixiu-filter/pkg/api/config" - perrors "github.com/pkg/errors" - "github.com/urfave/cli" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/common/yaml" - "github.com/apache/dubbo-go-pixiu/pkg/logger" - etcdv3 "github.com/apache/dubbo-go-pixiu/pkg/remoting/etcd3" -) - -// AdminBootstrap admin bootstrap config -type AdminBootstrap struct { - Server ServerConfig `yaml:"server" json:"server" mapstructure:"server"` - EtcdConfig EtcdConfig `yaml:"etcd" json:"etcd" mapstructure:"etcd"` -} - -// ServerConfig admin http server config -type ServerConfig struct { - Address string `yaml:"address" json:"address" mapstructure:"address"` -} - -// EtcdConfig admin etcd client config -type EtcdConfig struct { - Address string `yaml:"address" json:"admin" mapstructure:"admin"` - Path string `yaml:"path" json:"path" mapstructure:"path"` -} - -var ( - cmdStart = cli.Command{ - Name: "start", - Usage: "start dubbogo pixiu admin", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "config, c", - Usage: "Load configuration from `FILE`", - EnvVar: "PROXY_ADMIN_CONFIG", - Value: "configs/admin_config.yaml", - }, - }, - Action: func(c *cli.Context) error { - configPath := c.String("config") - _, err := LoadAPIConfigFromFile(configPath) - if err != nil { - logger.Errorf("load admin config error:%+v", err) - return err - } - Start() - return nil - }, - } - - cmdStop = cli.Command{ - Name: "stop", - Usage: "stop dubbogo pixiu admin", - Action: func(c *cli.Context) error { - return nil - }, - } -) - -// Version admin version -const Version = "0.1.0" - -func newAdminApp(startCmd *cli.Command) *cli.App { - app := cli.NewApp() - app.Name = "dubbogo pixiu admin" - app.Version = Version - app.Compiled = time.Now() - app.Copyright = "(c) " + strconv.Itoa(time.Now().Year()) + " Dubbogo" - app.Usage = "Dubbogo pixiu admin" - app.Flags = cmdStart.Flags - - // commands - app.Commands = []cli.Command{ - cmdStart, - cmdStop, - } - - // action - app.Action = func(c *cli.Context) error { - if c.NumFlags() == 0 { - return cli.ShowAppHelp(c) - } - return startCmd.Action.(func(c *cli.Context) error)(c) - } - - return app -} - -// LoadAPIConfigFromFile load config from file -func LoadAPIConfigFromFile(path string) (*AdminBootstrap, error) { - if len(path) == 0 { - return nil, perrors.Errorf("Config file not specified") - } - adminBootstrap := &AdminBootstrap{} - err := yaml.UnmarshalYMLConfig(path, adminBootstrap) - if err != nil { - return nil, perrors.Errorf("unmarshalYmlConfig error %v", perrors.WithStack(err)) - } - bootstrap = adminBootstrap - return adminBootstrap, nil -} - -func main() { - app := newAdminApp(&cmdStart) - // ignore error so we don't exit non-zero and break gfmrun README example tests - _ = app.Run(os.Args) -} - -var ( - client *etcdv3.Client - bootstrap *AdminBootstrap -) - -// Start start init etcd client and start admin http server -func Start() { - client = etcdv3.NewConfigClient( - etcdv3.WithName(etcdv3.RegistryETCDV3Client), - etcdv3.WithTimeout(10*time.Second), - etcdv3.WithEndpoints(strings.Split(bootstrap.EtcdConfig.Address, ",")...), - ) - defer client.Close() - - http.HandleFunc("/config/api", GetAPIConfig) - http.HandleFunc("/config/api/set", SetAPIConfig) - - http.ListenAndServe(bootstrap.Server.Address, nil) -} - -// GetAPIConfig handle get api config http request -func GetAPIConfig(w http.ResponseWriter, req *http.Request) { - config, err := client.Get(bootstrap.EtcdConfig.Path) - if err != nil { - logger.Errorf("GetAPIConfig err, %v\n", err) - w.Write([]byte("Error")) - } - w.Write([]byte(config)) -} - -// SetAPIConfig handle modify api config http request -func SetAPIConfig(w http.ResponseWriter, req *http.Request) { - body, err := ioutil.ReadAll(req.Body) - if err != nil { - logger.Errorf("read body err, %v\n", err) - return - } - // validate the api config - apiConf := &fc.APIConfig{} - err = yaml.UnmarshalYML([]byte(body), apiConf) - - if err != nil { - logger.Warnf("read body err, %v\n", err) - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - setErr := client.Update(bootstrap.EtcdConfig.Path, string(body)) - if setErr != nil { - logger.Warnf("update etcd error, %v\n", err) - w.Write([]byte(setErr.Error())) - } - w.Write([]byte("Success")) -} diff --git a/cmd/pixiu/control.go b/cmd/pixiu/control.go index 25e72f994..c07b70fb1 100644 --- a/cmd/pixiu/control.go +++ b/cmd/pixiu/control.go @@ -92,7 +92,7 @@ var ( initFromRemote := false if bootstrap.GetAPIMetaConfig() != nil { if _, err := config.LoadAPIConfig(bootstrap.GetAPIMetaConfig()); err != nil { - logger.Errorf("load api config from etcd error:%+v", err) + logger.Warnf("load api config from etcd error:%+v", err) } else { initFromRemote = true } diff --git a/configs/admin_config.yaml b/configs/admin_config.yaml deleted file mode 100644 index 897236634..000000000 --- a/configs/admin_config.yaml +++ /dev/null @@ -1,23 +0,0 @@ -# -# 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. -# -server: - address: 127.0.0.1:8080 -etcd: - address: 127.0.0.1:2379 - path: /pixiu/config/api diff --git a/configs/api_config.yaml b/configs/api_config.yaml index 7b0f191b8..7650eae4b 100644 --- a/configs/api_config.yaml +++ b/configs/api_config.yaml @@ -22,6 +22,7 @@ resources: - path: '/api/v1/test-dubbo/user' type: restful description: user + timeout: 100ms plugins: pre: pluginNames: diff --git a/go.mod b/go.mod index af8467de5..c1f0aea30 100644 --- a/go.mod +++ b/go.mod @@ -6,13 +6,13 @@ require ( github.com/alibaba/sentinel-golang v1.0.2 github.com/apache/dubbo-go v1.5.7-rc1 github.com/apache/dubbo-go-hessian2 v1.9.2 - github.com/coreos/etcd v3.3.25+incompatible github.com/dubbogo/dubbo-go-pixiu-filter v0.1.4-0.20210613012702-8488bf80772c github.com/dubbogo/go-zookeeper v1.0.3 - github.com/dubbogo/gost v1.11.7 + github.com/dubbogo/gost v1.11.8 github.com/emirpasic/gods v1.12.0 github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 github.com/goinggo/mapstructure v0.0.0-20140717182941-194205d9b4a9 + github.com/golang/protobuf v1.5.2 // indirect github.com/hashicorp/consul/api v1.5.0 github.com/pkg/errors v0.9.1 github.com/shirou/gopsutil v3.21.3+incompatible // indirect @@ -20,12 +20,15 @@ require ( github.com/stretchr/testify v1.7.0 github.com/tklauser/go-sysconf v0.3.5 // indirect github.com/urfave/cli v1.22.4 + go.etcd.io/etcd v0.0.0-20200402134248-51bdeb39e698 go.uber.org/zap v1.16.0 - google.golang.org/grpc v1.33.1 + golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc // indirect + golang.org/x/sys v0.0.0-20210415045647-66c3f260301c // indirect + golang.org/x/text v0.3.6 // indirect gopkg.in/yaml.v2 v2.4.0 ) replace ( - github.com/envoyproxy/go-control-plane => github.com/envoyproxy/go-control-plane v0.8.0 - google.golang.org/grpc v1.33.1 => google.golang.org/grpc v1.26.0 + github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473 => github.com/envoyproxy/go-control-plane v0.8.0 + google.golang.org/grpc => google.golang.org/grpc v1.26.0 ) diff --git a/go.sum b/go.sum index d2b7690a3..532fd1e9f 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,3 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= @@ -84,7 +83,6 @@ github.com/apache/dubbo-getty v1.4.3 h1:PCKpryDasKOxwT5MBC6MIMO+0NLOaHF6Xco9YXQw github.com/apache/dubbo-getty v1.4.3/go.mod h1:ansXgKxxyhCOiQL29nO5ce1MDcEKmCyZuNR9oMs3hek= github.com/apache/dubbo-go v1.5.7-rc1 h1:PBK/Ox35L8QH85eBmHAqsg/EL2SnO+xwcCw67lQ4z4Q= github.com/apache/dubbo-go v1.5.7-rc1/go.mod h1:wLJvPWbnrf6/bhoohBT404QK4t2JoNcckMw+/fJ9P+c= -github.com/apache/dubbo-go-hessian2 v1.9.1 h1:ceSsU/9z/gv3hzUpl8GceEhQvF3i0BionfdHUGMmjHU= github.com/apache/dubbo-go-hessian2 v1.9.1/go.mod h1:xQUjE7F8PX49nm80kChFvepA/AvqAZ0oh/UaB6+6pBE= github.com/apache/dubbo-go-hessian2 v1.9.2 h1:XuI8KvENSfKiAhiCBS4RNihmQDoPNmGWKT3gTui0p9A= github.com/apache/dubbo-go-hessian2 v1.9.2/go.mod h1:xQUjE7F8PX49nm80kChFvepA/AvqAZ0oh/UaB6+6pBE= @@ -127,20 +125,16 @@ github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6D github.com/circonus-labs/circonusllhist v0.1.3 h1:TJH+oke8D16535+jHExHj4nQvzlZrj7ug5D7I/orNUA= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f h1:WBZRG4aNOuI15bLRrCgN8fCq8E5Xuty6jGbmSNEvSsU= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coredns/coredns v1.1.2 h1:bAFHrSsBeTeRG5W3Nf2su3lUGw7Npw2UKeCJm/3A638= github.com/coredns/coredns v1.1.2/go.mod h1:zASH/MVDgR6XZTbxvOnsZfffS+31vg6Ackf/wo1+AM0= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/bbolt v1.3.3 h1:n6AiVyVRKQFNb6mJlwESEvvLoDyiTzXX7ORAUlkeBdY= github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/etcd v3.3.25+incompatible h1:0GQEw6h3YnuOVdtwygkIfJ+Omx0tZ8/QkVyXI4LkbeY= github.com/coreos/etcd v3.3.25+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= @@ -152,9 +146,9 @@ github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU= github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.0.0 h1:XJIw/+VlJ+87J+doOxznsAWIdmWuViOVhkQamW5YV28= github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= @@ -183,18 +177,15 @@ github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyG github.com/docker/go-connections v0.3.0 h1:3lOnM9cSzgGwx8VfK/NGOW5fLQ0GjIlCkaktF+n1M6o= github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= -github.com/dubbogo/dubbo-go-pixiu-filter v0.1.4-0.20210427062645-0bec837d429e h1:0d1cHanEYqLu/WRwvTe4Y2zeCeeiimVqFKTL5QXAxAs= -github.com/dubbogo/dubbo-go-pixiu-filter v0.1.4-0.20210427062645-0bec837d429e/go.mod h1:d6SDK5BHl/QCvg84BN+g6LZS9QzVqnI2+yw0NBu0uac= github.com/dubbogo/dubbo-go-pixiu-filter v0.1.4-0.20210613012702-8488bf80772c h1:Hff+hNfuM7lx01sJy0ZmEZELNGZkNEgU8WQOz8D4Tno= github.com/dubbogo/dubbo-go-pixiu-filter v0.1.4-0.20210613012702-8488bf80772c/go.mod h1:o0tgVjbQyVxVq27Av9VvB5ZBv6tk4ypfIPkhfTLfmuw= github.com/dubbogo/go-zookeeper v1.0.3 h1:UkuY+rBsxdT7Bs63QAzp9z7XqQ53W1j8E5rwl83me8g= github.com/dubbogo/go-zookeeper v1.0.3/go.mod h1:fn6n2CAEer3novYgk9ULLwAjuV8/g4DdC2ENwRb6E+c= github.com/dubbogo/gost v1.9.0/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8= github.com/dubbogo/gost v1.10.1/go.mod h1:+mQGS51XQEUWZP2JeGZTxJwipjRKtJO7Tr+FOg+72rI= -github.com/dubbogo/gost v1.11.2 h1:NanyHmvzE1HrgI2T9H/jE/N1wkxFEj+IbM1A4RT9H7Q= github.com/dubbogo/gost v1.11.2/go.mod h1:3QQEj50QOhkWTERT785YZ5ZxIRGNdR11FCLP7FzHsMc= -github.com/dubbogo/gost v1.11.7 h1:mS2nuUOhOQmHSPnbs/94GakMigcKhzRr0TFLreiqNyo= -github.com/dubbogo/gost v1.11.7/go.mod h1:2nB8jSrxVPwW5DBsRu3FZQH1+Ual3wnRHwFqjG9+4PY= +github.com/dubbogo/gost v1.11.8 h1:OPTG4qIyNQ949GbdgHvpvYiVNno/X/YBozOVBLuNkS4= +github.com/dubbogo/gost v1.11.8/go.mod h1:2nB8jSrxVPwW5DBsRu3FZQH1+Ual3wnRHwFqjG9+4PY= github.com/dubbogo/jsonparser v1.0.1/go.mod h1:tYAtpctvSP/tWw4MeelsowSPgXQRVHHWbqL6ynps8jU= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= @@ -300,8 +291,10 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -311,8 +304,9 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= @@ -326,7 +320,6 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4 github.com/google/tcpproxy v0.0.0-20180808230851-dfa16c61dad2 h1:AtvtonGEH/fZK0XPNNBdB6swgy7Iudfx88wzyIpwqJ8= github.com/google/tcpproxy v0.0.0-20180808230851-dfa16c61dad2/go.mod h1:DavVbd41y+b7ukKDmlnPR4nGYmkWXR6vHUkjQNiHPBs= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -356,7 +349,6 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaD 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/grpc-ecosystem/grpc-gateway v1.14.6/go.mod h1:zdiPV4Yse/1gnckTHtghG4GkDEdKCRJduHpTxT3/jcw= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= @@ -476,7 +468,6 @@ github.com/jinzhu/copier v0.0.0-20190625015134-976e0346caa8 h1:mGIXW/lubQ4B+3bXT github.com/jinzhu/copier v0.0.0-20190625015134-976e0346caa8/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= @@ -510,7 +501,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -524,7 +514,6 @@ github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-b github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/linode/linodego v0.7.1 h1:4WZmMpSA2NRwlPZcc0+4Gyn7rr99Evk9bnr0B3gXRKE= github.com/linode/linodego v0.7.1/go.mod h1:ga11n3ivecUrPCHN0rANxKmfWBJVkOXfLMZinAbj2sY= -github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.4 h1:8KGKTcQQGm0Kv7vEbKFErAoAOFyyacLStRtQSeYtvkY= @@ -718,7 +707,6 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUt github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shirou/gopsutil v0.0.0-20181107111621-48177ef5f880/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shirou/gopsutil v3.20.11-0.20201116082039-2fb5da2f2449+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shirou/gopsutil v3.20.11+incompatible h1:LJr4ZQK4mPpIV5gOa4jCOKOGb4ty4DZO54I4FGqIpto= github.com/shirou/gopsutil v3.20.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shirou/gopsutil v3.21.3+incompatible h1:uenXGGa8ESCQq+dbgtl916dmg6PSAz2cXov0uORQ9v8= github.com/shirou/gopsutil v3.21.3+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= @@ -773,7 +761,6 @@ 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/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -820,6 +807,7 @@ go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg= go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.etcd.io/etcd v0.0.0-20200402134248-51bdeb39e698 h1:jWtjCJX1qxhHISBMLRztWwR+EXkI7MJAF2HjHAE/x/I= go.etcd.io/etcd v0.0.0-20200402134248-51bdeb39e698/go.mod h1:YoUyTScD3Vcv2RBm3eGVOq7i1ULiz3OuXoQFWOirmAM= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= @@ -859,8 +847,9 @@ golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc h1:+q90ECDSAQirdykUN6sPEiBXBsp8Csjcca8Oy7bgLTA= +golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 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= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -869,7 +858,6 @@ golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= 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= @@ -914,13 +902,12 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 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 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -930,13 +917,11 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -979,32 +964,32 @@ golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201223074533-0d417f636930 h1:vRgIt+nup/B/BwIS0g2oC0haq0iqbV3ZA+u6+0TlNCo= golang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa h1:ZYxPR6aca/uhfRJyaOAtflSHjJYiktO7QnJC5ut7iY4= golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210415045647-66c3f260301c h1:6L+uOeS3OQt/f4eFHXZcTxeZrGCuz+CLElgEBjbcTA4= +golang.org/x/sys v0.0.0-20210415045647-66c3f260301c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -1028,7 +1013,6 @@ golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200928182047-19e03678916f h1:VwGa2Wf+rHGIxvsssCkUNIyFv8jQY0VCBCNWtikoWq0= golang.org/x/tools v0.0.0-20200928182047-19e03678916f/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20210106214847-113979e3529a h1:CB3a9Nez8M13wwlr/E2YtwoU+qYHKfC+JrDa45RXXoQ= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= @@ -1044,7 +1028,6 @@ google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEn 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.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 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= @@ -1065,30 +1048,17 @@ google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884 h1:fiNLklpBwWK1mth30Hlwk+fcdBmIALlgF5iy77O37Ig= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 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/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= @@ -1126,7 +1096,6 @@ 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 h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1151,7 +1120,6 @@ k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= -sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/pkg/config/api_config.go b/pkg/config/api_config.go index 1a97921f0..d7b4e4491 100644 --- a/pkg/config/api_config.go +++ b/pkg/config/api_config.go @@ -18,36 +18,49 @@ package config import ( + "regexp" + "strconv" "strings" "sync" "time" ) import ( - "github.com/coreos/etcd/mvcc/mvccpb" - fc "github.com/dubbogo/dubbo-go-pixiu-filter/pkg/api/config" + etcdv3 "github.com/dubbogo/gost/database/kv/etcd/v3" perrors "github.com/pkg/errors" + "go.etcd.io/etcd/clientv3" + "go.etcd.io/etcd/mvcc/mvccpb" ) import ( "github.com/apache/dubbo-go-pixiu/pkg/common/yaml" "github.com/apache/dubbo-go-pixiu/pkg/logger" "github.com/apache/dubbo-go-pixiu/pkg/model" - etcdv3 "github.com/apache/dubbo-go-pixiu/pkg/remoting/etcd3" ) var ( apiConfig *fc.APIConfig once sync.Once client *etcdv3.Client - listener APIConfigListener + listener APIConfigResourceListener lock sync.RWMutex ) -// APIConfigListener defines api config listener interface -type APIConfigListener interface { - APIConfigChange(apiConfig fc.APIConfig) bool // bool is return for interface implement is interesting +// APIConfigResourceListener defines api resource and method config listener interface +type APIConfigResourceListener interface { + // ResourceChange handle modify resource event + ResourceChange(new fc.Resource, old fc.Resource) bool // bool is return for interface implement is interesting + // ResourceAdd handle add resource event + ResourceAdd(res fc.Resource) bool + // ResourceDelete handle delete resource event + ResourceDelete(deleted fc.Resource) bool + // MethodChange handle modify method event + MethodChange(res fc.Resource, method fc.Method, old fc.Method) bool + // MethodAdd handle add method below one resource event + MethodAdd(res fc.Resource, method fc.Method) bool + // MethodDelete handle delete method event + MethodDelete(res fc.Resource, method fc.Method) bool } // LoadAPIConfigFromFile load the api config from file @@ -67,64 +80,144 @@ func LoadAPIConfigFromFile(path string) (*fc.APIConfig, error) { // LoadAPIConfig load the api config from config center func LoadAPIConfig(metaConfig *model.APIMetaConfig) (*fc.APIConfig, error) { - client = etcdv3.NewConfigClient( + tmpClient, err := etcdv3.NewConfigClientWithErr( etcdv3.WithName(etcdv3.RegistryETCDV3Client), etcdv3.WithTimeout(10*time.Second), etcdv3.WithEndpoints(strings.Split(metaConfig.Address, ",")...), ) + if err != nil { + return nil, perrors.Errorf("Init etcd client fail error %v", err) + } - go listenAPIConfigNodeEvent(metaConfig.APIConfigPath) - - content, err := client.Get(metaConfig.APIConfigPath) + client = tmpClient + kList, vList, err := client.GetChildren(metaConfig.APIConfigPath) if err != nil { return nil, perrors.Errorf("Get remote config fail error %v", err) } - - if err = initAPIConfigFromString(content); err != nil { + if err = initAPIConfigFromKVList(kList, vList); err != nil { return nil, err } - + // TODO: init other setting which need fetch from remote + go listenResourceAndMethodEvent(metaConfig.APIConfigPath) + // TODO: watch other setting which need fetch from remote return apiConfig, nil } -func initAPIConfigFromString(content string) error { +func initAPIConfigFromKVList(kList, vList []string) error { + var skList, svList, mkList, mvList []string + + for i, k := range kList { + v := vList[i] + // handle resource + re := getCheckResourceRegexp() + if m := re.Match([]byte(k)); m { + skList = append(skList, k) + svList = append(svList, v) + continue + } + // handle method + re = getExtractMethodRegexp() + if m := re.Match([]byte(k)); m { + mkList = append(mkList, k) + mvList = append(mvList, v) + continue + } + } + lock.Lock() defer lock.Unlock() - apiConf := &fc.APIConfig{} - if len(content) != 0 { - err := yaml.UnmarshalYML([]byte(content), apiConf) + tmpApiConf := &fc.APIConfig{} + if err := initAPIConfigServiceFromKvList(tmpApiConf, skList, svList); err != nil { + logger.Error("initAPIConfigServiceFromKvList error %v", err.Error()) + return err + } + if err := initAPIConfigMethodFromKvList(tmpApiConf, mkList, mvList); err != nil { + logger.Error("initAPIConfigMethodFromKvList error %v", err.Error()) + return err + } + + apiConfig = tmpApiConf + return nil +} + +func initAPIConfigMethodFromKvList(config *fc.APIConfig, kList, vList []string) error { + for i, _ := range kList { + v := vList[i] + method := &fc.Method{} + err := yaml.UnmarshalYML([]byte(v), method) if err != nil { - return perrors.Errorf("unmarshalYmlConfig error %v", perrors.WithStack(err)) + logger.Error("unmarshalYmlConfig error %v", err.Error()) + return err } - valid := validateAPIConfig(apiConf) - if !valid { - return perrors.Errorf("api config not valid error %v", perrors.WithStack(err)) + found := false + for r, resource := range config.Resources { + if method.ResourcePath != resource.Path { + continue + } + + for j, old := range resource.Methods { + if old.HTTPVerb == method.HTTPVerb { + // modify one method + resource.Methods[j] = *method + found = true + } + } + if !found { + resource.Methods = append(resource.Methods, *method) + config.Resources[r] = resource + found = true + } } - apiConfig = apiConf + // not found one resource, so need add empty resource first + if !found { + resource := &fc.Resource{} + resource.Methods = append(resource.Methods, *method) + resource.Path = method.ResourcePath + config.Resources = append(config.Resources, *resource) + } } return nil } -// validateAPIConfig check api config valid -func validateAPIConfig(conf *fc.APIConfig) bool { - if conf.Name == "" { - return false - } - if conf.Description == "" { - return false - } - if conf.Resources == nil || len(conf.Resources) == 0 { - return false +func initAPIConfigServiceFromKvList(config *fc.APIConfig, kList, vList []string) error { + for i, _ := range kList { + v := vList[i] + resource := &fc.Resource{} + err := yaml.UnmarshalYML([]byte(v), resource) + if err != nil { + logger.Error("unmarshalYmlConfig error %v", err.Error()) + return err + } + + found := false + if config.Resources == nil { + config.Resources = make([]fc.Resource, 0) + } + + for i, old := range config.Resources { + if old.Path != resource.Path { + continue + } + // replace old with new one except method list + resource.Methods = old.Methods + config.Resources[i] = *resource + found = true + } + + if !found { + config.Resources = append(config.Resources, *resource) + } + continue } - return true + return nil } -func listenAPIConfigNodeEvent(key string) bool { +func listenResourceAndMethodEvent(key string) bool { for { - wc, err := client.Watch(key) + wc, err := client.WatchWithOption(key, clientv3.WithPrefix()) if err != nil { logger.Warnf("Watch api config {key:%s} = error{%v}", key, err) return false @@ -151,11 +244,11 @@ func listenAPIConfigNodeEvent(key string) bool { for _, event := range e.Events { switch event.Type { case mvccpb.PUT: - if err = initAPIConfigFromString(string(event.Kv.Value)); err == nil { - listener.APIConfigChange(GetAPIConf()) - } + logger.Infof("get event (key{%s}) = event{EventNodePut}", event.Kv.Key) + handlePutEvent(event.Kv.Key, event.Kv.Value) case mvccpb.DELETE: - logger.Warnf("get event (key{%s}) = event{EventNodeDeleted}", event.Kv.Key) + logger.Infof("get event (key{%s}) = event{EventNodeDeleted}", event.Kv.Key) + handleDeleteEvent(event.Kv.Key, event.Kv.Value) return true default: return false @@ -165,12 +258,167 @@ func listenAPIConfigNodeEvent(key string) bool { } } +func handleDeleteEvent(key, val []byte) { + lock.Lock() + defer lock.Unlock() + + keyStr := string(key) + keyStr = strings.TrimSuffix(keyStr, "/") + + re := getCheckResourceRegexp() + if m := re.Match(key); m { + pathArray := strings.Split(keyStr, "/") + if len(pathArray) == 0 { + logger.Errorf("handleDeleteEvent key format error") + return + } + resourceIdStr := pathArray[len(pathArray)-1] + ID, err := strconv.Atoi(resourceIdStr) + if err != nil { + logger.Error("handleDeleteEvent ID is not int error %v", err) + return + } + deleteApiConfigResource(ID) + return + } + + re = getExtractMethodRegexp() + if m := re.Match(key); m { + pathArray := strings.Split(keyStr, "/") + if len(pathArray) < 3 { + logger.Errorf("handleDeleteEvent key format error") + return + } + resourceIdStr := pathArray[len(pathArray)-3] + resourceId, err := strconv.Atoi(resourceIdStr) + if err != nil { + logger.Error("handleDeleteEvent ID is not int error %v", err) + return + } + + methodIdStr := pathArray[len(pathArray)-1] + methodId, err := strconv.Atoi(methodIdStr) + if err != nil { + logger.Error("handleDeleteEvent ID is not int error %v", err) + return + } + deleteApiConfigMethod(resourceId, methodId) + } +} + +func handlePutEvent(key, val []byte) { + lock.Lock() + defer lock.Unlock() + + re := getCheckResourceRegexp() + if m := re.Match(key); m { + res := &fc.Resource{} + err := yaml.UnmarshalYML(val, res) + if err != nil { + logger.Error("handlePutEvent UnmarshalYML error %v", err) + return + } + mergeApiConfigResource(*res) + return + } + + re = getExtractMethodRegexp() + if m := re.Match(key); m { + res := &fc.Method{} + err := yaml.UnmarshalYML(val, res) + if err != nil { + logger.Error("handlePutEvent UnmarshalYML error %v", err) + return + } + mergeApiConfigMethod(res.ResourcePath, *res) + } +} + +func deleteApiConfigResource(resourceId int) { + for i := 0; i < len(apiConfig.Resources); i++ { + itr := apiConfig.Resources[i] + if itr.ID == resourceId { + apiConfig.Resources = append(apiConfig.Resources[:i], apiConfig.Resources[i+1:]...) + listener.ResourceDelete(itr) + return + } + } +} + +func mergeApiConfigResource(val fc.Resource) { + for i, resource := range apiConfig.Resources { + if val.ID != resource.ID { + continue + } + // modify one resource + val.Methods = resource.Methods + apiConfig.Resources[i] = val + listener.ResourceChange(val, resource) + return + } + // add one resource + apiConfig.Resources = append(apiConfig.Resources, val) + listener.ResourceAdd(val) +} + +func deleteApiConfigMethod(resourceId, methodId int) { + for _, resource := range apiConfig.Resources { + if resource.ID != resourceId { + continue + } + + for i := 0; i < len(resource.Methods); i++ { + method := resource.Methods[i] + + if method.ID == methodId { + resource.Methods = append(resource.Methods[:i], resource.Methods[i+1:]...) + listener.MethodDelete(resource, method) + return + } + } + } +} + +func mergeApiConfigMethod(path string, val fc.Method) { + for i, resource := range apiConfig.Resources { + if path != resource.Path { + continue + } + + for j, method := range resource.Methods { + if method.ID == val.ID { + // modify one method + resource.Methods[j] = val + listener.MethodChange(resource, val, method) + apiConfig.Resources[i] = resource + return + } + } + // add one method + resource.Methods = append(resource.Methods, val) + apiConfig.Resources[i] = resource + listener.MethodAdd(resource, val) + } +} + +func getCheckBaseInfoRegexp() *regexp.Regexp { + return regexp.MustCompile(".+/base$") +} + +func getCheckResourceRegexp() *regexp.Regexp { + return regexp.MustCompile(".+/Resources/[^/]+/?$") +} + +func getExtractMethodRegexp() *regexp.Regexp { + return regexp.MustCompile("Resources/([^/]+)/Method/[^/]+/?$") +} + // RegisterConfigListener register APIConfigListener -func RegisterConfigListener(li APIConfigListener) { +func RegisterConfigListener(li APIConfigResourceListener) { listener = li } -// GetAPIConf returns the initted api config +// GetAPIConf returns the init api config func GetAPIConf() fc.APIConfig { return *apiConfig } diff --git a/pkg/pixiu/pixiu_start.go b/pkg/pixiu/pixiu_start.go index 254eca7ed..d5e53c4f9 100644 --- a/pkg/pixiu/pixiu_start.go +++ b/pkg/pixiu/pixiu_start.go @@ -77,10 +77,8 @@ func (p *PX) Start() { func (p *PX) beforeStart() { dubbo.SingletonDubboClient().Init() - - api.InitAPIsFromConfig(config.GetAPIConf()) - initialize.Run(config.GetAPIConf()) + api.InitAPIsFromConfig(config.GetAPIConf()) } // NewPX create pixiu diff --git a/pkg/registry/zookeeper.go b/pkg/registry/zookeeper.go index f84f43e9e..ada2452e2 100644 --- a/pkg/registry/zookeeper.go +++ b/pkg/registry/zookeeper.go @@ -26,7 +26,7 @@ import ( import ( "github.com/apache/dubbo-go-pixiu/pkg/logger" "github.com/apache/dubbo-go/common" - zookeeper "github.com/dubbogo/gost/database/kv/zk" + gxzookeeper "github.com/dubbogo/gost/database/kv/zk" ) const ( @@ -40,13 +40,13 @@ func init() { // ZookeeperRegistryLoad load dubbo apis from zookeeper registry type ZookeeperRegistryLoad struct { zkName string - client *zookeeper.ZookeeperClient + client *gxzookeeper.ZookeeperClient Address string cluster string } func newZookeeperRegistryLoad(address, cluster string) (Loader, error) { - newClient, err := zookeeper.NewZookeeperClient("zkClient", strings.Split(address, ","), false, zookeeper.WithZkTimeOut(15*time.Second)) + newClient, err := gxzookeeper.NewZookeeperClient("zkClient", strings.Split(address, ","), false, gxzookeeper.WithZkTimeOut(15*time.Second)) if err != nil { logger.Warnf("newZookeeperClient error:%v", err) return nil, err diff --git a/pkg/registry/zookeeper_test.go b/pkg/registry/zookeeper_test.go index d03ff121f..8086cfe3c 100644 --- a/pkg/registry/zookeeper_test.go +++ b/pkg/registry/zookeeper_test.go @@ -24,24 +24,25 @@ import ( "strings" "testing" "time" +) +import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" "github.com/dubbogo/go-zookeeper/zk" - - zookeeper "github.com/dubbogo/gost/database/kv/zk" + gxzookeeper "github.com/dubbogo/gost/database/kv/zk" "github.com/stretchr/testify/assert" ) const providerUrlstr = "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?methods.GetUser.retries=1" -func newMockZkRegistry(t *testing.T, providerURL *common.URL, opts ...zookeeper.Option) (*zk.TestCluster, error) { +func newMockZkRegistry(t *testing.T, providerURL *common.URL, opts ...gxzookeeper.Option) (*zk.TestCluster, error) { var ( err error c *zk.TestCluster - client *zookeeper.ZookeeperClient + client *gxzookeeper.ZookeeperClient ) - c, client, _, err = zookeeper.NewMockZookeeperClient("test", 15*time.Second, opts...) + c, client, _, err = gxzookeeper.NewMockZookeeperClient("test", 15*time.Second, opts...) if err != nil { return nil, err } diff --git a/pkg/remoting/etcd3/client.go b/pkg/remoting/etcd3/client.go deleted file mode 100644 index b2f867679..000000000 --- a/pkg/remoting/etcd3/client.go +++ /dev/null @@ -1,458 +0,0 @@ -/* - * 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 etcdv3 - -import ( - "context" - "sync" - "time" -) - -import ( - "github.com/coreos/etcd/clientv3" - "github.com/coreos/etcd/clientv3/concurrency" - perrors "github.com/pkg/errors" - "google.golang.org/grpc" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/logger" -) - -const ( - // ConnDelay connection delay - ConnDelay = 3 - // MaxFailTimes max failure times - MaxFailTimes = 15 - // RegistryETCDV3Client client name - RegistryETCDV3Client = "etcd registry" - // MetadataETCDV3Client client name - MetadataETCDV3Client = "etcd metadata" -) - -var ( - // ErrNilETCDV3Client raw client nil - ErrNilETCDV3Client = perrors.New("etcd raw client is nil") // full describe the ERR - // ErrKVPairNotFound not found key - ErrKVPairNotFound = perrors.New("k/v pair not found") -) - -// Options client configuration -type Options struct { - name string - endpoints []string - client *Client - timeout time.Duration - heartbeat int // heartbeat second -} - -// Option will define a function of handling Options -type Option func(*Options) - -// WithEndpoints sets etcd client endpoints -func WithEndpoints(endpoints ...string) Option { - return func(opt *Options) { - opt.endpoints = endpoints - } -} - -// WithName sets etcd client name -func WithName(name string) Option { - return func(opt *Options) { - opt.name = name - } -} - -// WithTimeout sets etcd client timeout -func WithTimeout(timeout time.Duration) Option { - return func(opt *Options) { - opt.timeout = timeout - } -} - -// WithHeartbeat sets etcd client heartbeat -func WithHeartbeat(heartbeat int) Option { - return func(opt *Options) { - opt.heartbeat = heartbeat - } -} - -// NewConfigClient create new Client -func NewConfigClient(opts ...Option) *Client { - options := &Options{ - heartbeat: 1, // default heartbeat - } - for _, opt := range opts { - opt(options) - } - - newClient, err := NewClient(options.name, options.endpoints, options.timeout, options.heartbeat) - if err != nil { - logger.Errorf("new etcd client (name{%s}, etcd addresses{%v}, timeout{%d}) = error{%v}", - options.name, options.endpoints, options.timeout, err) - } - return newClient -} - -// Client represents etcd client Configuration -type Client struct { - lock sync.RWMutex - - // these properties are only set once when they are started. - name string - endpoints []string - timeout time.Duration - heartbeat int - - ctx context.Context // if etcd server connection lose, the ctx.Done will be sent msg - cancel context.CancelFunc // cancel the ctx, all watcher will stopped - rawClient *clientv3.Client - - exit chan struct{} - Wait sync.WaitGroup -} - -// NewClient create a client instance with name, endpoints etc. -func NewClient(name string, endpoints []string, timeout time.Duration, heartbeat int) (*Client, error) { - ctx, cancel := context.WithCancel(context.Background()) - rawClient, err := clientv3.New(clientv3.Config{ - Context: ctx, - Endpoints: endpoints, - DialTimeout: timeout, - DialOptions: []grpc.DialOption{grpc.WithBlock()}, - }) - if err != nil { - cancel() - return nil, perrors.WithMessage(err, "new raw client block connect to server") - } - - c := &Client{ - name: name, - timeout: timeout, - endpoints: endpoints, - heartbeat: heartbeat, - - ctx: ctx, - cancel: cancel, - rawClient: rawClient, - - exit: make(chan struct{}), - } - - if err := c.maintenanceStatus(); err != nil { - return nil, perrors.WithMessage(err, "client maintenance status") - } - return c, nil -} - -// NOTICE: need to get the lock before calling this method -func (c *Client) clean() { - // close raw client - c.rawClient.Close() - - // cancel ctx for raw client - c.cancel() - - // clean raw client - c.rawClient = nil -} - -func (c *Client) stop() bool { - select { - case <-c.exit: - return true - default: - close(c.exit) - } - return false -} - -// Close close client -func (c *Client) Close() { - if c == nil { - return - } - - // stop the client - c.stop() - - // wait client maintenance status stop - c.Wait.Wait() - - c.lock.Lock() - defer c.lock.Unlock() - if c.rawClient != nil { - c.clean() - } - logger.Warnf("etcd client{name:%s, endpoints:%s} exit now.", c.name, c.endpoints) -} - -func (c *Client) maintenanceStatus() error { - s, err := concurrency.NewSession(c.rawClient, concurrency.WithTTL(c.heartbeat)) - if err != nil { - return perrors.WithMessage(err, "new session with server") - } - - // must add wg before go maintenance status goroutine - c.Wait.Add(1) - go c.maintenanceStatusLoop(s) - return nil -} - -func (c *Client) maintenanceStatusLoop(s *concurrency.Session) { - defer func() { - c.Wait.Done() - logger.Infof("etcd client {endpoints:%v, name:%s} maintenance goroutine game over.", c.endpoints, c.name) - }() - - for { - select { - case <-c.Done(): - // Client be stopped, will clean the client hold resources - return - case <-s.Done(): - logger.Warn("etcd server stopped") - c.lock.Lock() - // when etcd server stopped, cancel ctx, stop all watchers - c.clean() - // when connection lose, stop client, trigger reconnect to etcd - c.stop() - c.lock.Unlock() - return - } - } -} - -// if k not exist will put k/v in etcd, otherwise return nil -func (c *Client) put(k string, v string, opts ...clientv3.OpOption) error { - c.lock.RLock() - defer c.lock.RUnlock() - - if c.rawClient == nil { - return ErrNilETCDV3Client - } - - _, err := c.rawClient.Txn(c.ctx). - If(clientv3.Compare(clientv3.Version(k), "<", 1)). - Then(clientv3.OpPut(k, v, opts...)). - Commit() - return err -} - -// if k not exist will put k/v in etcd -// if k is already exist in etcd, replace it -func (c *Client) update(k string, v string, opts ...clientv3.OpOption) error { - c.lock.RLock() - defer c.lock.RUnlock() - - if c.rawClient == nil { - return ErrNilETCDV3Client - } - - _, err := c.rawClient.Txn(c.ctx). - If(clientv3.Compare(clientv3.Version(k), "!=", -1)). - Then(clientv3.OpPut(k, v, opts...)). - Commit() - return err -} - -func (c *Client) delete(k string) error { - c.lock.RLock() - defer c.lock.RUnlock() - - if c.rawClient == nil { - return ErrNilETCDV3Client - } - - _, err := c.rawClient.Delete(c.ctx, k) - return err -} - -func (c *Client) get(k string) (string, error) { - c.lock.RLock() - defer c.lock.RUnlock() - - if c.rawClient == nil { - return "", ErrNilETCDV3Client - } - - resp, err := c.rawClient.Get(c.ctx, k) - if err != nil { - return "", err - } - - if len(resp.Kvs) == 0 { - return "", ErrKVPairNotFound - } - - return string(resp.Kvs[0].Value), nil -} - -// CleanKV delete all key and value -func (c *Client) CleanKV() error { - c.lock.RLock() - defer c.lock.RUnlock() - - if c.rawClient == nil { - return ErrNilETCDV3Client - } - - _, err := c.rawClient.Delete(c.ctx, "", clientv3.WithPrefix()) - return err -} - -func (c *Client) getChildren(k string) ([]string, []string, error) { - c.lock.RLock() - defer c.lock.RUnlock() - - if c.rawClient == nil { - return nil, nil, ErrNilETCDV3Client - } - - resp, err := c.rawClient.Get(c.ctx, k, clientv3.WithPrefix()) - if err != nil { - return nil, nil, err - } - - if len(resp.Kvs) == 0 { - return nil, nil, ErrKVPairNotFound - } - - kList := make([]string, 0, len(resp.Kvs)) - vList := make([]string, 0, len(resp.Kvs)) - for _, kv := range resp.Kvs { - kList = append(kList, string(kv.Key)) - vList = append(vList, string(kv.Value)) - } - return kList, vList, nil -} - -func (c *Client) watchWithPrefix(prefix string) (clientv3.WatchChan, error) { - c.lock.RLock() - defer c.lock.RUnlock() - - if c.rawClient == nil { - return nil, ErrNilETCDV3Client - } - - return c.rawClient.Watch(c.ctx, prefix, clientv3.WithPrefix()), nil -} - -func (c *Client) watch(k string) (clientv3.WatchChan, error) { - c.lock.RLock() - defer c.lock.RUnlock() - - if c.rawClient == nil { - return nil, ErrNilETCDV3Client - } - - return c.rawClient.Watch(c.ctx, k), nil -} - -func (c *Client) keepAliveKV(k string, v string) error { - c.lock.RLock() - defer c.lock.RUnlock() - - if c.rawClient == nil { - return ErrNilETCDV3Client - } - - // make lease time longer, since 1 second is too short - lease, err := c.rawClient.Grant(c.ctx, int64(30*time.Second.Seconds())) - if err != nil { - return perrors.WithMessage(err, "grant lease") - } - - keepAlive, err := c.rawClient.KeepAlive(c.ctx, lease.ID) - if err != nil || keepAlive == nil { - c.rawClient.Revoke(c.ctx, lease.ID) - if err != nil { - return perrors.WithMessage(err, "keep alive lease") - } - return perrors.New("keep alive lease") - } - - _, err = c.rawClient.Put(c.ctx, k, v, clientv3.WithLease(lease.ID)) - return perrors.WithMessage(err, "put k/v with lease") -} - -// Done return exit chan -func (c *Client) Done() <-chan struct{} { - return c.exit -} - -// Valid check client -func (c *Client) Valid() bool { - select { - case <-c.exit: - return false - default: - } - - c.lock.RLock() - defer c.lock.RUnlock() - return c.rawClient != nil -} - -// Create key value ... -func (c *Client) Create(k string, v string) error { - err := c.put(k, v) - return perrors.WithMessagef(err, "put k/v (key: %s value %s)", k, v) -} - -// Update key value ... -func (c *Client) Update(k, v string) error { - err := c.update(k, v) - return perrors.WithMessagef(err, "Update k/v (key: %s value %s)", k, v) -} - -// Delete key -func (c *Client) Delete(k string) error { - err := c.delete(k) - return perrors.WithMessagef(err, "delete k/v (key %s)", k) -} - -// RegisterTemp registers a temporary node -func (c *Client) RegisterTemp(k, v string) error { - err := c.keepAliveKV(k, v) - return perrors.WithMessagef(err, "keepalive kv (key %s)", k) -} - -// GetChildrenKVList gets children kv list by @k -func (c *Client) GetChildrenKVList(k string) ([]string, []string, error) { - kList, vList, err := c.getChildren(k) - return kList, vList, perrors.WithMessagef(err, "get key children (key %s)", k) -} - -// Get gets value by @k -func (c *Client) Get(k string) (string, error) { - v, err := c.get(k) - return v, perrors.WithMessagef(err, "get key value (key %s)", k) -} - -// Watch watches on spec key -func (c *Client) Watch(k string) (clientv3.WatchChan, error) { - wc, err := c.watch(k) - return wc, perrors.WithMessagef(err, "watch prefix (key %s)", k) -} - -// WatchWithPrefix watches on spec prefix -func (c *Client) WatchWithPrefix(prefix string) (clientv3.WatchChan, error) { - wc, err := c.watchWithPrefix(prefix) - return wc, perrors.WithMessagef(err, "watch prefix (key %s)", prefix) -} diff --git a/pkg/remoting/etcd3/client_test.go b/pkg/remoting/etcd3/client_test.go deleted file mode 100644 index 2bf51bbb2..000000000 --- a/pkg/remoting/etcd3/client_test.go +++ /dev/null @@ -1,399 +0,0 @@ -/* - * 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 etcdv3 - -import ( - "net/url" - "os" - "path" - "reflect" - "strings" - "sync" - "testing" - "time" -) - -import ( - "github.com/coreos/etcd/embed" - "github.com/coreos/etcd/mvcc/mvccpb" - perrors "github.com/pkg/errors" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" - "google.golang.org/grpc/connectivity" -) - -const defaultEtcdV3WorkDir = "/tmp/default-dubbo-go-remote.etcd" - -// tests dataset -var tests = []struct { - input struct { - k string - v string - } -}{ - {input: struct { - k string - v string - }{k: "name", v: "scott.wang"}}, - {input: struct { - k string - v string - }{k: "namePrefix", v: "prefix.scott.wang"}}, - {input: struct { - k string - v string - }{k: "namePrefix1", v: "prefix1.scott.wang"}}, - {input: struct { - k string - v string - }{k: "age", v: "27"}}, -} - -// test dataset prefix -const prefix = "name" - -type ClientTestSuite struct { - suite.Suite - - etcdConfig struct { - name string - endpoints []string - timeout time.Duration - heartbeat int - } - - etcd *embed.Etcd - - client *Client -} - -// start etcd server -func (suite *ClientTestSuite) SetupSuite() { - t := suite.T() - - DefaultListenPeerURLs := "http://localhost:2382" - DefaultListenClientURLs := "http://localhost:2381" - lpurl, _ := url.Parse(DefaultListenPeerURLs) - lcurl, _ := url.Parse(DefaultListenClientURLs) - cfg := embed.NewConfig() - cfg.LPUrls = []url.URL{*lpurl} - cfg.LCUrls = []url.URL{*lcurl} - cfg.Dir = defaultEtcdV3WorkDir - e, err := embed.StartEtcd(cfg) - if err != nil { - t.Fatal(err) - } - select { - case <-e.Server.ReadyNotify(): - t.Log("Server is ready!") - case <-time.After(60 * time.Second): - e.Server.Stop() // trigger a shutdown - t.Logf("Server took too long to start!") - } - - suite.etcd = e - return -} - -// stop etcd server -func (suite *ClientTestSuite) TearDownSuite() { - suite.etcd.Close() - if err := os.RemoveAll(defaultEtcdV3WorkDir); err != nil { - suite.FailNow(err.Error()) - } -} - -func (suite *ClientTestSuite) setUpClient() *Client { - c, err := NewClient(suite.etcdConfig.name, - suite.etcdConfig.endpoints, - suite.etcdConfig.timeout, - suite.etcdConfig.heartbeat) - if err != nil { - suite.T().Fatal(err) - } - return c -} - -// set up a client for suite -func (suite *ClientTestSuite) SetupTest() { - c := suite.setUpClient() - c.CleanKV() - suite.client = c - return -} - -func (suite *ClientTestSuite) TestClientClose() { - c := suite.client - t := suite.T() - - defer c.Close() - if c.rawClient.ActiveConnection().GetState() != connectivity.Ready { - t.Fatal(suite.client.rawClient.ActiveConnection().GetState()) - } -} - -func (suite *ClientTestSuite) TestClientValid() { - c := suite.client - t := suite.T() - - if !c.Valid() { - t.Fatal("client is not valid") - } - c.Close() - if suite.client.Valid() != false { - t.Fatal("client is valid") - } -} - -func (suite *ClientTestSuite) TestClientDone() { - c := suite.client - - go func() { - time.Sleep(2 * time.Second) - c.Close() - }() - - c.Wait.Wait() - - if c.Valid() { - suite.T().Fatal("client should be invalid then") - } -} - -func (suite *ClientTestSuite) TestClientCreateKV() { - tests := tests - - c := suite.client - t := suite.T() - - defer suite.client.Close() - - for _, tc := range tests { - - k := tc.input.k - v := tc.input.v - expect := tc.input.v - - if err := c.Create(k, v); err != nil { - t.Fatal(err) - } - - value, err := c.Get(k) - if err != nil { - t.Fatal(err) - } - - if value != expect { - t.Fatalf("expect %v but get %v", expect, value) - } - - } -} - -func (suite *ClientTestSuite) TestClientDeleteKV() { - tests := tests - c := suite.client - t := suite.T() - - defer c.Close() - - for _, tc := range tests { - - k := tc.input.k - v := tc.input.v - expect := ErrKVPairNotFound - - if err := c.Create(k, v); err != nil { - t.Fatal(err) - } - - if err := c.Delete(k); err != nil { - t.Fatal(err) - } - - _, err := c.Get(k) - if perrors.Cause(err) == expect { - continue - } - - if err != nil { - t.Fatal(err) - } - } -} - -func (suite *ClientTestSuite) TestClientGetChildrenKVList() { - tests := tests - - c := suite.client - t := suite.T() - - var expectKList []string - var expectVList []string - - for _, tc := range tests { - - k := tc.input.k - v := tc.input.v - - if strings.Contains(k, prefix) { - expectKList = append(expectKList, k) - expectVList = append(expectVList, v) - } - - if err := c.Create(k, v); err != nil { - t.Fatal(err) - } - } - - kList, vList, err := c.GetChildrenKVList(prefix) - if err != nil { - t.Fatal(err) - } - - if reflect.DeepEqual(expectKList, kList) && reflect.DeepEqual(expectVList, vList) { - return - } - - t.Fatalf("expect keylist %v but got %v expect valueList %v but got %v ", expectKList, kList, expectVList, vList) -} - -func (suite *ClientTestSuite) TestClientWatch() { - tests := tests - - c := suite.client - t := suite.T() - - wg := sync.WaitGroup{} - wg.Add(1) - - go func() { - defer wg.Done() - - wc, err := c.watch(prefix) - if err != nil { - assert.Error(t, err) - return - } - - events := make([]mvccpb.Event, 0) - var eCreate, eDelete mvccpb.Event - - for e := range wc { - for _, event := range e.Events { - events = append(events, (mvccpb.Event)(*event)) - if event.Type == mvccpb.PUT { - eCreate = (mvccpb.Event)(*event) - } - if event.Type == mvccpb.DELETE { - eDelete = (mvccpb.Event)(*event) - } - t.Logf("type IsCreate %v k %s v %s", event.IsCreate(), event.Kv.Key, event.Kv.Value) - } - } - - assert.Equal(t, 2, len(events)) - assert.Contains(t, events, eCreate) - assert.Contains(t, events, eDelete) - }() - - for _, tc := range tests { - - k := tc.input.k - v := tc.input.v - - if err := c.Create(k, v); err != nil { - t.Fatal(err) - } - - if err := c.delete(k); err != nil { - t.Fatal(err) - } - } - - c.Close() - - wg.Wait() -} - -func (suite *ClientTestSuite) TestClientRegisterTemp() { - c := suite.client - observeC := suite.setUpClient() - t := suite.T() - - wg := sync.WaitGroup{} - wg.Add(1) - - go func() { - defer wg.Done() - - completePath := path.Join("scott", "wang") - wc, err := observeC.watch(completePath) - if err != nil { - assert.Error(t, err) - return - } - - events := make([]mvccpb.Event, 0) - var eCreate, eDelete mvccpb.Event - - for e := range wc { - for _, event := range e.Events { - events = append(events, (mvccpb.Event)(*event)) - if event.Type == mvccpb.DELETE { - eDelete = (mvccpb.Event)(*event) - t.Logf("complete key (%s) is delete", completePath) - observeC.Close() - break - } - eCreate = (mvccpb.Event)(*event) - t.Logf("type IsCreate %v k %s v %s", event.IsCreate(), event.Kv.Key, event.Kv.Value) - } - } - - assert.Equal(t, 2, len(events)) - assert.Contains(t, events, eCreate) - assert.Contains(t, events, eDelete) - }() - - err := c.RegisterTemp("scott/wang", "test") - if err != nil { - t.Fatal(err) - } - - time.Sleep(2 * time.Second) - c.Close() - - wg.Wait() -} - -func TestClientSuite(t *testing.T) { - suite.Run(t, &ClientTestSuite{ - etcdConfig: struct { - name string - endpoints []string - timeout time.Duration - heartbeat int - }{ - name: "test", - endpoints: []string{"localhost:2381"}, - timeout: time.Second, - heartbeat: 1, - }, - }) -} diff --git a/pkg/router/route.go b/pkg/router/route.go index 18346966e..94688336b 100644 --- a/pkg/router/route.go +++ b/pkg/router/route.go @@ -120,6 +120,29 @@ func (rt *Route) FindAPI(fullPath string, httpverb config.HTTPVerb) (*router.API return nil, false } +// DeleteNode delete node by fullPath +func (rt *Route) DeleteNode(fullPath string) bool { + lowerPath := strings.ToLower(fullPath) + if _, found := rt.searchWildcard(lowerPath); found { + rt.lock.RLock() + defer rt.lock.RUnlock() + rt.tree.Remove(lowerPath) + return true + } + return false +} + +// DeleteAPI delete api by fullPath and http verb +func (rt *Route) DeleteAPI(fullPath string, httpverb config.HTTPVerb) bool { + if n, found := rt.findNode(fullPath); found { + rt.lock.RLock() + defer rt.lock.RUnlock() + delete(n.methods, httpverb) + return true + } + return false +} + func (rt *Route) findNode(fullPath string) (*Node, bool) { lowerPath := strings.ToLower(fullPath) var n interface{} diff --git a/pkg/service/api/discovery_service.go b/pkg/service/api/discovery_service.go index 52b6e3d1c..42e37205a 100644 --- a/pkg/service/api/discovery_service.go +++ b/pkg/service/api/discovery_service.go @@ -27,7 +27,6 @@ import ( "github.com/apache/dubbo-go-pixiu/pkg/common/constant" "github.com/apache/dubbo-go-pixiu/pkg/common/extension" pc "github.com/apache/dubbo-go-pixiu/pkg/config" - "github.com/apache/dubbo-go-pixiu/pkg/filter/plugins" "github.com/apache/dubbo-go-pixiu/pkg/router" "github.com/apache/dubbo-go-pixiu/pkg/service" ) @@ -74,13 +73,78 @@ func (ads *LocalMemoryAPIDiscoveryService) ClearAPI() error { return nil } -// APIConfigChange to response to api config change -func (ads *LocalMemoryAPIDiscoveryService) APIConfigChange(apiConfig config.APIConfig) bool { - ads.ClearAPI() - loadAPIFromResource("", apiConfig.Resources, nil, ads) +// RemoveAPIByPath remove all api belonged to path +func (ads *LocalMemoryAPIDiscoveryService) RemoveAPIByPath(deleted config.Resource) error { + _, groupPath := getDefaultPath() + fullPath := getFullPath(groupPath, deleted.Path) - plugins.Init(apiConfig.PluginsGroup, apiConfig.PluginFilePath, apiConfig.Resources) - return true + ads.router.DeleteNode(fullPath) + return nil +} + +// RemoveAPIByPath remove all api +func (ads *LocalMemoryAPIDiscoveryService) RemoveAPI(fullPath string, method config.Method) error { + ads.router.DeleteAPI(fullPath, method.HTTPVerb) + return nil +} + +// ResourceChange handle modify resource event +func (ads *LocalMemoryAPIDiscoveryService) ResourceChange(new config.Resource, old config.Resource) bool { + if err := modifyAPIFromResource(new, old, ads); err == nil { + return true + } + return false +} + +// ResourceAdd handle add resource event +func (ads *LocalMemoryAPIDiscoveryService) ResourceAdd(res config.Resource) bool { + parentPath, groupPath := getDefaultPath() + + fullHeaders := make(map[string]string, 9) + if err := addAPIFromResource(res, ads, groupPath, parentPath, fullHeaders); err == nil { + return true + } + return false +} + +// ResourceDelete handle delete resource event +func (ads *LocalMemoryAPIDiscoveryService) ResourceDelete(deleted config.Resource) bool { + if err := deleteAPIFromResource(deleted, ads); err == nil { + return true + } + return false +} + +// MethodChange handle modify method event +func (ads *LocalMemoryAPIDiscoveryService) MethodChange(res config.Resource, new config.Method, old config.Method) bool { + _, groupPath := getDefaultPath() + fullPath := getFullPath(groupPath, res.Path) + fullHeaders := make(map[string]string, 9) + if err := modifyAPIFromMethod(fullPath, new, old, fullHeaders, ads); err == nil { + return true + } + return false +} + +// MethodAdd handle add method event +func (ads *LocalMemoryAPIDiscoveryService) MethodAdd(res config.Resource, method config.Method) bool { + _, groupPath := getDefaultPath() + fullPath := getFullPath(groupPath, res.Path) + fullHeaders := make(map[string]string, 9) + if err := addAPIFromMethod(fullPath, method, fullHeaders, ads); err == nil { + return true + } + return false +} + +// MethodDelete handle delete method event +func (ads *LocalMemoryAPIDiscoveryService) MethodDelete(res config.Resource, method config.Method) bool { + _, groupPath := getDefaultPath() + fullPath := getFullPath(groupPath, res.Path) + if err := deleteAPIFromMethod(fullPath, method, ads); err == nil { + return true + } + return false } // InitAPIsFromConfig inits the router from API config and to local cache @@ -94,26 +158,13 @@ func InitAPIsFromConfig(apiConfig config.APIConfig) error { return loadAPIFromResource("", apiConfig.Resources, nil, localAPIDiscSrv) } -// RefreshAPIsFromConfig fresh the router from API config and to local cache -func RefreshAPIsFromConfig(apiConfig config.APIConfig) error { - localAPIDiscSrv := NewLocalMemoryAPIDiscoveryService() - if len(apiConfig.Resources) == 0 { - return nil - } - error := loadAPIFromResource("", apiConfig.Resources, nil, localAPIDiscSrv) - if error == nil { - extension.SetAPIDiscoveryService(constant.LocalMemoryApiDiscoveryService, localAPIDiscSrv) - } - return error -} - -func loadAPIFromResource(parrentPath string, resources []config.Resource, parentHeaders map[string]string, localSrv service.APIDiscoveryService) error { +func loadAPIFromResource(parentPath string, resources []config.Resource, parentHeaders map[string]string, localSrv service.APIDiscoveryService) error { errStack := []string{} if len(resources) == 0 { return nil } - groupPath := parrentPath - if parrentPath == constant.PathSlash { + groupPath := parentPath + if parentPath == constant.PathSlash { groupPath = "" } fullHeaders := parentHeaders @@ -121,21 +172,8 @@ func loadAPIFromResource(parrentPath string, resources []config.Resource, parent fullHeaders = make(map[string]string, 9) } for _, resource := range resources { - fullPath := groupPath + resource.Path - if !strings.HasPrefix(resource.Path, constant.PathSlash) { - errStack = append(errStack, fmt.Sprintf("Path %s in %s doesn't start with /", resource.Path, parrentPath)) - continue - } - for headerName, headerValue := range resource.Headers { - fullHeaders[headerName] = headerValue - } - if len(resource.Resources) > 0 { - if err := loadAPIFromResource(resource.Path, resource.Resources, fullHeaders, localSrv); err != nil { - errStack = append(errStack, err.Error()) - } - } - - if err := loadAPIFromMethods(fullPath, resource.Methods, fullHeaders, localSrv); err != nil { + err := addAPIFromResource(resource, localSrv, groupPath, parentPath, fullHeaders) + if err != nil { errStack = append(errStack, err.Error()) } } @@ -145,16 +183,85 @@ func loadAPIFromResource(parrentPath string, resources []config.Resource, parent return nil } +func getDefaultPath() (string, string) { + return "", "" +} + +func modifyAPIFromResource(new config.Resource, old config.Resource, localSrv service.APIDiscoveryService) error { + parentPath, groupPath := getDefaultPath() + fullHeaders := make(map[string]string, 9) + + err := deleteAPIFromResource(old, localSrv) + if err != nil { + return err + } + + err = addAPIFromResource(new, localSrv, groupPath, parentPath, fullHeaders) + return err +} + +func deleteAPIFromResource(old config.Resource, localSrv service.APIDiscoveryService) error { + return localSrv.RemoveAPIByPath(old) +} + +func addAPIFromResource(resource config.Resource, localSrv service.APIDiscoveryService, groupPath string, parentPath string, fullHeaders map[string]string) error { + fullPath := getFullPath(groupPath, resource.Path) + if !strings.HasPrefix(resource.Path, constant.PathSlash) { + return errors.New(fmt.Sprintf("Path %s in %s doesn't start with /", resource.Path, parentPath)) + } + for headerName, headerValue := range resource.Headers { + fullHeaders[headerName] = headerValue + } + if len(resource.Resources) > 0 { + if err := loadAPIFromResource(resource.Path, resource.Resources, fullHeaders, localSrv); err != nil { + return err + } + } + + if err := loadAPIFromMethods(fullPath, resource.Methods, fullHeaders, localSrv); err != nil { + return err + } + return nil +} + +func addAPIFromMethod(fullPath string, method config.Method, headers map[string]string, localSrv service.APIDiscoveryService) error { + api := fr.API{ + URLPattern: fullPath, + Method: method, + Headers: headers, + } + if err := localSrv.AddAPI(api); err != nil { + return errors.New(fmt.Sprintf("Path: %s, Method: %s, error: %s", fullPath, method.HTTPVerb, err.Error())) + } + return nil +} + +func modifyAPIFromMethod(fullPath string, new config.Method, old config.Method, headers map[string]string, localSrv service.APIDiscoveryService) error { + if err := localSrv.RemoveAPI(fullPath, old); err != nil { + return err + } + + if err := addAPIFromMethod(fullPath, new, headers, localSrv); err != nil { + return err + } + + return nil +} + +func deleteAPIFromMethod(fullPath string, deleted config.Method, localSrv service.APIDiscoveryService) error { + return localSrv.RemoveAPI(fullPath, deleted) +} + +func getFullPath(groupPath string, resourcePath string) string { + return groupPath + resourcePath +} + func loadAPIFromMethods(fullPath string, methods []config.Method, headers map[string]string, localSrv service.APIDiscoveryService) error { errStack := []string{} for _, method := range methods { - api := fr.API{ - URLPattern: fullPath, - Method: method, - Headers: headers, - } - if err := localSrv.AddAPI(api); err != nil { - errStack = append(errStack, fmt.Sprintf("Path: %s, Method: %s, error: %s", fullPath, method.HTTPVerb, err.Error())) + + if err := addAPIFromMethod(fullPath, method, headers, localSrv); err != nil { + errStack = append(errStack, err.Error()) } } if len(errStack) > 0 { diff --git a/pkg/service/discovery_service.go b/pkg/service/discovery_service.go index f4353facb..f26b1f6bc 100644 --- a/pkg/service/discovery_service.go +++ b/pkg/service/discovery_service.go @@ -63,10 +63,12 @@ var EmptyDiscoveryResponse = &DiscoveryResponse{} // APIDiscoveryService api discovery service interface type APIDiscoveryService interface { - pc.APIConfigListener + pc.APIConfigResourceListener AddAPI(router.API) error ClearAPI() error GetAPI(string, config.HTTPVerb) (router.API, error) + RemoveAPIByPath(deleted config.Resource) error + RemoveAPI(fullPath string, method config.Method) error } // DiscoveryService is come from envoy, it can used for admin diff --git a/samples/admin/sample.md b/samples/admin/sample.md index dd31c939b..efa39a0d6 100644 --- a/samples/admin/sample.md +++ b/samples/admin/sample.md @@ -48,14 +48,11 @@ curl "http://127.0.0.1:8888/api/v1/test-dubbo/user?name=tc" , the result: ## Start admin -run cmd/admin/admin.go +clone from https://github.com/dubbogo/pixiu-admin -config program arguments: -- -c /XXX/dubbo-go-proxy/samples/admin/admin/admin_config.yaml +cd pixiu-admin +run cmd/admin/admin.go -- run cmd `curl 127.0.0.1:8080/config/api` to check current proxy api config -- modify api_config.yaml content, such as change path `test-dubbo/user` to `test-dubbo/user_new` -- run cmd `curl "127.0.0.1:8080/config/api/set" -X POST --data-binary "@/xx/xx/dubbo-go-proxy/samples/admin/proxy/api_config.yaml"` to modify proxy api config -- then, `curl "http://127.0.0.1:8888/api/v1/test-dubbo/user?name=tc"` , the result will be 404 -- run `curl "http://127.0.0.1:8888/api/v1/test-dubbo/user_new?name=tc"` get the correct result +config program arguments: +- -c /xx/pixiu-admin/configs/admin_config.yaml \ No newline at end of file