github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/engine/integration/service/plugin_test.go (about)

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