plugin_test.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. package plugins // import "github.com/docker/docker/pkg/plugins"
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "io"
  6. "net/http"
  7. "os"
  8. "path/filepath"
  9. "runtime"
  10. "sync"
  11. "testing"
  12. "time"
  13. "github.com/docker/docker/pkg/plugins/transport"
  14. "github.com/docker/go-connections/tlsconfig"
  15. "github.com/pkg/errors"
  16. "gotest.tools/v3/assert"
  17. is "gotest.tools/v3/assert/cmp"
  18. )
  19. const (
  20. fruitPlugin = "fruit"
  21. fruitImplements = "apple"
  22. )
  23. // regression test for deadlock in handlers
  24. func TestPluginAddHandler(t *testing.T) {
  25. t.Parallel()
  26. // make a plugin which is pre-activated
  27. p := &Plugin{activateWait: sync.NewCond(&sync.Mutex{})}
  28. p.Manifest = &Manifest{Implements: []string{"bananas"}}
  29. storage.Lock()
  30. storage.plugins["qwerty"] = p
  31. storage.Unlock()
  32. testActive(t, p)
  33. Handle("bananas", func(_ string, _ *Client) {})
  34. testActive(t, p)
  35. }
  36. func TestPluginWaitBadPlugin(t *testing.T) {
  37. p := &Plugin{activateWait: sync.NewCond(&sync.Mutex{})}
  38. p.activateErr = errors.New("some junk happened")
  39. testActive(t, p)
  40. }
  41. func testActive(t *testing.T, p *Plugin) {
  42. done := make(chan struct{})
  43. go func() {
  44. p.waitActive()
  45. close(done)
  46. }()
  47. select {
  48. case <-time.After(100 * time.Millisecond):
  49. _, f, l, _ := runtime.Caller(1)
  50. t.Fatalf("%s:%d: deadlock in waitActive", filepath.Base(f), l)
  51. case <-done:
  52. }
  53. }
  54. func TestGet(t *testing.T) {
  55. // TODO: t.Parallel()
  56. // TestPluginWithNoManifest also registers fruitPlugin
  57. p := &Plugin{name: fruitPlugin, activateWait: sync.NewCond(&sync.Mutex{})}
  58. p.Manifest = &Manifest{Implements: []string{fruitImplements}}
  59. storage.Lock()
  60. storage.plugins[fruitPlugin] = p
  61. storage.Unlock()
  62. t.Run("success", func(t *testing.T) {
  63. plugin, err := Get(fruitPlugin, fruitImplements)
  64. assert.NilError(t, err)
  65. assert.Check(t, is.Equal(p.Name(), plugin.Name()))
  66. assert.Check(t, is.Nil(plugin.Client()))
  67. assert.Check(t, plugin.IsV1())
  68. })
  69. // check negative case where plugin fruit doesn't implement banana
  70. t.Run("not implemented", func(t *testing.T) {
  71. _, err := Get("fruit", "banana")
  72. assert.Check(t, is.ErrorIs(err, ErrNotImplements))
  73. })
  74. // check negative case where plugin vegetable doesn't exist
  75. t.Run("not exists", func(t *testing.T) {
  76. _, err := Get(testNonExistingPlugin, "no-such-implementation")
  77. assert.Check(t, is.ErrorIs(err, ErrNotFound))
  78. })
  79. }
  80. func TestPluginWithNoManifest(t *testing.T) {
  81. // TODO: t.Parallel()
  82. // TestGet also registers fruitPlugin
  83. mux, addr := setupRemotePluginServer(t)
  84. m := Manifest{[]string{fruitImplements}}
  85. var buf bytes.Buffer
  86. err := json.NewEncoder(&buf).Encode(m)
  87. assert.NilError(t, err)
  88. mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
  89. assert.Assert(t, is.Equal(r.Method, http.MethodPost))
  90. header := w.Header()
  91. header.Set("Content-Type", transport.VersionMimetype)
  92. io.Copy(w, &buf)
  93. })
  94. p := &Plugin{
  95. name: fruitPlugin,
  96. activateWait: sync.NewCond(&sync.Mutex{}),
  97. Addr: addr,
  98. TLSConfig: &tlsconfig.Options{InsecureSkipVerify: true},
  99. }
  100. storage.Lock()
  101. storage.plugins[fruitPlugin] = p
  102. storage.Unlock()
  103. plugin, err := Get(fruitPlugin, fruitImplements)
  104. assert.NilError(t, err)
  105. assert.Check(t, is.Equal(p.name, plugin.Name()))
  106. }
  107. func TestGetAll(t *testing.T) {
  108. t.Parallel()
  109. tmpdir := t.TempDir()
  110. r := LocalRegistry{
  111. socketsPath: tmpdir,
  112. specsPaths: []string{tmpdir},
  113. }
  114. p := filepath.Join(tmpdir, "example.json")
  115. spec := `{
  116. "Name": "example",
  117. "Addr": "https://example.com/docker/plugin"
  118. }`
  119. err := os.WriteFile(p, []byte(spec), 0o644)
  120. assert.NilError(t, err)
  121. plugin, err := r.Plugin("example")
  122. assert.NilError(t, err)
  123. plugin.Manifest = &Manifest{Implements: []string{"apple"}}
  124. storage.Lock()
  125. storage.plugins["example"] = plugin
  126. storage.Unlock()
  127. fetchedPlugins, err := r.GetAll("apple")
  128. assert.NilError(t, err)
  129. assert.Check(t, is.Len(fetchedPlugins, 1))
  130. assert.Check(t, is.Equal(fetchedPlugins[0].Name(), plugin.Name()))
  131. }