plugin_test.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. package service
  2. import (
  3. "context"
  4. "io"
  5. "io/ioutil"
  6. "os"
  7. "path"
  8. "strings"
  9. "testing"
  10. "github.com/docker/docker/api/types"
  11. "github.com/docker/docker/api/types/filters"
  12. swarmtypes "github.com/docker/docker/api/types/swarm"
  13. "github.com/docker/docker/api/types/swarm/runtime"
  14. "github.com/docker/docker/integration/internal/swarm"
  15. "github.com/docker/docker/testutil/daemon"
  16. "github.com/docker/docker/testutil/fixtures/plugin"
  17. "github.com/docker/docker/testutil/registry"
  18. "gotest.tools/assert"
  19. "gotest.tools/poll"
  20. "gotest.tools/skip"
  21. )
  22. func TestServicePlugin(t *testing.T) {
  23. skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
  24. skip.If(t, testEnv.DaemonInfo.OSType == "windows")
  25. skip.If(t, os.Getenv("DOCKER_ENGINE_GOARCH") != "amd64")
  26. defer setupTest(t)()
  27. reg := registry.NewV2(t)
  28. defer reg.Close()
  29. name := "test-" + strings.ToLower(t.Name())
  30. repo := path.Join(registry.DefaultURL, "swarm", name+":v1")
  31. repo2 := path.Join(registry.DefaultURL, "swarm", name+":v2")
  32. d := daemon.New(t)
  33. d.StartWithBusybox(t)
  34. apiclient := d.NewClientT(t)
  35. err := plugin.Create(context.Background(), apiclient, repo)
  36. assert.NilError(t, err)
  37. r, err := apiclient.PluginPush(context.Background(), repo, "")
  38. assert.NilError(t, err)
  39. _, err = io.Copy(ioutil.Discard, r)
  40. assert.NilError(t, err)
  41. err = apiclient.PluginRemove(context.Background(), repo, types.PluginRemoveOptions{})
  42. assert.NilError(t, err)
  43. err = plugin.Create(context.Background(), apiclient, repo2)
  44. assert.NilError(t, err)
  45. r, err = apiclient.PluginPush(context.Background(), repo2, "")
  46. assert.NilError(t, err)
  47. _, err = io.Copy(ioutil.Discard, r)
  48. assert.NilError(t, err)
  49. err = apiclient.PluginRemove(context.Background(), repo2, types.PluginRemoveOptions{})
  50. assert.NilError(t, err)
  51. d.Stop(t)
  52. d1 := swarm.NewSwarm(t, testEnv, daemon.WithExperimental())
  53. defer d1.Stop(t)
  54. d2 := daemon.New(t, daemon.WithExperimental(), daemon.WithSwarmPort(daemon.DefaultSwarmPort+1))
  55. d2.StartAndSwarmJoin(t, d1, true)
  56. defer d2.Stop(t)
  57. d3 := daemon.New(t, daemon.WithExperimental(), daemon.WithSwarmPort(daemon.DefaultSwarmPort+2))
  58. d3.StartAndSwarmJoin(t, d1, false)
  59. defer d3.Stop(t)
  60. id := d1.CreateService(t, makePlugin(repo, name, nil))
  61. poll.WaitOn(t, d1.PluginIsRunning(t, name), swarm.ServicePoll)
  62. poll.WaitOn(t, d2.PluginIsRunning(t, name), swarm.ServicePoll)
  63. poll.WaitOn(t, d3.PluginIsRunning(t, name), swarm.ServicePoll)
  64. // test that environment variables are passed from plugin service to plugin instance
  65. service := d1.GetService(t, id)
  66. tasks := d1.GetServiceTasks(t, service.Spec.Annotations.Name, filters.Arg("runtime", "plugin"))
  67. if len(tasks) == 0 {
  68. t.Log("No tasks found for plugin service")
  69. t.Fail()
  70. }
  71. plugin, _, err := d1.NewClientT(t).PluginInspectWithRaw(context.Background(), name)
  72. assert.NilError(t, err, "Error inspecting service plugin")
  73. found := false
  74. for _, env := range plugin.Settings.Env {
  75. assert.Equal(t, strings.HasPrefix(env, "baz"), false, "Environment variable entry %q is invalid and should not be present", "baz")
  76. if strings.HasPrefix(env, "foo=") {
  77. found = true
  78. assert.Equal(t, env, "foo=bar")
  79. }
  80. }
  81. assert.Equal(t, true, found, "Environment variable %q not found in plugin", "foo")
  82. d1.UpdateService(t, service, makePlugin(repo2, name, nil))
  83. poll.WaitOn(t, d1.PluginReferenceIs(t, name, repo2), swarm.ServicePoll)
  84. poll.WaitOn(t, d2.PluginReferenceIs(t, name, repo2), swarm.ServicePoll)
  85. poll.WaitOn(t, d3.PluginReferenceIs(t, name, repo2), swarm.ServicePoll)
  86. poll.WaitOn(t, d1.PluginIsRunning(t, name), swarm.ServicePoll)
  87. poll.WaitOn(t, d2.PluginIsRunning(t, name), swarm.ServicePoll)
  88. poll.WaitOn(t, d3.PluginIsRunning(t, name), swarm.ServicePoll)
  89. d1.RemoveService(t, id)
  90. poll.WaitOn(t, d1.PluginIsNotPresent(t, name), swarm.ServicePoll)
  91. poll.WaitOn(t, d2.PluginIsNotPresent(t, name), swarm.ServicePoll)
  92. poll.WaitOn(t, d3.PluginIsNotPresent(t, name), swarm.ServicePoll)
  93. // constrain to managers only
  94. id = d1.CreateService(t, makePlugin(repo, name, []string{"node.role==manager"}))
  95. poll.WaitOn(t, d1.PluginIsRunning(t, name), swarm.ServicePoll)
  96. poll.WaitOn(t, d2.PluginIsRunning(t, name), swarm.ServicePoll)
  97. poll.WaitOn(t, d3.PluginIsNotPresent(t, name), swarm.ServicePoll)
  98. d1.RemoveService(t, id)
  99. poll.WaitOn(t, d1.PluginIsNotPresent(t, name), swarm.ServicePoll)
  100. poll.WaitOn(t, d2.PluginIsNotPresent(t, name), swarm.ServicePoll)
  101. poll.WaitOn(t, d3.PluginIsNotPresent(t, name), swarm.ServicePoll)
  102. // with no name
  103. id = d1.CreateService(t, makePlugin(repo, "", nil))
  104. poll.WaitOn(t, d1.PluginIsRunning(t, repo), swarm.ServicePoll)
  105. poll.WaitOn(t, d2.PluginIsRunning(t, repo), swarm.ServicePoll)
  106. poll.WaitOn(t, d3.PluginIsRunning(t, repo), swarm.ServicePoll)
  107. d1.RemoveService(t, id)
  108. poll.WaitOn(t, d1.PluginIsNotPresent(t, repo), swarm.ServicePoll)
  109. poll.WaitOn(t, d2.PluginIsNotPresent(t, repo), swarm.ServicePoll)
  110. poll.WaitOn(t, d3.PluginIsNotPresent(t, repo), swarm.ServicePoll)
  111. }
  112. func makePlugin(repo, name string, constraints []string) func(*swarmtypes.Service) {
  113. return func(s *swarmtypes.Service) {
  114. s.Spec.TaskTemplate.Runtime = swarmtypes.RuntimePlugin
  115. s.Spec.TaskTemplate.PluginSpec = &runtime.PluginSpec{
  116. Name: name,
  117. Remote: repo,
  118. Env: []string{
  119. "baz", // invalid environment variable entries are ignored
  120. "foo=bar", // "foo" will be the single environment variable
  121. },
  122. }
  123. if constraints != nil {
  124. s.Spec.TaskTemplate.Placement = &swarmtypes.Placement{
  125. Constraints: constraints,
  126. }
  127. }
  128. }
  129. }