From bee3972856ace158331337a8ca3e6c8e2b58698c Mon Sep 17 00:00:00 2001 From: David Calavera Date: Mon, 15 Jun 2015 15:35:49 -0700 Subject: [PATCH] Separate plugin sockets and specs. Check if there is a plugin socket first under `/run/docker/plugins/NAME.sock`. If there is no socket for a plugin, check `/etc/docker/plugins/NAME.spec` and `/usr/lib/docker/plugins/NAME.spec` for spec files. Signed-off-by: David Calavera --- plugins/discovery.go | 65 +++++++++++------------ plugins/discovery_test.go | 109 ++++++++++++++++++++++---------------- plugins/plugins.go | 2 +- 3 files changed, 94 insertions(+), 82 deletions(-) diff --git a/plugins/discovery.go b/plugins/discovery.go index 0160fd5..2128a92 100644 --- a/plugins/discovery.go +++ b/plugins/discovery.go @@ -11,10 +11,10 @@ import ( "strings" ) -const defaultLocalRegistry = "/usr/share/docker/plugins" - var ( ErrNotFound = errors.New("Plugin not found") + socketsPath = "/run/docker/plugins" + specsPaths = []string{"/etc/docker/plugins", "/usr/lib/docker/plugins"} ) type Registry interface { @@ -22,41 +22,39 @@ type Registry interface { Plugin(name string) (*Plugin, error) } -type LocalRegistry struct { - path string -} +type LocalRegistry struct{} -func newLocalRegistry(path string) *LocalRegistry { - if len(path) == 0 { - path = defaultLocalRegistry - } - - return &LocalRegistry{path} +func newLocalRegistry() LocalRegistry { + return LocalRegistry{} } func (l *LocalRegistry) Plugin(name string) (*Plugin, error) { - filepath := filepath.Join(l.path, name) - specpath := filepath + ".spec" - if fi, err := os.Stat(specpath); err == nil { - return readPluginSpecInfo(specpath, fi) + 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 + } } - socketpath := filepath + ".sock" - if fi, err := os.Stat(socketpath); err == nil { - return readPluginSocketInfo(socketpath, fi) + var txtspecpaths []string + for _, p := range specsPaths { + txtspecpaths = append(txtspecpaths, pluginPaths(p, name, ".spec")...) + txtspecpaths = append(txtspecpaths, pluginPaths(p, name, ".json")...) } - jsonpath := filepath + ".json" - if _, err := os.Stat(jsonpath); err == nil { - return readPluginJSONInfo(name, jsonpath) + 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 readPluginSpecInfo(path string, fi os.FileInfo) (*Plugin, error) { - name := strings.Split(fi.Name(), ".")[0] - +func readPluginInfo(name, path string) (*Plugin, error) { content, err := ioutil.ReadFile(path) if err != nil { return nil, err @@ -75,16 +73,6 @@ func readPluginSpecInfo(path string, fi os.FileInfo) (*Plugin, error) { return newLocalPlugin(name, addr), nil } -func readPluginSocketInfo(path string, fi os.FileInfo) (*Plugin, error) { - name := strings.Split(fi.Name(), ".")[0] - - if fi.Mode()&os.ModeSocket == 0 { - return nil, fmt.Errorf("%s is not a socket", path) - } - - return newLocalPlugin(name, "unix://"+path), nil -} - func readPluginJSONInfo(name, path string) (*Plugin, error) { f, err := os.Open(path) if err != nil { @@ -103,3 +91,10 @@ func readPluginJSONInfo(name, path string) (*Plugin, error) { return &p, nil } + +func pluginPaths(base, name, ext string) []string { + return []string{ + filepath.Join(base, name+ext), + filepath.Join(base, name, name+ext), + } +} diff --git a/plugins/discovery_test.go b/plugins/discovery_test.go index e6001c5..5610fe1 100644 --- a/plugins/discovery_test.go +++ b/plugins/discovery_test.go @@ -10,62 +10,72 @@ import ( "testing" ) -func TestUnknownLocalPath(t *testing.T) { +func setup(t *testing.T) (string, func()) { tmpdir, err := ioutil.TempDir("", "docker-test") if err != nil { t.Fatal(err) } - defer os.RemoveAll(tmpdir) + backup := socketsPath + socketsPath = tmpdir + specsPaths = []string{tmpdir} - l := newLocalRegistry(filepath.Join(tmpdir, "unknown")) - _, err = l.Plugin("foo") - if err == nil || err != ErrNotFound { - t.Fatalf("Expected error for unknown directory") + return tmpdir, func() { + socketsPath = backup + os.RemoveAll(tmpdir) } } func TestLocalSocket(t *testing.T) { - tmpdir, err := ioutil.TempDir("", "docker-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpdir) - l, err := net.Listen("unix", filepath.Join(tmpdir, "echo.sock")) - if err != nil { - t.Fatal(err) - } - defer l.Close() + tmpdir, unregister := setup(t) + defer unregister() - r := newLocalRegistry(tmpdir) - p, err := r.Plugin("echo") - if err != nil { - t.Fatal(err) + cases := []string{ + filepath.Join(tmpdir, "echo.sock"), + filepath.Join(tmpdir, "echo", "echo.sock"), } - pp, err := r.Plugin("echo") - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(p, pp) { - t.Fatalf("Expected %v, was %v\n", p, pp) - } + for _, c := range cases { + if err := os.MkdirAll(filepath.Dir(c), 0755); err != nil { + t.Fatal(err) + } - if p.Name != "echo" { - t.Fatalf("Expected plugin `echo`, got %s\n", p.Name) - } + l, err := net.Listen("unix", c) + if err != nil { + t.Fatal(err) + } - addr := fmt.Sprintf("unix://%s/echo.sock", tmpdir) - if p.Addr != addr { - t.Fatalf("Expected plugin addr `%s`, got %s\n", addr, p.Addr) + r := newLocalRegistry() + p, err := r.Plugin("echo") + if err != nil { + t.Fatal(err) + } + + pp, err := r.Plugin("echo") + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(p, pp) { + t.Fatalf("Expected %v, was %v\n", p, pp) + } + + if p.Name != "echo" { + t.Fatalf("Expected plugin `echo`, got %s\n", p.Name) + } + + addr := fmt.Sprintf("unix://%s", c) + if p.Addr != addr { + t.Fatalf("Expected plugin addr `%s`, got %s\n", addr, p.Addr) + } + if p.TLSConfig.InsecureSkipVerify != true { + t.Fatalf("Expected TLS verification to be skipped") + } + l.Close() } } func TestFileSpecPlugin(t *testing.T) { - tmpdir, err := ioutil.TempDir("", "docker-test-") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpdir) + tmpdir, unregister := setup(t) + defer unregister() cases := []struct { path string @@ -74,16 +84,21 @@ func TestFileSpecPlugin(t *testing.T) { fail bool }{ {filepath.Join(tmpdir, "echo.spec"), "echo", "unix://var/lib/docker/plugins/echo.sock", false}, + {filepath.Join(tmpdir, "echo", "echo.spec"), "echo", "unix://var/lib/docker/plugins/echo.sock", false}, {filepath.Join(tmpdir, "foo.spec"), "foo", "tcp://localhost:8080", false}, + {filepath.Join(tmpdir, "foo", "foo.spec"), "foo", "tcp://localhost:8080", false}, {filepath.Join(tmpdir, "bar.spec"), "bar", "localhost:8080", true}, // unknown transport } for _, c := range cases { - if err = ioutil.WriteFile(c.path, []byte(c.addr), 0644); err != nil { + if err := os.MkdirAll(filepath.Dir(c.path), 0755); err != nil { + t.Fatal(err) + } + if err := ioutil.WriteFile(c.path, []byte(c.addr), 0644); err != nil { t.Fatal(err) } - r := newLocalRegistry(tmpdir) + r := newLocalRegistry() p, err := r.Plugin(c.name) if c.fail && err == nil { continue @@ -100,14 +115,16 @@ func TestFileSpecPlugin(t *testing.T) { if p.Addr != c.addr { t.Fatalf("Expected plugin addr `%s`, got %s\n", c.addr, p.Addr) } + + if p.TLSConfig.InsecureSkipVerify != true { + t.Fatalf("Expected TLS verification to be skipped") + } } } func TestFileJSONSpecPlugin(t *testing.T) { - tmpdir, err := ioutil.TempDir("", "docker-test-") - if err != nil { - t.Fatal(err) - } + tmpdir, unregister := setup(t) + defer unregister() p := filepath.Join(tmpdir, "example.json") spec := `{ @@ -120,11 +137,11 @@ func TestFileJSONSpecPlugin(t *testing.T) { } }` - if err = ioutil.WriteFile(p, []byte(spec), 0644); err != nil { + if err := ioutil.WriteFile(p, []byte(spec), 0644); err != nil { t.Fatal(err) } - r := newLocalRegistry(tmpdir) + r := newLocalRegistry() plugin, err := r.Plugin("example") if err != nil { t.Fatal(err) diff --git a/plugins/plugins.go b/plugins/plugins.go index 642e9c8..a624e79 100644 --- a/plugins/plugins.go +++ b/plugins/plugins.go @@ -68,7 +68,7 @@ func (p *Plugin) activate() error { } func load(name string) (*Plugin, error) { - registry := newLocalRegistry("") + registry := newLocalRegistry() pl, err := registry.Plugin(name) if err != nil { return nil, err