Skip to content

Commit

Permalink
feat: add admin api
Browse files Browse the repository at this point in the history
  • Loading branch information
edocevol committed Nov 14, 2022
1 parent c2a3583 commit 0a9d157
Show file tree
Hide file tree
Showing 6 changed files with 301 additions and 10 deletions.
137 changes: 137 additions & 0 deletions client/admin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package client

import (
"fmt"

"github.com/emicklei/go-restful/v3"
restfulspec "github.com/polarismesh/go-restful-openapi/v2"

"github.com/polaris-contrib/polaris-server-remote-plugin-common/log"
)

// Resource plugin client resources handler.
type Resource struct {
}

// NewResource returns a new Resource
func NewResource() *Resource {
return &Resource{}
}

// Plugin plugin model
type Plugin struct {
Name string `json:"name"`
Path string `json:"path"`
Config *Config `json:"config"`
}

// WebService return restful web service
func (resource *Resource) WebService() *restful.WebService {
ws := new(restful.WebService)
ws.Path("/admin/plugins").Consumes(restful.MIME_JSON).Produces(restful.MIME_JSON)
tags := []string{"plugin"}

ws.Route(
ws.GET("").To(resource.findAll).
Doc("get all plugin").
Metadata(restfulspec.KeyOpenAPITags, tags).
Writes([]Plugin{}).
Returns(200, "OK", []Plugin{}).
DefaultReturns("OK", []Plugin{}),
)

ws.Route(
ws.PUT("{name}/disable").To(resource.disable).
Doc("disable the given name plugin").
Metadata(restfulspec.KeyOpenAPITags, tags).
Returns(200, "OK", nil).
DefaultReturns("OK", nil),
)

ws.Route(
ws.PUT("{name}/enable").To(resource.disable).
Doc("enable the given name plugin").
Metadata(restfulspec.KeyOpenAPITags, tags).
Returns(200, "OK", nil).
DefaultReturns("OK", nil),
)

return ws
}

// findAll find and return all plugins
//
// GET http://localhost:9050/admin/plugins
func (resource *Resource) findAll(_ *restful.Request, res *restful.Response) {
var plugins []Plugin
for name, plugin := range factory.pluginSet {
plugins = append(plugins, Plugin{
Name: name,
Path: plugin.pluginPath,
Config: plugin.config,
})
}
_ = res.WriteEntity(plugins)
}

// disable disable one plugin by plugin name.
//
// PUT http://localhost:9050/admin/plugins/{name}/disable
func (resource *Resource) disable(req *restful.Request, res *restful.Response) {
name := req.PathParameter("name")
if name == "" {
responseError(res, 400, fmt.Errorf("plugin name is required"))
return
}

plugin := factory.Get(name)
if plugin == nil {
responseError(res, 404, fmt.Errorf("plugin with name %s is not exists", name))
return
}

err := plugin.Disable()
if err != nil {
responseError(res, 500, fmt.Errorf("fail to disable plugin: %w", err))
return
}

_ = res.WriteEntity(map[string]interface{}{"success": "true"})
return
}

// enable enable one plugin by plugin name.
//
// PUT http://localhost:9050/admin/plugins/{name}/enable
func (resource *Resource) enable(req *restful.Request, res *restful.Response) {
name := req.PathParameter("name")
if name == "" {
responseError(res, 400, fmt.Errorf("plugin name is required"))
return
}

plugin := factory.Get(name)
if plugin == nil {
responseError(res, 404, fmt.Errorf("plugin with name %s is not exists", name))
return
}

err := plugin.Open()
if err != nil {
responseError(res, 500, fmt.Errorf("fail to enable plugin: %w", err))
return
}
_ = res.WriteEntity(map[string]interface{}{"success": "true"})
return
}

// ResError error response
type ResError struct {
Err error `json:"err"`
}

func responseError(res *restful.Response, code int, err error) {
log.Error("admin api returns a error response", "error_response", err)
_ = res.WriteHeaderAndJson(code, map[string]interface{}{"error": err.Error()}, restful.MIME_JSON)
return
}
66 changes: 66 additions & 0 deletions client/admin_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package client

import (
"fmt"
"net/http"
"os"
"os/exec"
"testing"

"github.com/emicklei/go-restful/v3"

"github.com/polaris-contrib/polaris-server-remote-plugin-common/log"
)

func TestMain(m *testing.M) {
cmd := &exec.Cmd{
Path: "/usr/bin/make",
Args: append([]string{"/usr/bin/make"}, "build"),
Dir: "../",
}

err := cmd.Run()
if err != nil {
log.Fatal("got error", "error", err.Error())
os.Exit(-1)
}

m.Run()
}

// TestAdminAPI 测试 API server
func TestAdminAPI(t *testing.T) {
if _, err := Register(
&Config{
Name: "remote-rate-limit-server-v1",
Mode: RumModelLocal,
Local: LocalConfig{
MaxProcs: 1,
Path: "../remote-rate-limit-server-v1",
},
},
); err != nil {
log.Fatal("server-v1 register failed", "error", err.Error())
return
}

if _, err := Register(
&Config{
Name: "remote-rate-limit-server-v2",
Mode: RumModelLocal,
Local: LocalConfig{
Path: "../remote-rate-limit-server-v2",
},
},
); err != nil {
log.Fatal("server-v2 register failed", "error", err.Error())
}

restful.DefaultContainer.Add(NewResource().WebService())
adminPort := 9050

log.Info(fmt.Sprintf("request the admin api using http://localhost:%d", adminPort))
if err := http.ListenAndServe(fmt.Sprintf(":%d", adminPort), nil); err != nil {
log.Fatal("plugin admin serve error", "error", err)
}
}
5 changes: 5 additions & 0 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ func (c *Client) Close() error {
func (c *Client) Disable() error {
c.Lock()
defer c.Unlock()

if c.enable == false {
return fmt.Errorf("plugin %s alread disbled", c.pluginName)
}

c.enable = false

if c.pluginClient != nil {
Expand Down
34 changes: 28 additions & 6 deletions client/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,46 @@ package client

import "sync"

var pluginSet = make(map[string]*Client)
var locker sync.Mutex
// factory
var factory = NewFactory()

// Factory plugin client factory.
type Factory struct {
// pluginSet plugin named client set.
pluginSet map[string]*Client
// locker locker for pluginSet update/delete
locker sync.Mutex
}

// NewFactory returns a new Factory.
func NewFactory() *Factory {
return &Factory{pluginSet: make(map[string]*Client)}
}

// Get return client by name, return nil if the named plugin not exists.
func (f *Factory) Get(name string) *Client {
client, ex := f.pluginSet[name]
if !ex {
return nil
}
return client
}

// Register called by plugin client and start up the plugin main process.
func Register(config *Config) (*Client, error) {
locker.Lock()
defer locker.Unlock()
factory.locker.Lock()
defer factory.locker.Unlock()

name := config.Name
if c, ok := pluginSet[name]; ok {
if c, ok := factory.pluginSet[name]; ok {
return c, nil
}

c, err := newClient(config)
if err != nil {
return nil, err
}
pluginSet[name] = c
factory.pluginSet[name] = c

return c, nil
}
18 changes: 16 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,42 @@ module github.com/polaris-contrib/polaris-server-remote-plugin-common
go 1.17

require (
github.com/emicklei/go-restful/v3 v3.10.0
github.com/fsnotify/fsnotify v1.6.0
github.com/golang/mock v1.6.0
github.com/golang/protobuf v1.5.2
github.com/hashicorp/go-hclog v1.3.1
github.com/hashicorp/go-plugin v1.4.6
github.com/polarismesh/go-restful-openapi/v2 v2.0.0-20220928152401-083908d10219
go.uber.org/zap v1.23.0
golang.org/x/time v0.2.0
google.golang.org/grpc v1.50.1
google.golang.org/protobuf v1.28.1
)

require (
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.6 // indirect
github.com/go-openapi/spec v0.20.4 // indirect
github.com/go-openapi/swag v0.19.15 // indirect
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/mailru/easyjson v0.7.6 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 // indirect
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/oklog/run v1.0.0 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect
golang.org/x/net v0.0.0-20210421230115-4e50805a0758 // indirect
golang.org/x/sys v0.0.0-20220908164124-27713097b956 // indirect
golang.org/x/text v0.3.3 // indirect
golang.org/x/text v0.3.7 // indirect
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
Loading

0 comments on commit 0a9d157

Please sign in to comment.