Skip to content

Commit b5cb5ee

Browse files
committed
Add first e2e test
Signed-off-by: Daniel Nephin <[email protected]>
1 parent 26418a1 commit b5cb5ee

File tree

5 files changed

+135
-0
lines changed

5 files changed

+135
-0
lines changed

cli/command/stack/remove.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package stack
22

33
import (
44
"fmt"
5+
"sort"
56
"strings"
67

78
"github.com/docker/cli/cli"
@@ -88,12 +89,19 @@ func runRemove(dockerCli command.Cli, opts removeOptions) error {
8889
return nil
8990
}
9091

92+
func sortServiceByName(services []swarm.Service) func(i, j int) bool {
93+
return func(i, j int) bool {
94+
return services[i].Spec.Name < services[j].Spec.Name
95+
}
96+
}
97+
9198
func removeServices(
9299
ctx context.Context,
93100
dockerCli command.Cli,
94101
services []swarm.Service,
95102
) bool {
96103
var hasError bool
104+
sort.Slice(services, sortServiceByName(services))
97105
for _, service := range services {
98106
fmt.Fprintf(dockerCli.Err(), "Removing service %s\n", service.Spec.Name)
99107
if err := dockerCli.Client().ServiceRemove(ctx, service.ID); err != nil {

e2e/stack/main_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package stack
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"testing"
7+
8+
"github.com/pkg/errors"
9+
)
10+
11+
func TestMain(m *testing.M) {
12+
if err := setupTestEnv(); err != nil {
13+
fmt.Println(err.Error())
14+
os.Exit(3)
15+
}
16+
os.Exit(m.Run())
17+
}
18+
19+
// TODO: move to shared internal package
20+
func setupTestEnv() error {
21+
dockerHost := os.Getenv("TEST_DOCKER_HOST")
22+
if dockerHost == "" {
23+
return errors.New("$TEST_DOCKER_HOST must be set")
24+
}
25+
return os.Setenv("DOCKER_HOST", dockerHost)
26+
}

e2e/stack/remove_test.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package stack
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
"testing"
7+
"time"
8+
9+
shlex "github.com/flynn-archive/go-shlex"
10+
"github.com/gotestyourself/gotestyourself/golden"
11+
"github.com/gotestyourself/gotestyourself/icmd"
12+
"github.com/stretchr/testify/require"
13+
)
14+
15+
func TestRemove(t *testing.T) {
16+
stackname := "test-stack-remove"
17+
deployFullStack(t, stackname)
18+
defer cleanupFullStack(t, stackname)
19+
20+
result := icmd.RunCmd(shell(t, "docker stack rm %s", stackname))
21+
22+
result.Assert(t, icmd.Expected{Out: icmd.None})
23+
golden.Assert(t, result.Stderr(), "stack-remove-success.golden")
24+
}
25+
26+
func deployFullStack(t *testing.T, stackname string) {
27+
// TODO: this stack should have full options not minimal options
28+
result := icmd.RunCmd(shell(t,
29+
"docker stack deploy --compose-file=./testdata/full-stack.yml %s", stackname))
30+
result.Assert(t, icmd.Success)
31+
32+
waitOn(t, taskCount(stackname, 2), 0)
33+
}
34+
35+
func cleanupFullStack(t *testing.T, stackname string) {
36+
result := icmd.RunCmd(shell(t, "docker stack rm %s", stackname))
37+
result.Assert(t, icmd.Success)
38+
waitOn(t, taskCount(stackname, 0), 0)
39+
}
40+
41+
func taskCount(stackname string, expected int) func() (bool, error) {
42+
return func() (bool, error) {
43+
result := icmd.RunCommand(
44+
"docker", "stack", "ps", "-f=desired-state=running", stackname)
45+
count := lines(result.Stdout()) - 1
46+
return count == expected, nil
47+
}
48+
}
49+
50+
func lines(out string) int {
51+
return len(strings.Split(strings.TrimSpace(out), "\n"))
52+
}
53+
54+
// TODO: move to gotestyourself
55+
func shell(t *testing.T, format string, args ...interface{}) icmd.Cmd {
56+
cmd, err := shlex.Split(fmt.Sprintf(format, args...))
57+
require.NoError(t, err)
58+
return icmd.Cmd{Command: cmd}
59+
}
60+
61+
// TODO: move to gotestyourself
62+
func waitOn(t *testing.T, check func() (bool, error), timeout time.Duration) {
63+
if timeout == time.Duration(0) {
64+
timeout = defaultTimeout()
65+
}
66+
67+
after := time.After(timeout)
68+
for {
69+
select {
70+
case <-after:
71+
// TODO: include check function name in error message
72+
t.Fatalf("timeout hit after %s", timeout)
73+
default:
74+
// TODO: maybe return a failure message as well?
75+
done, err := check()
76+
if done {
77+
return
78+
}
79+
if err != nil {
80+
t.Fatal(err.Error())
81+
}
82+
}
83+
}
84+
}
85+
86+
func defaultTimeout() time.Duration {
87+
// TODO: support override from environment variable
88+
return 10 * time.Second
89+
}

e2e/stack/testdata/full-stack.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
version: '3.3'
2+
3+
services:
4+
one:
5+
image: registry:5000/alpine:3.6
6+
command: top
7+
two:
8+
image: registry:5000/alpine:3.6
9+
command: top
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Removing service test-stack-remove_one
2+
Removing service test-stack-remove_two
3+
Removing network test-stack-remove_default

0 commit comments

Comments
 (0)