pkg/plugins/discovery.go

133 lines
2.9 KiB
Go
Raw Normal View History

package plugins
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/url"
"os"
"path/filepath"
"strings"
"sync"
)
var (
// ErrNotFound plugin not found
pkg: authorization: do not register the same plugin This patches avoids registering (and calling) the same plugin more than once. Using an helper map which indexes by name guarantees this and keeps the order. The behavior of overriding the same name in a flag is consistent with, for instance, the `docker run -v /test -v /test` flag which register the volume just once. Adds integration tests. Without this patch: ``` Dec 20 19:34:52 localhost.localdomain docker[9988]: time="2015-12-20T19:34:52.080901676+01:00" level=debug msg="Calling GET /v1.22/info" Dec 20 19:34:52 localhost.localdomain docker[9988]: time="2015-12-20T19:34:52.081213202+01:00" level=debug msg="AuthZ request using plugin docker-novolume-plugin" Dec 20 19:34:52 localhost.localdomain docker[9988]: time="2015-12-20T19:34:52.081268132+01:00" level=debug msg="docker-novolume-plugin implements: authz" Dec 20 19:34:52 localhost.localdomain docker[9988]: time="2015-12-20T19:34:52.081699788+01:00" level=debug msg="AuthZ request using plugin docker-novolume-plugin" Dec 20 19:34:52 localhost.localdomain docker[9988]: time="2015-12-20T19:34:52.081762507+01:00" level=debug msg="docker-novolume-plugin implements: authz" Dec 20 19:34:52 localhost.localdomain docker[9988]: time="2015-12-20T19:34:52.082092480+01:00" level=debug msg="GET /v1.22/info" Dec 20 19:34:52 localhost.localdomain docker[9988]: time="2015-12-20T19:34:52.628691038+01:00" level=debug msg="AuthZ response using plugin docker-novolume-plugin" Dec 20 19:34:52 localhost.localdomain docker[9988]: time="2015-12-20T19:34:52.629880930+01:00" level=debug msg="AuthZ response using plugin docker-novolume-plugin" ``` With this patch: ``` Dec 20 19:37:32 localhost.localdomain docker[16620]: time="2015-12-20T19:37:32.376523958+01:00" level=debug msg="Calling GET /v1.22/info" Dec 20 19:37:32 localhost.localdomain docker[16620]: time="2015-12-20T19:37:32.376715483+01:00" level=debug msg="AuthZ request using plugin docker-novolume-plugin" Dec 20 19:37:32 localhost.localdomain docker[16620]: time="2015-12-20T19:37:32.376771230+01:00" level=debug msg="docker-novolume-plugin implements: authz" Dec 20 19:37:32 localhost.localdomain docker[16620]: time="2015-12-20T19:37:32.377698897+01:00" level=debug msg="GET /v1.22/info" Dec 20 19:37:32 localhost.localdomain docker[16620]: time="2015-12-20T19:37:32.951016441+01:00" level=debug msg="AuthZ response using plugin docker-novolume-plugin" ``` Also removes a somehow duplicate debug statement (leaving only the second one as it's a loop of plugin's manifest): ``` Dec 20 19:52:30 localhost.localdomain docker[25767]: time="2015-12-20T19:52:30.544090518+01:00" level=debug msg="docker-novolume-plugin's manifest: &{[authz]}" Dec 20 19:52:30 localhost.localdomain docker[25767]: time="2015-12-20T19:52:30.544170677+01:00" level=debug msg="docker-novolume-plugin implements: authz" ``` Signed-off-by: Antonio Murdaca <runcom@redhat.com>
2015-12-20 18:44:01 +00:00
ErrNotFound = errors.New("plugin not found")
socketsPath = "/run/docker/plugins"
specsPaths = []string{"/etc/docker/plugins", "/usr/lib/docker/plugins"}
)
// localRegistry defines a registry that is local (using unix socket).
type localRegistry struct{}
func newLocalRegistry() localRegistry {
return localRegistry{}
}
// Scan scans all the plugin paths and returns all the names it found
func Scan() ([]string, error) {
var names []string
if err := filepath.Walk(socketsPath, func(path string, fi os.FileInfo, err error) error {
if err != nil {
return nil
}
if fi.Mode()&os.ModeSocket != 0 {
name := strings.TrimSuffix(fi.Name(), filepath.Ext(fi.Name()))
names = append(names, name)
}
return nil
}); err != nil {
return nil, err
}
for _, path := range specsPaths {
if err := filepath.Walk(path, func(p string, fi os.FileInfo, err error) error {
if err != nil || fi.IsDir() {
return nil
}
name := strings.TrimSuffix(fi.Name(), filepath.Ext(fi.Name()))
names = append(names, name)
return nil
}); err != nil {
return nil, err
}
}
return names, nil
}
// Plugin returns the plugin registered with the given name (or returns an error).
func (l *localRegistry) Plugin(name string) (*Plugin, error) {
socketpaths := pluginPaths(socketsPath, name, ".sock")
for _, p := range socketpaths {
if fi, err := os.Stat(p); err == nil && fi.Mode()&os.ModeSocket != 0 {
return newLocalPlugin(name, "unix://"+p), nil
}
}
var txtspecpaths []string
for _, p := range specsPaths {
txtspecpaths = append(txtspecpaths, pluginPaths(p, name, ".spec")...)
txtspecpaths = append(txtspecpaths, pluginPaths(p, name, ".json")...)
}
for _, p := range txtspecpaths {
if _, err := os.Stat(p); err == nil {
if strings.HasSuffix(p, ".json") {
return readPluginJSONInfo(name, p)
}
return readPluginInfo(name, p)
}
}
return nil, ErrNotFound
}
func readPluginInfo(name, path string) (*Plugin, error) {
content, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
addr := strings.TrimSpace(string(content))
u, err := url.Parse(addr)
if err != nil {
return nil, err
}
if len(u.Scheme) == 0 {
return nil, fmt.Errorf("Unknown protocol")
}
return newLocalPlugin(name, addr), nil
}
func readPluginJSONInfo(name, path string) (*Plugin, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
var p Plugin
if err := json.NewDecoder(f).Decode(&p); err != nil {
return nil, err
}
p.Name = name
if len(p.TLSConfig.CAFile) == 0 {
p.TLSConfig.InsecureSkipVerify = true
}
p.activateWait = sync.NewCond(&sync.Mutex{})
return &p, nil
}
func pluginPaths(base, name, ext string) []string {
return []string{
filepath.Join(base, name+ext),
filepath.Join(base, name, name+ext),
}
}