github.com/docker/docker@v299999999.0.0-20200612211812-aaf470eca7b5+incompatible/integration/service/plugin_test.go (about)

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