Skip to content

Commit bc35fc5

Browse files
authored
Merge pull request #114 from SimonBaeumer/add-docker-executor
Add docker executor
2 parents bd82909 + 7dca238 commit bc35fc5

File tree

642 files changed

+277263
-10
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

642 files changed

+277263
-10
lines changed

.travis.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ jobs:
5858
- make integration-windows
5959

6060
- name: Unit tests
61+
script:
62+
- make test
63+
64+
- name: Unit test all
6165
before_script:
6266
- curl https://s3.amazonaws.com/codeclimate/test-reporter/test-reporter-0.6.3-linux-amd64 --output test-reporter
6367
- chmod +x test-reporter
@@ -68,7 +72,9 @@ jobs:
6872
- ./test-reporter after-build -t gocov --exit-code $TRAVIS_TEST_RESULT
6973

7074
- name: Integration test
71-
script: make integration-linux
75+
script:
76+
- docker pull docker.io/library/alpine:3.11
77+
- make integration-linux
7278

7379
- stage: deploy
7480
name: "Deployment"

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
- Added `nodes` which allow remote execution of tests
44
- Added `SSHExecutor` and `LocalExecutor`
55
- Removed `concurrent` argument from `test` command
6+
- Added `DockerExecutor`
67

78
# v1.3.0
89

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ test-coverage:
3232
go test -coverprofile c.out ./...
3333

3434

35+
test-coverage-all: export COMMANDER_TEST_ALL = 1
3536
test-coverage-all: export COMMANDER_SSH_TEST = 1
3637
test-coverage-all: export COMMANDER_TEST_SSH_HOST = localhost:2222
3738
test-coverage-all: export COMMANDER_TEST_SSH_USER = root

README.md

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ For more information take a look at the [quick start](#quick-start), the [exampl
5252
+ [Nodes](#nodes)
5353
- [local](#local)
5454
- [ssh](#ssh)
55+
- [docker](#docker)
5556
+ [Development](#development)
5657
* [Misc](#misc)
5758

@@ -643,6 +644,7 @@ Available node types are currently:
643644

644645
- `local`, execute tests locally
645646
- `ssh`, execute tests viá ssh
647+
- `docker`, execute tests inside a docker container
646648

647649
```yaml
648650
nodes: # define nodes in the node section
@@ -690,9 +692,11 @@ tests:
690692

691693
#### ssh
692694

693-
The `ssh` will execute tests against a configured node using ssh.
695+
The `ssh` node type will execute tests against a configured node using ssh.
694696

695-
**Limitations:** The `inhereit-env` config is disabled for ssh hosts, nevertheless it is possible to set env variables
697+
**Limitations:**
698+
- The `inhereit-env` config is disabled for ssh hosts, nevertheless it is possible to set env variables
699+
- Private registries are not supported at the moment
696700

697701
```yaml
698702
nodes: # define nodes in the node section
@@ -711,6 +715,27 @@ tests:
711715
exit-code: 0
712716
```
713717
718+
#### docker
719+
720+
The `docker` node type executes the given command inside a docker container.
721+
722+
**Notes:** If the default docker registry should be used prefix the container with the registry `docker.io/library/`
723+
724+
```yaml:
725+
nodes:
726+
docker-host:
727+
type: docker
728+
image: docker.io/library/alpine:3.11
729+
user: 1000 # define the owner of the executed command
730+
config:
731+
nodes:
732+
- docker-host
733+
734+
tests:
735+
"id -u":
736+
stdout: "1001"
737+
```
738+
714739
### Development
715740
716741
```
@@ -744,10 +769,12 @@ $ make deps
744769

745770
### Unit tests
746771

772+
`COMMANDER_TEST_ALL` will enable all tests which are depending on external systems like docker or databases.
747773
Enables ssh tests in unit test suite and sets the credentials for the target host.
748774
`COMMANDER_SSH_TEST` must be set to `1` to enable ssh tests.
749775

750776
```
777+
export COMMANDER_TEST_ALL = 1
751778
export COMMANDER_TEST_SSH=1
752779
export COMMANDER_TEST_SSH_HOST=localhost:2222
753780
export COMMANDER_TEST_SSH_PASS=pass

commander_linux.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,9 @@ tests:
99
- ✓ [ssh-host] it should test multiple hosts
1010
- ✓ [ssh-host-default] it should test multiple hosts
1111
- ✓ [local] it should test multiple hosts
12+
exit-code: 0
13+
14+
test docker:
15+
command: ./commander test integration/linux/docker.yaml
16+
stdout: ✓ [docker-host] cat /etc/os-release
1217
exit-code: 0

go.mod

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
11
module github.com/SimonBaeumer/commander
22

33
require (
4+
github.com/Microsoft/go-winio v0.4.14 // indirect
45
github.com/SimonBaeumer/cmd v1.1.0
56
github.com/antchfx/xmlquery v1.1.0
67
github.com/antchfx/xpath v1.1.0 // indirect
8+
github.com/docker/distribution v2.7.1+incompatible // indirect
9+
github.com/docker/docker v1.13.1
10+
github.com/docker/go-connections v0.4.0 // indirect
11+
github.com/docker/go-units v0.4.0 // indirect
712
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e
13+
github.com/magiconair/properties v1.8.1 // indirect
14+
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
15+
github.com/pkg/errors v0.9.1 // indirect
816
github.com/pmezard/go-difflib v1.0.0
917
github.com/stretchr/objx v0.2.0 // indirect
1018
github.com/stretchr/testify v1.4.0
1119
github.com/tidwall/gjson v1.3.2
1220
github.com/urfave/cli v1.20.0
21+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
1322
golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582 // indirect
1423
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
1524
gopkg.in/yaml.v2 v2.2.4

go.sum

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
2+
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
13
github.com/SimonBaeumer/cmd v1.1.0 h1:tr5dUMlly/8bLiC5B0J1AcE4ISru8POEfzAirWnUJnY=
24
github.com/SimonBaeumer/cmd v1.1.0/go.mod h1:4mc/LDXDWNbkeooqHP83yx3JXtInPHjJkF8zhzqqmZE=
35
github.com/antchfx/jsonquery v1.0.0 h1:1Yhk496SrCoY6fJkFZqpXEqbwOw5sFtLns9la4NoK3I=
@@ -10,12 +12,31 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8
1012
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1113
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
1214
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
15+
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
16+
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
17+
github.com/docker/docker v1.13.1 h1:IkZjBSIc8hBjLpqeAbeE5mca5mNgeatLHBy3GO78BWo=
18+
github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
19+
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
20+
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
21+
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
22+
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
23+
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
1324
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e h1:9MlwzLdW7QSDrhDjFlsEYmxpFyIoXmYRon3dt0io31k=
1425
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
26+
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
27+
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
28+
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
29+
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
30+
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
31+
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
32+
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
1533
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
1634
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
35+
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
1736
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
37+
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
1838
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
39+
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
1940
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
2041
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
2142
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
@@ -28,10 +49,15 @@ github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
2849
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
2950
github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
3051
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
52+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
3153
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
3254
golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582 h1:p9xBe/w/OzkeYVKm234g55gMdD1nSIooTir5kV11kfA=
3355
golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
56+
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
57+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
3458
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
59+
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b h1:ag/x1USPSsqHud38I9BAC88qdNLDHHtQ4mlgQIZPPNA=
60+
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
3561
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
3662
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
3763
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

hack/docker-private-registry.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
nodes:
2+
docker-host:
3+
type: docker
4+
image: docker.io/library/simonbaeumer/test-private:latest
5+
6+
config:
7+
nodes:
8+
- docker-host
9+
10+
tests:
11+
cat /etc/os-release:
12+
stdout:
13+
exactly: |-
14+
NAME="Alpine Linux"
15+
ID=alpine
16+
VERSION_ID=3.11.3
17+
PRETTY_NAME="Alpine Linux v3.11"
18+
HOME_URL="https://alpinelinux.org/"
19+
BUG_REPORT_URL="https://bugs.alpinelinux.org/"

integration/linux/docker.yaml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
nodes:
2+
docker-host:
3+
type: docker
4+
image: docker.io/library/alpine:3.11
5+
user: 1001
6+
7+
config:
8+
nodes:
9+
- docker-host
10+
11+
tests:
12+
cat /etc/os-release:
13+
stdout:
14+
exactly: |-
15+
NAME="Alpine Linux"
16+
ID=alpine
17+
VERSION_ID=3.11.3
18+
PRETTY_NAME="Alpine Linux v3.11"
19+
HOME_URL="https://alpinelinux.org/"
20+
BUG_REPORT_URL="https://bugs.alpinelinux.org/"
21+
22+
"id -u":
23+
stdout: "1001"

pkg/runtime/docker_executor.go

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package runtime
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"fmt"
7+
"github.com/docker/docker/api/types"
8+
"github.com/docker/docker/api/types/container"
9+
"github.com/docker/docker/client"
10+
"github.com/docker/docker/pkg/stdcopy"
11+
"log"
12+
"strings"
13+
"time"
14+
)
15+
16+
// DockerExecutor executes the test inside a docker container
17+
type DockerExecutor struct {
18+
Image string // Image which is started to execute the test
19+
Privileged bool // Enable privileged mode for the container
20+
User string // User defines which user executes the test
21+
}
22+
23+
// Execute executes the script inside a docker container
24+
func (e DockerExecutor) Execute(test TestCase) TestResult {
25+
ctx := context.Background()
26+
cli, err := client.NewEnvClient()
27+
if err != nil {
28+
test.Result.Error = err
29+
return TestResult{
30+
TestCase: test,
31+
}
32+
}
33+
34+
log.Printf("Pulling image %s\n", e.Image)
35+
reader, err := cli.ImagePull(ctx, e.Image, types.ImagePullOptions{})
36+
if err != nil {
37+
test.Result.Error = fmt.Errorf("could not pull image '%s' with error: '%s'", e.Image, err)
38+
return TestResult{
39+
TestCase: test,
40+
}
41+
}
42+
buf := bytes.Buffer{}
43+
buf.ReadFrom(reader)
44+
log.Printf("Pull log image'%s':\n %s\n", e.Image, buf.String())
45+
46+
var env []string
47+
for k, v := range test.Command.Env {
48+
env = append(env, fmt.Sprintf("%s=%s", k, v))
49+
}
50+
51+
log.Printf("Create container %s\n", e.Image)
52+
resp, err := cli.ContainerCreate(ctx, &container.Config{
53+
Image: e.Image,
54+
WorkingDir: test.Command.Dir,
55+
Env: env,
56+
User: e.User,
57+
Cmd: []string{"/bin/sh", "-c", test.Command.Cmd},
58+
Tty: false,
59+
}, nil, nil, "")
60+
if err != nil {
61+
test.Result.Error = fmt.Errorf("could not pull image '%s' with error: '%s'", e.Image, err)
62+
return TestResult{
63+
TestCase: test,
64+
}
65+
}
66+
67+
log.Printf("Started container %s %s\n", e.Image, resp.ID)
68+
if err := cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}); err != nil {
69+
test.Result.Error = fmt.Errorf("could not pull image '%s' with error: '%s'", e.Image, err)
70+
return TestResult{
71+
TestCase: test,
72+
}
73+
}
74+
duration := time.Duration(1 * time.Second)
75+
defer cli.ContainerStop(ctx, resp.ID, &duration)
76+
77+
status, err := cli.ContainerWait(ctx, resp.ID)
78+
if err != nil {
79+
panic(err)
80+
}
81+
82+
out, err := cli.ContainerLogs(ctx, resp.ID, types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true})
83+
if err != nil {
84+
panic(err)
85+
}
86+
87+
stdout := &bytes.Buffer{}
88+
stderr := &bytes.Buffer{}
89+
90+
_, err = stdcopy.StdCopy(stdout, stderr, out)
91+
if err != nil {
92+
panic(err)
93+
}
94+
95+
log.Println("title: '"+test.Title+"'", " Command: ", test.Command.Cmd)
96+
log.Println("title: '"+test.Title+"'", " Directory: ", test.Command.Dir)
97+
log.Println("title: '"+test.Title+"'", " Env: ", test.Command.Env)
98+
99+
// Write test result
100+
test.Result = CommandResult{
101+
ExitCode: int(status),
102+
Stdout: strings.TrimSpace(strings.Replace(stdout.String(), "\r\n", "\n", -1)),
103+
Stderr: strings.TrimSpace(strings.Replace(stderr.String(), "\r\n", "\n", -1)),
104+
}
105+
106+
log.Println("title: '"+test.Title+"'", " ExitCode: ", test.Result.ExitCode)
107+
log.Println("title: '"+test.Title+"'", " Stdout: ", test.Result.Stdout)
108+
log.Println("title: '"+test.Title+"'", " Stderr: ", test.Result.Stderr)
109+
110+
return Validate(test)
111+
}

0 commit comments

Comments
 (0)