Browse code

Migrate TestAPISwarmServicesPlugin to integration

Also starts to create more "poll/check" function to `internal/test/daemon`.

Signed-off-by: Vincent Demeester <vincent@sbr.pm>

Vincent Demeester authored on 2018/04/18 23:18:53
Showing 6 changed files
... ...
@@ -4,19 +4,15 @@ package main
4 4
 
5 5
 import (
6 6
 	"fmt"
7
-	"path"
8 7
 	"strconv"
9 8
 	"strings"
10 9
 	"time"
11 10
 
12 11
 	"github.com/docker/docker/api/types"
13 12
 	"github.com/docker/docker/api/types/swarm"
14
-	"github.com/docker/docker/api/types/swarm/runtime"
15 13
 	"github.com/docker/docker/integration-cli/checker"
16 14
 	"github.com/docker/docker/integration-cli/daemon"
17 15
 	testdaemon "github.com/docker/docker/internal/test/daemon"
18
-	"github.com/docker/docker/internal/test/fixtures/plugin"
19
-	"github.com/docker/docker/internal/test/registry"
20 16
 	"github.com/go-check/check"
21 17
 	"golang.org/x/net/context"
22 18
 	"golang.org/x/sys/unix"
... ...
@@ -611,78 +607,3 @@ func (s *DockerSwarmSuite) TestAPISwarmServicesStateReporting(c *check.C) {
611 611
 		}
612 612
 	}
613 613
 }
614
-
615
-// Test plugins deployed via swarm services
616
-func (s *DockerSwarmSuite) TestAPISwarmServicesPlugin(c *check.C) {
617
-	testRequires(c, ExperimentalDaemon, DaemonIsLinux, IsAmd64)
618
-
619
-	reg := registry.NewV2(c)
620
-	defer reg.Close()
621
-
622
-	repo := path.Join(privateRegistryURL, "swarm", "test:v1")
623
-	repo2 := path.Join(privateRegistryURL, "swarm", "test:v2")
624
-	name := "test"
625
-
626
-	err := plugin.CreateInRegistry(context.Background(), repo, nil)
627
-	c.Assert(err, checker.IsNil, check.Commentf("failed to create plugin"))
628
-	err = plugin.CreateInRegistry(context.Background(), repo2, nil)
629
-	c.Assert(err, checker.IsNil, check.Commentf("failed to create plugin"))
630
-
631
-	d1 := s.AddDaemon(c, true, true)
632
-	d2 := s.AddDaemon(c, true, true)
633
-	d3 := s.AddDaemon(c, true, false)
634
-
635
-	makePlugin := func(repo, name string, constraints []string) func(*swarm.Service) {
636
-		return func(s *swarm.Service) {
637
-			s.Spec.TaskTemplate.Runtime = "plugin"
638
-			s.Spec.TaskTemplate.PluginSpec = &runtime.PluginSpec{
639
-				Name:   name,
640
-				Remote: repo,
641
-			}
642
-			if constraints != nil {
643
-				s.Spec.TaskTemplate.Placement = &swarm.Placement{
644
-					Constraints: constraints,
645
-				}
646
-			}
647
-		}
648
-	}
649
-
650
-	id := d1.CreateService(c, makePlugin(repo, name, nil))
651
-	waitAndAssert(c, defaultReconciliationTimeout, d1.CheckPluginRunning(name), checker.True)
652
-	waitAndAssert(c, defaultReconciliationTimeout, d2.CheckPluginRunning(name), checker.True)
653
-	waitAndAssert(c, defaultReconciliationTimeout, d3.CheckPluginRunning(name), checker.True)
654
-
655
-	service := d1.GetService(c, id)
656
-	d1.UpdateService(c, service, makePlugin(repo2, name, nil))
657
-	waitAndAssert(c, defaultReconciliationTimeout, d1.CheckPluginImage(name), checker.Equals, repo2)
658
-	waitAndAssert(c, defaultReconciliationTimeout, d2.CheckPluginImage(name), checker.Equals, repo2)
659
-	waitAndAssert(c, defaultReconciliationTimeout, d3.CheckPluginImage(name), checker.Equals, repo2)
660
-	waitAndAssert(c, defaultReconciliationTimeout, d1.CheckPluginRunning(name), checker.True)
661
-	waitAndAssert(c, defaultReconciliationTimeout, d2.CheckPluginRunning(name), checker.True)
662
-	waitAndAssert(c, defaultReconciliationTimeout, d3.CheckPluginRunning(name), checker.True)
663
-
664
-	d1.RemoveService(c, id)
665
-	waitAndAssert(c, defaultReconciliationTimeout, d1.CheckPluginRunning(name), checker.False)
666
-	waitAndAssert(c, defaultReconciliationTimeout, d2.CheckPluginRunning(name), checker.False)
667
-	waitAndAssert(c, defaultReconciliationTimeout, d3.CheckPluginRunning(name), checker.False)
668
-
669
-	// constrain to managers only
670
-	id = d1.CreateService(c, makePlugin(repo, name, []string{"node.role==manager"}))
671
-	waitAndAssert(c, defaultReconciliationTimeout, d1.CheckPluginRunning(name), checker.True)
672
-	waitAndAssert(c, defaultReconciliationTimeout, d2.CheckPluginRunning(name), checker.True)
673
-	waitAndAssert(c, defaultReconciliationTimeout, d3.CheckPluginRunning(name), checker.False) // Not a manager, not running it
674
-	d1.RemoveService(c, id)
675
-	waitAndAssert(c, defaultReconciliationTimeout, d1.CheckPluginRunning(name), checker.False)
676
-	waitAndAssert(c, defaultReconciliationTimeout, d2.CheckPluginRunning(name), checker.False)
677
-	waitAndAssert(c, defaultReconciliationTimeout, d3.CheckPluginRunning(name), checker.False)
678
-
679
-	// with no name
680
-	id = d1.CreateService(c, makePlugin(repo, "", nil))
681
-	waitAndAssert(c, defaultReconciliationTimeout, d1.CheckPluginRunning(repo), checker.True)
682
-	waitAndAssert(c, defaultReconciliationTimeout, d2.CheckPluginRunning(repo), checker.True)
683
-	waitAndAssert(c, defaultReconciliationTimeout, d3.CheckPluginRunning(repo), checker.True)
684
-	d1.RemoveService(c, id)
685
-	waitAndAssert(c, defaultReconciliationTimeout, d1.CheckPluginRunning(repo), checker.False)
686
-	waitAndAssert(c, defaultReconciliationTimeout, d2.CheckPluginRunning(repo), checker.False)
687
-	waitAndAssert(c, defaultReconciliationTimeout, d3.CheckPluginRunning(repo), checker.False)
688
-}
... ...
@@ -19,10 +19,10 @@ import (
19 19
 // ServicePoll tweaks the pollSettings for `service`
20 20
 func ServicePoll(config *poll.Settings) {
21 21
 	// Override the default pollSettings for `service` resource here ...
22
-
22
+	config.Timeout = 30 * time.Second
23
+	config.Delay = 100 * time.Millisecond
23 24
 	if runtime.GOARCH == "arm64" || runtime.GOARCH == "arm" {
24 25
 		config.Timeout = 1 * time.Minute
25
-		config.Delay = 100 * time.Millisecond
26 26
 	}
27 27
 }
28 28
 
29 29
new file mode 100644
... ...
@@ -0,0 +1,120 @@
0
+package service
1
+
2
+import (
3
+	"context"
4
+	"io"
5
+	"io/ioutil"
6
+	"os"
7
+	"path"
8
+	"testing"
9
+
10
+	"github.com/docker/docker/api/types"
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/internal/test/daemon"
15
+	"github.com/docker/docker/internal/test/fixtures/plugin"
16
+	"github.com/docker/docker/internal/test/registry"
17
+	"github.com/gotestyourself/gotestyourself/assert"
18
+	"github.com/gotestyourself/gotestyourself/poll"
19
+	"github.com/gotestyourself/gotestyourself/skip"
20
+)
21
+
22
+func TestServicePlugin(t *testing.T) {
23
+	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
24
+	skip.If(t, os.Getenv("DOCKER_ENGINE_GOARCH") != "amd64")
25
+	defer setupTest(t)()
26
+
27
+	reg := registry.NewV2(t)
28
+	defer reg.Close()
29
+
30
+	repo := path.Join(registry.DefaultURL, "swarm", "test:v1")
31
+	repo2 := path.Join(registry.DefaultURL, "swarm", "test:v2")
32
+	name := "test"
33
+
34
+	d := daemon.New(t)
35
+	d.StartWithBusybox(t)
36
+	apiclient := d.NewClientT(t)
37
+	err := plugin.Create(context.Background(), apiclient, repo)
38
+	assert.NilError(t, err)
39
+	r, err := apiclient.PluginPush(context.Background(), repo, "")
40
+	assert.NilError(t, err)
41
+	_, err = io.Copy(ioutil.Discard, r)
42
+	assert.NilError(t, err)
43
+	err = apiclient.PluginRemove(context.Background(), repo, types.PluginRemoveOptions{})
44
+	assert.NilError(t, err)
45
+	err = plugin.Create(context.Background(), apiclient, repo2)
46
+	assert.NilError(t, err)
47
+	r, err = apiclient.PluginPush(context.Background(), repo2, "")
48
+	assert.NilError(t, err)
49
+	_, err = io.Copy(ioutil.Discard, r)
50
+	assert.NilError(t, err)
51
+	err = apiclient.PluginRemove(context.Background(), repo2, types.PluginRemoveOptions{})
52
+	assert.NilError(t, err)
53
+	d.Stop(t)
54
+
55
+	d1 := swarm.NewSwarm(t, testEnv, daemon.WithExperimental)
56
+	defer d1.Stop(t)
57
+	d2 := daemon.New(t, daemon.WithExperimental, daemon.WithSwarmPort(daemon.DefaultSwarmPort+1))
58
+	d2.StartAndSwarmJoin(t, d1, true)
59
+	defer d2.Stop(t)
60
+	d3 := daemon.New(t, daemon.WithExperimental, daemon.WithSwarmPort(daemon.DefaultSwarmPort+2))
61
+	d3.StartAndSwarmJoin(t, d1, false)
62
+	defer d3.Stop(t)
63
+
64
+	id := d1.CreateService(t, makePlugin(repo, name, nil))
65
+	poll.WaitOn(t, d1.PluginIsRunning(name), swarm.ServicePoll)
66
+	poll.WaitOn(t, d2.PluginIsRunning(name), swarm.ServicePoll)
67
+	poll.WaitOn(t, d3.PluginIsRunning(name), swarm.ServicePoll)
68
+
69
+	service := d1.GetService(t, id)
70
+	d1.UpdateService(t, service, makePlugin(repo2, name, nil))
71
+	poll.WaitOn(t, d1.PluginReferenceIs(name, repo2), swarm.ServicePoll)
72
+	poll.WaitOn(t, d2.PluginReferenceIs(name, repo2), swarm.ServicePoll)
73
+	poll.WaitOn(t, d3.PluginReferenceIs(name, repo2), swarm.ServicePoll)
74
+	poll.WaitOn(t, d1.PluginIsRunning(name), swarm.ServicePoll)
75
+	poll.WaitOn(t, d2.PluginIsRunning(name), swarm.ServicePoll)
76
+	poll.WaitOn(t, d3.PluginIsRunning(name), swarm.ServicePoll)
77
+
78
+	d1.RemoveService(t, id)
79
+	poll.WaitOn(t, d1.PluginIsNotPresent(name), swarm.ServicePoll)
80
+	poll.WaitOn(t, d2.PluginIsNotPresent(name), swarm.ServicePoll)
81
+	poll.WaitOn(t, d3.PluginIsNotPresent(name), swarm.ServicePoll)
82
+
83
+	// constrain to managers only
84
+	id = d1.CreateService(t, makePlugin(repo, name, []string{"node.role==manager"}))
85
+	poll.WaitOn(t, d1.PluginIsRunning(name), swarm.ServicePoll)
86
+	poll.WaitOn(t, d2.PluginIsRunning(name), swarm.ServicePoll)
87
+	poll.WaitOn(t, d3.PluginIsNotPresent(name), swarm.ServicePoll)
88
+
89
+	d1.RemoveService(t, id)
90
+	poll.WaitOn(t, d1.PluginIsNotPresent(name), swarm.ServicePoll)
91
+	poll.WaitOn(t, d2.PluginIsNotPresent(name), swarm.ServicePoll)
92
+	poll.WaitOn(t, d3.PluginIsNotPresent(name), swarm.ServicePoll)
93
+
94
+	// with no name
95
+	id = d1.CreateService(t, makePlugin(repo, "", nil))
96
+	poll.WaitOn(t, d1.PluginIsRunning(repo), swarm.ServicePoll)
97
+	poll.WaitOn(t, d2.PluginIsRunning(repo), swarm.ServicePoll)
98
+	poll.WaitOn(t, d3.PluginIsRunning(repo), swarm.ServicePoll)
99
+
100
+	d1.RemoveService(t, id)
101
+	poll.WaitOn(t, d1.PluginIsNotPresent(repo), swarm.ServicePoll)
102
+	poll.WaitOn(t, d2.PluginIsNotPresent(repo), swarm.ServicePoll)
103
+	poll.WaitOn(t, d3.PluginIsNotPresent(repo), swarm.ServicePoll)
104
+}
105
+
106
+func makePlugin(repo, name string, constraints []string) func(*swarmtypes.Service) {
107
+	return func(s *swarmtypes.Service) {
108
+		s.Spec.TaskTemplate.Runtime = "plugin"
109
+		s.Spec.TaskTemplate.PluginSpec = &runtime.PluginSpec{
110
+			Name:   name,
111
+			Remote: repo,
112
+		}
113
+		if constraints != nil {
114
+			s.Spec.TaskTemplate.Placement = &swarmtypes.Placement{
115
+				Constraints: constraints,
116
+			}
117
+		}
118
+	}
119
+}
... ...
@@ -113,7 +113,7 @@ func New(t testingT, ops ...func(*Daemon)) *Daemon {
113 113
 		execRoot:        filepath.Join(os.TempDir(), "docker-execroot", id),
114 114
 		dockerdBinary:   defaultDockerdBinary,
115 115
 		swarmListenAddr: defaultSwarmListenAddr,
116
-		SwarmPort:       defaultSwarmPort,
116
+		SwarmPort:       DefaultSwarmPort,
117 117
 		log:             t,
118 118
 	}
119 119
 
120 120
new file mode 100644
... ...
@@ -0,0 +1,77 @@
0
+package daemon
1
+
2
+import (
3
+	"context"
4
+
5
+	"github.com/docker/docker/api/types"
6
+	"github.com/docker/docker/client"
7
+	"github.com/gotestyourself/gotestyourself/poll"
8
+)
9
+
10
+// PluginIsRunning provides a poller to check if the specified plugin is running
11
+func (d *Daemon) PluginIsRunning(name string) func(poll.LogT) poll.Result {
12
+	return withClient(d, withPluginInspect(name, func(plugin *types.Plugin, t poll.LogT) poll.Result {
13
+		if plugin.Enabled {
14
+			return poll.Success()
15
+		}
16
+		return poll.Continue("plugin %q is not enabled", name)
17
+	}))
18
+}
19
+
20
+// PluginIsNotRunning provides a poller to check if the specified plugin is not running
21
+func (d *Daemon) PluginIsNotRunning(name string) func(poll.LogT) poll.Result {
22
+	return withClient(d, withPluginInspect(name, func(plugin *types.Plugin, t poll.LogT) poll.Result {
23
+		if !plugin.Enabled {
24
+			return poll.Success()
25
+		}
26
+		return poll.Continue("plugin %q is enabled", name)
27
+	}))
28
+}
29
+
30
+// PluginIsNotPresent provides a poller to check if the specified plugin is not present
31
+func (d *Daemon) PluginIsNotPresent(name string) func(poll.LogT) poll.Result {
32
+	return withClient(d, func(c client.APIClient, t poll.LogT) poll.Result {
33
+		_, _, err := c.PluginInspectWithRaw(context.Background(), name)
34
+		if client.IsErrNotFound(err) {
35
+			return poll.Success()
36
+		}
37
+		if err != nil {
38
+			return poll.Error(err)
39
+		}
40
+		return poll.Continue("plugin %q exists")
41
+	})
42
+}
43
+
44
+// PluginReferenceIs provides a poller to check if the specified plugin has the specified reference
45
+func (d *Daemon) PluginReferenceIs(name, expectedRef string) func(poll.LogT) poll.Result {
46
+	return withClient(d, withPluginInspect(name, func(plugin *types.Plugin, t poll.LogT) poll.Result {
47
+		if plugin.PluginReference == expectedRef {
48
+			return poll.Success()
49
+		}
50
+		return poll.Continue("plugin %q reference is not %q", name, expectedRef)
51
+	}))
52
+}
53
+
54
+func withPluginInspect(name string, f func(*types.Plugin, poll.LogT) poll.Result) func(client.APIClient, poll.LogT) poll.Result {
55
+	return func(c client.APIClient, t poll.LogT) poll.Result {
56
+		plugin, _, err := c.PluginInspectWithRaw(context.Background(), name)
57
+		if client.IsErrNotFound(err) {
58
+			return poll.Continue("plugin %q not found", name)
59
+		}
60
+		if err != nil {
61
+			return poll.Error(err)
62
+		}
63
+		return f(plugin, t)
64
+	}
65
+
66
+}
67
+
68
+func withClient(d *Daemon, f func(client.APIClient, poll.LogT) poll.Result) func(poll.LogT) poll.Result {
69
+	return func(t poll.LogT) poll.Result {
70
+		c, err := d.NewClient()
71
+		if err != nil {
72
+			poll.Error(err)
73
+		}
74
+		return f(c, t)
75
+	}
76
+}
... ...
@@ -10,7 +10,8 @@ import (
10 10
 )
11 11
 
12 12
 const (
13
-	defaultSwarmPort       = 2477
13
+	// DefaultSwarmPort is the default port use for swarm in the tests
14
+	DefaultSwarmPort       = 2477
14 15
 	defaultSwarmListenAddr = "0.0.0.0"
15 16
 )
16 17