github.com/rish1988/moby@v25.0.2+incompatible/integration/service/plugin_test.go (about)

     1  package service
     2  
     3  import (
     4  	"io"
     5  	"path"
     6  	"strings"
     7  	"testing"
     8  
     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  
    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  }