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