Skip to content
This repository has been archived by the owner on Nov 8, 2022. It is now read-only.

Commit

Permalink
Fix: #812 Add type/name filter support for rest getPlugins
Browse files Browse the repository at this point in the history
Currently, the rest call getPlugins does not
support filtering based on name/type.

These capabilities are documented in the REST API
docs as:

/v1/plugins/:type and /v1/plugins/:type/:name

This commit adds this behavior by extending
the current code behind getPlugins to accept
parameters for name and type and use them to
filter results.
  • Loading branch information
bodepd committed Apr 7, 2016
1 parent b7ec954 commit 5b724a1
Show file tree
Hide file tree
Showing 3 changed files with 220 additions and 17 deletions.
63 changes: 48 additions & 15 deletions mgmt/rest/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,26 +259,31 @@ func (s *Server) unloadPlugin(w http.ResponseWriter, r *http.Request, p httprout
respond(200, pr, w)
}

func (s *Server) getPlugins(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
func (s *Server) getPlugins(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
var detail bool
// make this a function because DRY
for k := range r.URL.Query() {
if k == "details" {
detail = true
}
}
plName := params.ByName("name")
plType := params.ByName("type")
respond(200, getPlugins(s.mm, detail, r.Host, plName, plType), w)
}

func getPlugins(mm managesMetrics, detail bool, h string, plName string, plType string) *rbody.PluginList {

plCatalog := mm.PluginCatalog()

plugins := new(rbody.PluginList)
plugins := rbody.PluginList{}

// Cache the catalog here to avoid multiple reads
plCatalog := s.mm.PluginCatalog()
plugins.LoadedPlugins = make([]rbody.LoadedPlugin, len(plCatalog))
for i, p := range s.mm.PluginCatalog() {
plugins.LoadedPlugins[i] = *catalogedPluginToLoaded(r.Host, p)
for i, p := range plCatalog {
plugins.LoadedPlugins[i] = *catalogedPluginToLoaded(h, p)
}

if detail {
aPlugins := s.mm.AvailablePlugins()
aPlugins := mm.AvailablePlugins()
plugins.AvailablePlugins = make([]rbody.AvailablePlugin, len(aPlugins))
for i, p := range aPlugins {
plugins.AvailablePlugins[i] = rbody.AvailablePlugin{
Expand All @@ -292,7 +297,41 @@ func (s *Server) getPlugins(w http.ResponseWriter, r *http.Request, _ httprouter
}
}

respond(200, plugins, w)
filteredPlugins := rbody.PluginList{}

if plName != "" {
for _, p := range plugins.LoadedPlugins {
if p.Name == plName {
filteredPlugins.LoadedPlugins = append(filteredPlugins.LoadedPlugins, p)
}
}
for _, p := range plugins.AvailablePlugins {
if p.Name == plName {
filteredPlugins.AvailablePlugins = append(filteredPlugins.AvailablePlugins, p)
}
}
// update plugins so that further filters consider previous filters
plugins = filteredPlugins
}

filteredPlugins = rbody.PluginList{}

if plType != "" {
for _, p := range plugins.LoadedPlugins {
if p.Type == plType {
filteredPlugins.LoadedPlugins = append(filteredPlugins.LoadedPlugins, p)
}
}
for _, p := range plugins.AvailablePlugins {
if p.Type == plType {
filteredPlugins.AvailablePlugins = append(filteredPlugins.AvailablePlugins, p)
}
}
// filter based on type
plugins = filteredPlugins
}

return &plugins
}

func catalogedPluginToLoaded(host string, c core.CatalogedPlugin) *rbody.LoadedPlugin {
Expand All @@ -307,12 +346,6 @@ func catalogedPluginToLoaded(host string, c core.CatalogedPlugin) *rbody.LoadedP
}
}

func (s *Server) getPluginsByType(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
}

func (s *Server) getPluginsByName(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
}

func (s *Server) getPlugin(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
plName := p.ByName("name")
plType := p.ByName("type")
Expand Down
170 changes: 170 additions & 0 deletions mgmt/rest/plugin_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
/*
http://www.apache.org/licenses/LICENSE-2.0.txt
Copyright 2015 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package rest

import (
"testing"
"time"

"github.com/intelsdi-x/snap/control/plugin/cpolicy"
"github.com/intelsdi-x/snap/core"
"github.com/intelsdi-x/snap/core/serror"
. "github.com/smartystreets/goconvey/convey"
)

type MockLoadedPlugin struct {
MyName string
MyType string
}

func (m MockLoadedPlugin) Name() string { return m.MyName }
func (m MockLoadedPlugin) TypeName() string { return m.MyType }
func (m MockLoadedPlugin) Version() int { return 0 }
func (m MockLoadedPlugin) Plugin() string { return "" }
func (m MockLoadedPlugin) IsSigned() bool { return false }
func (m MockLoadedPlugin) Status() string { return "" }
func (m MockLoadedPlugin) PluginPath() string { return "" }
func (m MockLoadedPlugin) LoadedTimestamp() *time.Time {
now := time.Now()
return &now
}
func (m MockLoadedPlugin) Policy() *cpolicy.ConfigPolicy { return nil }

// have my mock object also support AvailablePlugin
func (m MockLoadedPlugin) HitCount() int { return 0 }
func (m MockLoadedPlugin) LastHit() time.Time {
return time.Now()
}
func (m MockLoadedPlugin) ID() uint32 { return 0 }

type MockManagesMetrics struct{}

func (m MockManagesMetrics) MetricCatalog() ([]core.CatalogedMetric, error) {
return nil, nil
}
func (m MockManagesMetrics) FetchMetrics([]string, int) ([]core.CatalogedMetric, error) {
return nil, nil
}
func (m MockManagesMetrics) GetMetricVersions([]string) ([]core.CatalogedMetric, error) {
return nil, nil
}
func (m MockManagesMetrics) GetMetric([]string, int) (core.CatalogedMetric, error) {
return nil, nil
}
func (m MockManagesMetrics) Load(*core.RequestedPlugin) (core.CatalogedPlugin, serror.SnapError) {
return nil, nil
}
func (m MockManagesMetrics) Unload(core.Plugin) (core.CatalogedPlugin, serror.SnapError) {
return nil, nil
}

func (m MockManagesMetrics) PluginCatalog() core.PluginCatalog {
return []core.CatalogedPlugin{
MockLoadedPlugin{MyName: "foo", MyType: "collector"},
MockLoadedPlugin{MyName: "bar", MyType: "publisher"},
MockLoadedPlugin{MyName: "foo", MyType: "collector"},
MockLoadedPlugin{MyName: "baz", MyType: "publisher"},
MockLoadedPlugin{MyName: "foo", MyType: "processor"},
MockLoadedPlugin{MyName: "foobar", MyType: "processor"},
}
}
func (m MockManagesMetrics) AvailablePlugins() []core.AvailablePlugin {
return []core.AvailablePlugin{
MockLoadedPlugin{MyName: "foo", MyType: "collector"},
MockLoadedPlugin{MyName: "bar", MyType: "publisher"},
MockLoadedPlugin{MyName: "foo", MyType: "collector"},
MockLoadedPlugin{MyName: "baz", MyType: "publisher"},
MockLoadedPlugin{MyName: "foo", MyType: "processor"},
MockLoadedPlugin{MyName: "foobar", MyType: "processor"},
}
}
func (m MockManagesMetrics) GetAutodiscoverPaths() []string {
return nil
}

func TestGetPlugins(t *testing.T) {
mm := MockManagesMetrics{}
host := "localhost"
Convey("Test getPlugns method", t, func() {
Convey("Without details", func() {
detail := false
Convey("Get All plugins", func() {
plName := ""
plType := ""
plugins := getPlugins(mm, detail, host, plName, plType)
So(len(plugins.LoadedPlugins), ShouldEqual, 6)
So(len(plugins.AvailablePlugins), ShouldEqual, 0)
})
Convey("Filter plugins by Type", func() {
plName := ""
plType := "publisher"
plugins := getPlugins(mm, detail, host, plName, plType)
So(len(plugins.LoadedPlugins), ShouldEqual, 2)
So(len(plugins.AvailablePlugins), ShouldEqual, 0)
})
Convey("Filter plugins by Type and Name", func() {
plName := "foo"
plType := "processor"
plugins := getPlugins(mm, detail, host, plName, plType)
So(len(plugins.LoadedPlugins), ShouldEqual, 1)
So(len(plugins.AvailablePlugins), ShouldEqual, 0)
})
Convey("Filter plugins by Type and Name expect duplicates", func() {
plName := "foo"
plType := "collector"
plugins := getPlugins(mm, detail, host, plName, plType)
So(len(plugins.LoadedPlugins), ShouldEqual, 2)
So(len(plugins.AvailablePlugins), ShouldEqual, 0)
})
})
Convey("With details", func() {
detail := true
Convey("Get All plugins", func() {
plName := ""
plType := ""
plugins := getPlugins(mm, detail, host, plName, plType)
So(len(plugins.LoadedPlugins), ShouldEqual, 6)
So(len(plugins.AvailablePlugins), ShouldEqual, 6)
})
Convey("Filter plugins by Type", func() {
plName := ""
plType := "publisher"
plugins := getPlugins(mm, detail, host, plName, plType)
So(len(plugins.LoadedPlugins), ShouldEqual, 2)
So(len(plugins.AvailablePlugins), ShouldEqual, 2)
})
Convey("Filter plugins by Type and Name", func() {
plName := "foo"
plType := "processor"
plugins := getPlugins(mm, detail, host, plName, plType)
So(len(plugins.LoadedPlugins), ShouldEqual, 1)
So(len(plugins.AvailablePlugins), ShouldEqual, 1)
})
Convey("Filter plugins by Type and Name expect duplicates", func() {
plName := "foo"
plType := "collector"
plugins := getPlugins(mm, detail, host, plName, plType)
So(len(plugins.LoadedPlugins), ShouldEqual, 2)
So(len(plugins.AvailablePlugins), ShouldEqual, 2)
})
})
})

}
4 changes: 2 additions & 2 deletions mgmt/rest/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,8 +284,8 @@ func (s *Server) BindConfigManager(c managesConfig) {
func (s *Server) addRoutes() {
// plugin routes
s.r.GET("/v1/plugins", s.getPlugins)
s.r.GET("/v1/plugins/:type", s.getPluginsByType)
s.r.GET("/v1/plugins/:type/:name", s.getPluginsByName)
s.r.GET("/v1/plugins/:type", s.getPlugins)
s.r.GET("/v1/plugins/:type/:name", s.getPlugins)
s.r.GET("/v1/plugins/:type/:name/:version", s.getPlugin)
s.r.POST("/v1/plugins", s.loadPlugin)
s.r.DELETE("/v1/plugins/:type/:name/:version", s.unloadPlugin)
Expand Down

0 comments on commit 5b724a1

Please sign in to comment.