controller_test.go 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. package plugin // import "github.com/docker/docker/daemon/cluster/controllers/plugin"
  2. import (
  3. "context"
  4. "errors"
  5. "io"
  6. "net/http"
  7. "strings"
  8. "testing"
  9. "time"
  10. "github.com/containerd/log"
  11. "github.com/distribution/reference"
  12. "github.com/docker/docker/api/types"
  13. "github.com/docker/docker/api/types/backend"
  14. "github.com/docker/docker/api/types/registry"
  15. "github.com/docker/docker/api/types/swarm/runtime"
  16. "github.com/docker/docker/plugin"
  17. v2 "github.com/docker/docker/plugin/v2"
  18. "github.com/moby/pubsub"
  19. "github.com/sirupsen/logrus"
  20. )
  21. const (
  22. pluginTestName = "test"
  23. pluginTestRemote = "testremote"
  24. pluginTestRemoteUpgrade = "testremote2"
  25. )
  26. func TestPrepare(t *testing.T) {
  27. b := newMockBackend()
  28. c := newTestController(b, false)
  29. ctx := context.Background()
  30. if err := c.Prepare(ctx); err != nil {
  31. t.Fatal(err)
  32. }
  33. if b.p == nil {
  34. t.Fatal("pull not performed")
  35. }
  36. c = newTestController(b, false)
  37. if err := c.Prepare(ctx); err != nil {
  38. t.Fatal(err)
  39. }
  40. if b.p == nil {
  41. t.Fatal("unexpected nil")
  42. }
  43. if b.p.PluginObj.PluginReference != pluginTestRemoteUpgrade {
  44. t.Fatal("upgrade not performed")
  45. }
  46. c = newTestController(b, false)
  47. c.serviceID = "1"
  48. if err := c.Prepare(ctx); err == nil {
  49. t.Fatal("expected error on prepare")
  50. }
  51. }
  52. func TestStart(t *testing.T) {
  53. b := newMockBackend()
  54. c := newTestController(b, false)
  55. ctx := context.Background()
  56. if err := c.Prepare(ctx); err != nil {
  57. t.Fatal(err)
  58. }
  59. if err := c.Start(ctx); err != nil {
  60. t.Fatal(err)
  61. }
  62. if !b.p.IsEnabled() {
  63. t.Fatal("expected plugin to be enabled")
  64. }
  65. c = newTestController(b, true)
  66. if err := c.Prepare(ctx); err != nil {
  67. t.Fatal(err)
  68. }
  69. if err := c.Start(ctx); err != nil {
  70. t.Fatal(err)
  71. }
  72. if b.p.IsEnabled() {
  73. t.Fatal("expected plugin to be disabled")
  74. }
  75. c = newTestController(b, false)
  76. if err := c.Prepare(ctx); err != nil {
  77. t.Fatal(err)
  78. }
  79. if err := c.Start(ctx); err != nil {
  80. t.Fatal(err)
  81. }
  82. if !b.p.IsEnabled() {
  83. t.Fatal("expected plugin to be enabled")
  84. }
  85. }
  86. func TestWaitCancel(t *testing.T) {
  87. b := newMockBackend()
  88. c := newTestController(b, true)
  89. ctx := context.Background()
  90. if err := c.Prepare(ctx); err != nil {
  91. t.Fatal(err)
  92. }
  93. if err := c.Start(ctx); err != nil {
  94. t.Fatal(err)
  95. }
  96. ctxCancel, cancel := context.WithCancel(ctx)
  97. chErr := make(chan error, 1)
  98. go func() {
  99. chErr <- c.Wait(ctxCancel)
  100. }()
  101. cancel()
  102. select {
  103. case err := <-chErr:
  104. if err != context.Canceled {
  105. t.Fatal(err)
  106. }
  107. case <-time.After(10 * time.Second):
  108. t.Fatal("timeout waiting for cancelation")
  109. }
  110. }
  111. func TestWaitDisabled(t *testing.T) {
  112. b := newMockBackend()
  113. c := newTestController(b, true)
  114. ctx := context.Background()
  115. if err := c.Prepare(ctx); err != nil {
  116. t.Fatal(err)
  117. }
  118. if err := c.Start(ctx); err != nil {
  119. t.Fatal(err)
  120. }
  121. chErr := make(chan error, 1)
  122. go func() {
  123. chErr <- c.Wait(ctx)
  124. }()
  125. if err := b.Enable("test", nil); err != nil {
  126. t.Fatal(err)
  127. }
  128. select {
  129. case err := <-chErr:
  130. if err == nil {
  131. t.Fatal("expected error")
  132. }
  133. case <-time.After(10 * time.Second):
  134. t.Fatal("timeout waiting for event")
  135. }
  136. if err := c.Start(ctx); err != nil {
  137. t.Fatal(err)
  138. }
  139. ctxWaitReady, cancelCtxWaitReady := context.WithTimeout(ctx, 30*time.Second)
  140. c.signalWaitReady = cancelCtxWaitReady
  141. defer cancelCtxWaitReady()
  142. go func() {
  143. chErr <- c.Wait(ctx)
  144. }()
  145. chEvent, cancel := b.SubscribeEvents(1)
  146. defer cancel()
  147. if err := b.Disable("test", nil); err != nil {
  148. t.Fatal(err)
  149. }
  150. select {
  151. case <-chEvent:
  152. <-ctxWaitReady.Done()
  153. if err := ctxWaitReady.Err(); err == context.DeadlineExceeded {
  154. t.Fatal(err)
  155. }
  156. select {
  157. case <-chErr:
  158. t.Fatal("wait returned unexpectedly")
  159. default:
  160. // all good
  161. }
  162. case <-chErr:
  163. t.Fatal("wait returned unexpectedly")
  164. case <-time.After(10 * time.Second):
  165. t.Fatal("timeout waiting for event")
  166. }
  167. if err := b.Remove("test", nil); err != nil {
  168. t.Fatal(err)
  169. }
  170. select {
  171. case err := <-chErr:
  172. if err == nil {
  173. t.Fatal("expected error")
  174. }
  175. if !strings.Contains(err.Error(), "removed") {
  176. t.Fatal(err)
  177. }
  178. case <-time.After(10 * time.Second):
  179. t.Fatal("timeout waiting for event")
  180. }
  181. }
  182. func TestWaitEnabled(t *testing.T) {
  183. b := newMockBackend()
  184. c := newTestController(b, false)
  185. ctx := context.Background()
  186. if err := c.Prepare(ctx); err != nil {
  187. t.Fatal(err)
  188. }
  189. if err := c.Start(ctx); err != nil {
  190. t.Fatal(err)
  191. }
  192. chErr := make(chan error, 1)
  193. go func() {
  194. chErr <- c.Wait(ctx)
  195. }()
  196. if err := b.Disable("test", nil); err != nil {
  197. t.Fatal(err)
  198. }
  199. select {
  200. case err := <-chErr:
  201. if err == nil {
  202. t.Fatal("expected error")
  203. }
  204. case <-time.After(10 * time.Second):
  205. t.Fatal("timeout waiting for event")
  206. }
  207. if err := c.Start(ctx); err != nil {
  208. t.Fatal(err)
  209. }
  210. ctxWaitReady, ctxWaitCancel := context.WithCancel(ctx)
  211. c.signalWaitReady = ctxWaitCancel
  212. defer ctxWaitCancel()
  213. go func() {
  214. chErr <- c.Wait(ctx)
  215. }()
  216. chEvent, cancel := b.SubscribeEvents(1)
  217. defer cancel()
  218. if err := b.Enable("test", nil); err != nil {
  219. t.Fatal(err)
  220. }
  221. select {
  222. case <-chEvent:
  223. <-ctxWaitReady.Done()
  224. if err := ctxWaitReady.Err(); err == context.DeadlineExceeded {
  225. t.Fatal(err)
  226. }
  227. select {
  228. case <-chErr:
  229. t.Fatal("wait returned unexpectedly")
  230. default:
  231. // all good
  232. }
  233. case <-chErr:
  234. t.Fatal("wait returned unexpectedly")
  235. case <-time.After(10 * time.Second):
  236. t.Fatal("timeout waiting for event")
  237. }
  238. if err := b.Remove("test", nil); err != nil {
  239. t.Fatal(err)
  240. }
  241. select {
  242. case err := <-chErr:
  243. if err == nil {
  244. t.Fatal("expected error")
  245. }
  246. if !strings.Contains(err.Error(), "removed") {
  247. t.Fatal(err)
  248. }
  249. case <-time.After(10 * time.Second):
  250. t.Fatal("timeout waiting for event")
  251. }
  252. }
  253. func TestRemove(t *testing.T) {
  254. b := newMockBackend()
  255. c := newTestController(b, false)
  256. ctx := context.Background()
  257. if err := c.Prepare(ctx); err != nil {
  258. t.Fatal(err)
  259. }
  260. if err := c.Shutdown(ctx); err != nil {
  261. t.Fatal(err)
  262. }
  263. c2 := newTestController(b, false)
  264. if err := c2.Prepare(ctx); err != nil {
  265. t.Fatal(err)
  266. }
  267. if err := c.Remove(ctx); err != nil {
  268. t.Fatal(err)
  269. }
  270. if b.p == nil {
  271. t.Fatal("plugin removed unexpectedly")
  272. }
  273. if err := c2.Shutdown(ctx); err != nil {
  274. t.Fatal(err)
  275. }
  276. if err := c2.Remove(ctx); err != nil {
  277. t.Fatal(err)
  278. }
  279. if b.p != nil {
  280. t.Fatal("expected plugin to be removed")
  281. }
  282. }
  283. func newTestController(b Backend, disabled bool) *Controller {
  284. return &Controller{
  285. logger: &log.Entry{Logger: &logrus.Logger{Out: io.Discard}},
  286. backend: b,
  287. spec: runtime.PluginSpec{
  288. Name: pluginTestName,
  289. Remote: pluginTestRemote,
  290. Disabled: disabled,
  291. },
  292. }
  293. }
  294. func newMockBackend() *mockBackend {
  295. return &mockBackend{
  296. pub: pubsub.NewPublisher(0, 0),
  297. }
  298. }
  299. type mockBackend struct {
  300. p *v2.Plugin
  301. pub *pubsub.Publisher
  302. }
  303. func (m *mockBackend) Disable(name string, config *backend.PluginDisableConfig) error {
  304. m.p.PluginObj.Enabled = false
  305. m.pub.Publish(plugin.EventDisable{})
  306. return nil
  307. }
  308. func (m *mockBackend) Enable(name string, config *backend.PluginEnableConfig) error {
  309. m.p.PluginObj.Enabled = true
  310. m.pub.Publish(plugin.EventEnable{})
  311. return nil
  312. }
  313. func (m *mockBackend) Remove(name string, config *backend.PluginRmConfig) error {
  314. m.p = nil
  315. m.pub.Publish(plugin.EventRemove{})
  316. return nil
  317. }
  318. func (m *mockBackend) Pull(ctx context.Context, ref reference.Named, name string, metaHeaders http.Header, authConfig *registry.AuthConfig, privileges types.PluginPrivileges, outStream io.Writer, opts ...plugin.CreateOpt) error {
  319. m.p = &v2.Plugin{
  320. PluginObj: types.Plugin{
  321. ID: "1234",
  322. Name: name,
  323. PluginReference: ref.String(),
  324. },
  325. }
  326. return nil
  327. }
  328. func (m *mockBackend) Upgrade(ctx context.Context, ref reference.Named, name string, metaHeaders http.Header, authConfig *registry.AuthConfig, privileges types.PluginPrivileges, outStream io.Writer) error {
  329. m.p.PluginObj.PluginReference = pluginTestRemoteUpgrade
  330. return nil
  331. }
  332. func (m *mockBackend) Get(name string) (*v2.Plugin, error) {
  333. if m.p == nil {
  334. return nil, errors.New("not found")
  335. }
  336. return m.p, nil
  337. }
  338. func (m *mockBackend) SubscribeEvents(buffer int, events ...plugin.Event) (eventCh <-chan interface{}, cancel func()) {
  339. ch := m.pub.SubscribeTopicWithBuffer(nil, buffer)
  340. cancel = func() { m.pub.Evict(ch) }
  341. return ch, cancel
  342. }