func (lp *loadedPlugin) Key() string {
	return fmt.Sprintf("%s"+core.Separator+"%s"+core.Separator+"%d", lp.TypeName(), lp.Name(), lp.Version())
}
Key returns plugin type, name and version
package control
import (
	"crypto/sha256"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"
	"github.com/appc/spec/schema"
	log "github.com/sirupsen/logrus"
	"github.com/intelsdi-x/gomit"
	"github.com/intelsdi-x/snap/control/plugin"
	"github.com/intelsdi-x/snap/control/plugin/client"
	"github.com/intelsdi-x/snap/control/plugin/cpolicy"
	"github.com/intelsdi-x/snap/core"
	"github.com/intelsdi-x/snap/core/cdata"
	"github.com/intelsdi-x/snap/core/serror"
)
const (
	DetectedState pluginState = "detected"
	LoadingState pluginState = "loading"
	LoadedState pluginState = "loaded"
	UnloadedState pluginState = "unloaded"
)
var (
	ErrPluginNotFound = errors.New("plugin not found")
	ErrPluginAlreadyLoaded = errors.New("plugin is already loaded")
	ErrPluginCannotBeUnloaded = errors.New("Plugin is used by running task. Stop the task to be able to unload the plugin")
	ErrPluginNotInLoadedState = errors.New("Plugin must be in a LoadedState")
	pmLogger = log.WithField("_module", "control-plugin-mgr")
	defaultManagerOpts = []pluginManagerOpt{optDefaultManagerSecurity()}
)
func errorPluginCannotBeUnloaded(impactedTaskIDs []string) error {
	var impactedTasks string
	for _, id := range impactedTaskIDs {
		impactedTasks += fmt.Sprintf("\n%s", id)
	}
	return fmt.Errorf("%s:%s", ErrPluginCannotBeUnloaded, impactedTasks)
}
type pluginState string
type loadedPlugins struct {
	*sync.RWMutex
	table map[string]*loadedPlugin
}
func newLoadedPlugins() *loadedPlugins {
	return &loadedPlugins{
		RWMutex: &sync.RWMutex{},
		table:   make(map[string]*loadedPlugin),
	}
}
func (l *loadedPlugins) add(lp *loadedPlugin) serror.SnapError {
	l.Lock()
	defer l.Unlock()
	if _, exists := l.table[lp.Key()]; exists {
		return serror.New(ErrPluginAlreadyLoaded, map[string]interface{}{
			"plugin-name":    lp.Meta.Name,
			"plugin-version": lp.Meta.Version,
			"plugin-type":    lp.Type.String(),
		})
	}
	l.table[lp.Key()] = lp
	return nil
}
func (l *loadedPlugins) get(key string) (*loadedPlugin, error) {
	l.RLock()
	defer l.RUnlock()
	lp, ok := l.table[key]
	if !ok {
		tnv := strings.Split(key, core.Separator)
		if len(tnv) != 3 {
			return nil, ErrBadKey
		}
		v, err := strconv.Atoi(tnv[2])
		if err != nil {
			return nil, ErrBadKey
		}
		if v < 1 {
			pmLogger.Info("finding latest plugin")
			return l.findLatest(tnv[0], tnv[1])
		}
		return nil, ErrPluginNotFound
	}
	return lp, nil
}
func (l *loadedPlugins) remove(key string) {
	l.Lock()
	delete(l.table, key)
	l.Unlock()
}
func (l *loadedPlugins) findLatest(typeName, name string) (*loadedPlugin, error) {
	l.RLock()
	defer l.RUnlock()
	var latest *loadedPlugin
	for _, lp := range l.table {
		if lp.TypeName() == typeName && lp.Name() == name {
			latest = lp
			break
		}
	}
	if latest != nil {
		for _, lp := range l.table {
			if lp.TypeName() == typeName && lp.Name() == name && lp.Version() > latest.Version() {
				latest = lp
			}
		}
		return latest, nil
	}
	return nil, ErrPluginNotFound
}
type pluginDetails struct {
	CheckSum    [sha256.Size]byte
	Exec        []string
	ExecPath    string
	IsPackage   bool
	Manifest    *schema.ImageManifest
	Path        string
	Signed      bool
	Signature   []byte
	CertPath    string
	KeyPath     string
	CACertPaths string
	TLSEnabled  bool
	Uri         *url.URL
}
type loadedPlugin struct {
	Meta         plugin.PluginMeta
	Details      *pluginDetails
	Type         plugin.PluginType
	State        pluginState
	Token        string
	LoadedTime   time.Time
	ConfigPolicy *cpolicy.ConfigPolicy
}
func (lp *loadedPlugin) Name() string {
	return lp.Meta.Name
}
func (lp *loadedPlugin) PluginPath() string {
	return lp.Details.Path
}