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

Commit

Permalink
Fixes #385: Added swap plugins to snapctl and client
Browse files Browse the repository at this point in the history
  • Loading branch information
tiffanyfay committed May 23, 2016
1 parent 3b5c732 commit eff6b76
Show file tree
Hide file tree
Showing 5 changed files with 228 additions and 2 deletions.
11 changes: 11 additions & 0 deletions cmd/snapctl/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,17 @@ var (
flPluginVersion,
},
},
{
Name: "swap",
Usage: "swap <load_plugin_path> <unload_plugin_type>:<unload_plugin_name>:<unload_plugin_version> or swap <load_plugin_path> -t <unload_plugin_type> -n <unload_plugin_name> -v <unload_plugin_version>",
Action: swapPlugins,
Flags: []cli.Flag{
flPluginAsc,
flPluginType,
flPluginName,
flPluginVersion,
},
},
{
Name: "list",
Usage: "list",
Expand Down
84 changes: 82 additions & 2 deletions cmd/snapctl/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,7 @@ func loadPlugin(ctx *cli.Context) {

func unloadPlugin(ctx *cli.Context) {
pDetails := filepath.SplitList(ctx.Args().First())
var pType string
var pName string
var pType, pName string
var pVer int
var err error

Expand Down Expand Up @@ -116,6 +115,87 @@ func unloadPlugin(ctx *cli.Context) {
fmt.Printf("Type: %s\n", r.Type)
}

func swapPlugins(ctx *cli.Context) {
// plugin to load
pAsc := ctx.String("plugin-asc")
var paths []string
if len(ctx.Args()) < 1 || len(ctx.Args()) > 2 {
fmt.Println("Incorrect usage:")
cli.ShowCommandHelp(ctx, ctx.Command.Name)
os.Exit(1)
}
paths = append(paths, ctx.Args().First())
if pAsc != "" {
if !strings.Contains(pAsc, ".asc") {
fmt.Println("Must be a .asc file for the -a flag")
cli.ShowCommandHelp(ctx, ctx.Command.Name)
os.Exit(1)
}
paths = append(paths, pAsc)
}

// plugin to unload
var pDetails []string
var pType, pName string
var pVer int
var err error

if len(ctx.Args()) == 2 {
pDetails = filepath.SplitList(ctx.Args()[1])
if len(pDetails) == 3 {
pType = pDetails[0]
pName = pDetails[1]
pVer, err = strconv.Atoi(pDetails[2])
if err != nil {
fmt.Println("Can't convert version string to integer")
cli.ShowCommandHelp(ctx, ctx.Command.Name)
os.Exit(1)
}
} else {
fmt.Println("Missing type, name, or version")
cli.ShowCommandHelp(ctx, ctx.Command.Name)
os.Exit(1)
}
} else {
pType = ctx.String("plugin-type")
pName = ctx.String("plugin-name")
pVer = ctx.Int("plugin-version")
}
if pType == "" {
fmt.Println("Must provide plugin type")
cli.ShowCommandHelp(ctx, ctx.Command.Name)
os.Exit(1)
}
if pName == "" {
fmt.Println("Must provide plugin name")
cli.ShowCommandHelp(ctx, ctx.Command.Name)
os.Exit(1)
}
if pVer < 1 {
fmt.Println("Must provide plugin version")
cli.ShowCommandHelp(ctx, ctx.Command.Name)
os.Exit(1)
}

r := pClient.SwapPlugin(paths, pType, pName, pVer)
if r.Err != nil {
fmt.Printf("Error swapping plugins:\n%v\n", r.Err.Error())
os.Exit(1)
}

fmt.Println("Plugin loaded")
fmt.Printf("Name: %s\n", r.LoadedPlugin.Name)
fmt.Printf("Version: %d\n", r.LoadedPlugin.Version)
fmt.Printf("Type: %s\n", r.LoadedPlugin.Type)
fmt.Printf("Signed: %v\n", r.LoadedPlugin.Signed)
fmt.Printf("Loaded Time: %s\n\n", r.LoadedPlugin.LoadedTime().Format(timeFormat))

fmt.Println("\nPlugin unloaded")
fmt.Printf("Name: %s\n", r.UnloadedPlugin.Name)
fmt.Printf("Version: %d\n", r.UnloadedPlugin.Version)
fmt.Printf("Type: %s\n", r.UnloadedPlugin.Type)
}

func listPlugins(ctx *cli.Context) {
plugins := pClient.GetPlugins(ctx.Bool("running"))
if plugins.Err != nil {
Expand Down
43 changes: 43 additions & 0 deletions mgmt/rest/client/client_func_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,49 @@ func TestSnapClient(t *testing.T) {
})
})

Convey("SwapPlugins", t, func() {
Convey("Swap with different types should fail", func() {
sp := c.SwapPlugin(FILE_PLUGIN_PATH, p1.LoadedPlugins[0].Type, p1.LoadedPlugins[0].Name, p1.LoadedPlugins[0].Version)
So(sp.Err, ShouldNotBeNil)
So(sp.Err.Error(), ShouldEqual, "Plugins do not have the same type and name.")
lps := c.GetPlugins(false)
So(len(lps.LoadedPlugins), ShouldEqual, 1)
})
Convey("Swap with same plugin should fail", func() {
sp := c.SwapPlugin(MOCK_PLUGIN_PATH1, p1.LoadedPlugins[0].Type, p1.LoadedPlugins[0].Name, p1.LoadedPlugins[0].Version)
So(sp.Err, ShouldNotBeNil)
So(sp.Err.Error(), ShouldEqual, "plugin is already loaded")
lps := c.GetPlugins(false)
So(len(lps.LoadedPlugins), ShouldEqual, 1)
})
Convey("Swap with plugin that is not loaded should fail", func() {
sp := c.SwapPlugin(MOCK_PLUGIN_PATH1, "collector", "mock", 2)
So(sp.Err.Error(), ShouldEqual, "plugin not found collector:mock:2")
So(sp.Err, ShouldNotBeNil)
})
Convey("Swap with plugins with the same type and name", func() {
sp := c.SwapPlugin(MOCK_PLUGIN_PATH2, p1.LoadedPlugins[0].Type, p1.LoadedPlugins[0].Name, p1.LoadedPlugins[0].Version)
So(sp.Err, ShouldBeNil)
lps := c.GetPlugins(false)
So(len(lps.LoadedPlugins), ShouldEqual, 1)
So(lps.LoadedPlugins[0].Type, ShouldEqual, "collector")
So(lps.LoadedPlugins[0].Name, ShouldEqual, "mock")
So(lps.LoadedPlugins[0].Type, ShouldEqual, p1.LoadedPlugins[0].Type)
So(lps.LoadedPlugins[0].Name, ShouldEqual, p1.LoadedPlugins[0].Name)
So(lps.LoadedPlugins[0].Version, ShouldNotEqual, p1.LoadedPlugins[0].Version)

sp2 := c.SwapPlugin(MOCK_PLUGIN_PATH1, sp.LoadedPlugin.Type, sp.LoadedPlugin.Name, sp.LoadedPlugin.Version)
So(sp2.Err, ShouldBeNil)
lps2 := c.GetPlugins(false)
So(len(lps.LoadedPlugins), ShouldEqual, 1)
So(lps2.LoadedPlugins[0].Type, ShouldEqual, "collector")
So(lps2.LoadedPlugins[0].Name, ShouldEqual, "mock")
So(lps2.LoadedPlugins[0].Type, ShouldEqual, sp.LoadedPlugin.Type)
So(lps2.LoadedPlugins[0].Name, ShouldEqual, sp.LoadedPlugin.Name)
So(lps2.LoadedPlugins[0].Version, ShouldNotEqual, sp.LoadedPlugin.Version)
})
})

if cerr == nil {
p2 = c.LoadPlugin(MOCK_PLUGIN_PATH2)
}
Expand Down
90 changes: 90 additions & 0 deletions mgmt/rest/client/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ limitations under the License.
package client

import (
"errors"
"fmt"
"net/url"
"strconv"
"time"

"github.com/intelsdi-x/snap/core/serror"
Expand Down Expand Up @@ -78,6 +80,50 @@ func (c *Client) UnloadPlugin(pluginType, name string, version int) *UnloadPlugi
return r
}

// SwapPlugin swaps two plugins with the same type and name e.g. collector:mock:1 with collector:mock:2
func (c *Client) SwapPlugin(loadPath []string, unloadType, unloadName string, unloadVersion int) *SwapPluginsResult {
r := &SwapPluginsResult{}

// Check if plugin you are trying to unload is loaded
rp := c.GetPlugin(unloadType, unloadName, unloadVersion)
if rp.Err != nil {
r.Err = fmt.Errorf("%v %v:%v:%v", rp.Err.Error(), unloadType, unloadName, unloadVersion)
return r
}
// Load plugin
lp := c.LoadPlugin(loadPath)
if lp.Err != nil {
r.Err = errors.New(lp.Err.Error())
return r
}
lpr := lp.LoadedPlugins[0].LoadedPlugin

// Make sure both plugins have the same type and name before unloading. If not, rollback.
if lpr.Type != unloadType || lpr.Name != unloadName {
up := c.UnloadPlugin(lpr.Type, lpr.Name, lpr.Version)
if up.Err != nil {
r.Err = errors.New("Plugins do not have the same type and name. Failed to rollback after error.")
return r
}
r.Err = errors.New("Plugins do not have the same type and name.")
return r
}
// Unload plugin
up := c.UnloadPlugin(unloadType, unloadName, unloadVersion)
if up.Err != nil {
r.Err = up.Err
up2 := c.UnloadPlugin(lpr.Type, lpr.Name, lpr.Version)
if up2.Err != nil {
r.Err = errors.New("Failed to rollback after error unloading plugin.")
}
return r
}
upr := up.PluginUnloaded
r.LoadedPlugin = lp.LoadedPlugins[0]
r.UnloadedPlugin = upr
return r
}

// GetPlugins returns the loaded and available plugins through an HTTP GET request.
// By specifying the details flag to tweak output info. An error returns if it failed.
func (c *Client) GetPlugins(details bool) *GetPluginsResult {
Expand Down Expand Up @@ -112,6 +158,39 @@ func (c *Client) GetPlugins(details bool) *GetPluginsResult {
return r
}

// GetPlugin returns the requested plugin through an HTTP GET request. An error returns if it failed.
func (c *Client) GetPlugin(typ, name string, ver int) *GetPluginResult {
r := &GetPluginResult{}

path := "/plugins/" + typ + "/" + name + "/" + strconv.Itoa(ver)

resp, err := c.do("GET", path, ContentTypeJSON)
if err != nil {
r.Err = err
return r
}

switch resp.Meta.Type {
// TODO change this to concrete const type when Joel adds it
case rbody.PluginReturnedType:
// Success
b := resp.Body.(*rbody.PluginReturned)
r.ReturnedPlugin = ReturnedPlugin{b}
return r
case rbody.ErrorType:
r.Err = resp.Body.(*rbody.Error)
default:
r.Err = ErrAPIResponseMetaType
}
return r
}

// GetPluginResult
type GetPluginResult struct {
ReturnedPlugin ReturnedPlugin
Err error
}

// GetPluginsResult is the response from snap/client on a GetPlugins call.
type GetPluginsResult struct {
LoadedPlugins []LoadedPlugin
Expand All @@ -131,6 +210,12 @@ type UnloadPluginResult struct {
Err error
}

type SwapPluginsResult struct {
LoadedPlugin LoadedPlugin
UnloadedPlugin *rbody.PluginUnloaded
Err error
}

// We wrap this so we can provide some functionality (like LoadedTime)
type LoadedPlugin struct {
*rbody.LoadedPlugin
Expand All @@ -146,6 +231,11 @@ type AvailablePlugin struct {
*rbody.AvailablePlugin
}

// The wrapper for ReturnedPlugin struct defined inside rbody package.
type ReturnedPlugin struct {
*rbody.PluginReturned
}

func convertLoadedPlugins(r []rbody.LoadedPlugin) []LoadedPlugin {
lp := make([]LoadedPlugin, len(r))
for i := range r {
Expand Down
2 changes: 2 additions & 0 deletions mgmt/rest/rbody/body.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ func UnmarshalBody(t string, b []byte) (Body, error) {
return unmarshalAndHandleError(b, &PluginsLoaded{})
case PluginUnloadedType:
return unmarshalAndHandleError(b, &PluginUnloaded{})
case PluginReturnedType:
return unmarshalAndHandleError(b, &PluginReturned{})
case ScheduledTaskListReturnedType:
return unmarshalAndHandleError(b, &ScheduledTaskListReturned{})
case ScheduledTaskReturnedType:
Expand Down

0 comments on commit eff6b76

Please sign in to comment.