plugin_test.go 5.4 KB

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