Skip to content
This repository was archived by the owner on Jul 18, 2025. It is now read-only.

Commit f1e3cee

Browse files
author
Anca Iordache
committed
APP-257 CNAB integration tests
Signed-off-by: Anca Iordache <[email protected]>
1 parent 9a49da4 commit f1e3cee

File tree

7 files changed

+507
-29
lines changed

7 files changed

+507
-29
lines changed

docker.Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ test-unit: build_dev_image ## run unit tests
7272
docker run --rm -v $(CURDIR)/_build/test-results:/test-results $(DEV_IMAGE_NAME) make TEST_RESULTS_PREFIX=$(TEST_RESULTS_PREFIX) test-unit
7373

7474
test-e2e: build_dev_image invocation-image ## run end-to-end tests
75-
docker run -v /var/run:/var/run:ro --rm --network="host" $(DEV_IMAGE_NAME) make TEST_RESULTS_PREFIX=$(TEST_RESULTS_PREFIX) bin/$(BIN_NAME) test-e2e
75+
docker run -v /var/run:/var/run:ro --rm --network="host" $(DEV_IMAGE_NAME) make TEST_RESULTS_PREFIX=$(TEST_RESULTS_PREFIX) bin/$(BIN_NAME) E2E_TESTS=$(E2E_TESTS) test-e2e
7676

7777
COV_LABEL := com.docker.app.cov-run=$(TAG)
7878
coverage-run: build_dev_image ## run tests with coverage

e2e/compatibility_test.go

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
package e2e
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"io"
7+
"io/ioutil"
8+
"net/http"
9+
"os"
10+
"path/filepath"
11+
"regexp"
12+
"strings"
13+
"testing"
14+
"time"
15+
16+
"gotest.tools/assert"
17+
"k8s.io/apimachinery/pkg/util/wait"
18+
19+
"gotest.tools/fs"
20+
)
21+
22+
const (
23+
pollTimeout = 30 * time.Second
24+
)
25+
26+
func loadAndTagImage(info dindSwarmAndRegistryInfo, tmpDir *fs.Dir, tag string, url string) error {
27+
28+
err := downloadImageTarball(tmpDir.Join("image.tar"), url)
29+
if err != nil {
30+
return err
31+
}
32+
33+
combined := info.dockerCmd("load", "-q", "-i", tmpDir.Join("image.tar"))
34+
35+
digest := ""
36+
for _, line := range strings.Split(combined, "\n") {
37+
if strings.Contains(line, "sha256:") {
38+
digest = strings.Split(line, "sha256:")[1]
39+
}
40+
}
41+
if digest == "" {
42+
return errors.New("Image digest not found in docker load's stdout")
43+
}
44+
45+
digest = strings.Trim(digest, " \r\n")
46+
info.dockerCmd("tag", digest, tag)
47+
48+
return nil
49+
}
50+
51+
func downloadImageTarball(filepath string, url string) error {
52+
client := http.Client{Timeout: time.Minute * 1}
53+
res, err := client.Get(url)
54+
if err != nil {
55+
return err
56+
}
57+
defer res.Body.Close()
58+
59+
// Create the file
60+
out, err := os.Create(filepath)
61+
if err != nil {
62+
return err
63+
}
64+
defer out.Close()
65+
// Write the body to file
66+
_, err = io.Copy(out, res.Body)
67+
return err
68+
}
69+
70+
func TestBackwardsCompatibilityV1(t *testing.T) {
71+
runWithDindSwarmAndRegistry(t, func(info dindSwarmAndRegistryInfo) {
72+
appName := "app-e2e"
73+
74+
data, err := ioutil.ReadFile(filepath.Join("testdata", "compatibility", "bundle-v0.9.0.json"))
75+
assert.NilError(t, err)
76+
// update bundle
77+
bundleDir := filepath.Join(info.configDir, "app", "bundles", "docker.io", "library", "app-e2e", "_tags", "v0.9.0")
78+
assert.NilError(t, os.MkdirAll(bundleDir, os.FileMode(0777)))
79+
assert.NilError(t, ioutil.WriteFile(filepath.Join(bundleDir, "bundle.json"), data, os.FileMode(0644)))
80+
81+
// load images build with an old Docker App version
82+
assert.NilError(t, loadAndTagImage(info, info.tmpDir, "app-e2e:0.1.0-invoc", "https://github.com/docker/app-e2e/raw/master/images/v0.9.0/app-e2e-invoc.tar"))
83+
assert.NilError(t, loadAndTagImage(info, info.tmpDir, "app-e2e/backend", "https://github.com/docker/app-e2e/raw/master/images/v0.9.0/backend.tar"))
84+
assert.NilError(t, loadAndTagImage(info, info.tmpDir, "app-e2e/frontend", "https://github.com/docker/app-e2e/raw/master/images/v0.9.0/frontend.tar"))
85+
86+
// list images
87+
output := info.dockerCmd("app", "image", "ls")
88+
checkContains(t, output, []string{appName})
89+
// inspect bundle
90+
output = info.dockerCmd("app", "image", "inspect", "app-e2e:v0.9.0", "--pretty")
91+
checkContains(t, output,
92+
[]string{
93+
`name:\s+app-e2e`,
94+
`backend\s+1\s+app-e2e/backend`,
95+
`frontend\s+1\s+8080\s+app-e2e/frontend`,
96+
`ports.frontend\s+8080`,
97+
})
98+
99+
// render bundle
100+
output = info.dockerCmd("app", "image", "render", "app-e2e:v0.9.0")
101+
checkContains(t, output,
102+
[]string{
103+
"image: app-e2e/frontend",
104+
"image: app-e2e/backend",
105+
"published: 8080",
106+
"target: 80",
107+
})
108+
109+
// Install app
110+
output = info.dockerCmd("app", "run", "app-e2e:v0.9.0", "--name", appName)
111+
checkContains(t, output,
112+
[]string{
113+
fmt.Sprintf("Creating service %s_backend", appName),
114+
fmt.Sprintf("Creating service %s_frontend", appName),
115+
fmt.Sprintf("Creating network %s_default", appName),
116+
})
117+
118+
// Status check -- poll app list
119+
checkStatus := func(lastAction string) {
120+
err = wait.Poll(2*time.Second, pollTimeout, func() (bool, error) {
121+
fmt.Println("Polling app status...")
122+
output = info.dockerCmd("app", "ls")
123+
fmt.Println(output)
124+
expectedLines := []string{
125+
`RUNNING APP\s+APP NAME\s+SERVICES\s+LAST ACTION\s+RESULT\s+CREATED\s+MODIFIED\s+REFERENCE`,
126+
fmt.Sprintf(`%s\s+%s \(0.1.0\)\s+2/2\s+%s\s+success\s+.+second[s]?\sago\s+.+second[s]?\sago\s+`, appName, appName, lastAction),
127+
}
128+
matches := true
129+
for _, expected := range expectedLines {
130+
exp := regexp.MustCompile(expected)
131+
matches = matches && exp.MatchString(output)
132+
}
133+
return matches, nil
134+
})
135+
assert.NilError(t, err)
136+
}
137+
138+
queryService := func(port string) {
139+
err = wait.Poll(2*time.Second, pollTimeout, func() (bool, error) {
140+
fmt.Println("Querying service ...")
141+
// Check the frontend service responds
142+
url := `http://localhost:` + port
143+
output = info.execCmd("/usr/bin/wget", "-O", "-", url)
144+
fmt.Println(output)
145+
expectedLines := []string{`Hi there, I love Docker!`}
146+
matches := true
147+
for _, expected := range expectedLines {
148+
exp := regexp.MustCompile(expected)
149+
matches = matches && exp.MatchString(output)
150+
}
151+
return matches, nil
152+
})
153+
assert.NilError(t, err)
154+
}
155+
156+
// Check status on install
157+
checkStatus("install")
158+
159+
// query deployed service
160+
queryService("8080")
161+
162+
// Inspect app
163+
output = info.dockerCmd("app", "inspect", appName, "--pretty")
164+
checkContains(t, output,
165+
[]string{
166+
"Running App:",
167+
fmt.Sprintf("Name: %s", appName),
168+
"Result: success",
169+
`ports.frontend: "8080"`,
170+
})
171+
172+
// Update the application, changing the port
173+
output = info.dockerCmd("app", "update", appName, "--set", "ports.frontend=8081")
174+
checkContains(t, output,
175+
[]string{
176+
fmt.Sprintf("Updating service %s_backend", appName),
177+
fmt.Sprintf("Updating service %s_frontend", appName),
178+
})
179+
180+
// check status on upgrade
181+
checkStatus("upgrade")
182+
183+
// Check the frontend service responds on the new port
184+
queryService("8081")
185+
186+
// Uninstall the application
187+
output = info.dockerCmd("app", "rm", appName)
188+
checkContains(t, output,
189+
[]string{
190+
fmt.Sprintf("Removing service %s_backend", appName),
191+
fmt.Sprintf("Removing service %s_frontend", appName),
192+
fmt.Sprintf("Removing network %s_default", appName),
193+
})
194+
})
195+
}

e2e/helper_test.go

Lines changed: 56 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,13 @@ type dindSwarmAndRegistryInfo struct {
2929
swarmAddress string
3030
registryAddress string
3131
configuredCmd icmd.Cmd
32+
configDir string
33+
tmpDir *fs.Dir
3234
stopRegistry func()
3335
registryLogs func() string
36+
dockerCmd func(...string) string
37+
execCmd func(...string) string
38+
localCmd func(...string) string
3439
}
3540

3641
func runWithDindSwarmAndRegistry(t *testing.T, todo func(dindSwarmAndRegistryInfo)) {
@@ -40,15 +45,40 @@ func runWithDindSwarmAndRegistry(t *testing.T, todo func(dindSwarmAndRegistryInf
4045
tmpDir := fs.NewDir(t, t.Name())
4146
defer tmpDir.Remove()
4247

48+
var configDir string
49+
for _, val := range cmd.Env {
50+
if ok := strings.HasPrefix(val, "DOCKER_CONFIG="); ok {
51+
configDir = strings.Replace(val, "DOCKER_CONFIG=", "", 1)
52+
}
53+
}
54+
55+
// Initialize the info struct
56+
runner := dindSwarmAndRegistryInfo{configuredCmd: cmd, configDir: configDir, tmpDir: tmpDir}
57+
58+
// Func to execute command locally
59+
runLocalCmd := func(params ...string) string {
60+
if len(params) == 0 {
61+
return ""
62+
}
63+
cmd := icmd.Command(params[0], params[1:]...)
64+
result := icmd.RunCmd(cmd)
65+
result.Assert(t, icmd.Success)
66+
return result.Combined()
67+
}
68+
// Func to execute docker cli commands
69+
runDockerCmd := func(params ...string) string {
70+
runner.configuredCmd.Command = dockerCli.Command(params...)
71+
result := icmd.RunCmd(runner.configuredCmd)
72+
result.Assert(t, icmd.Success)
73+
return result.Combined()
74+
}
75+
4376
// The dind doesn't have the cnab-app-base image so we save it in order to load it later
44-
saveCmd := icmd.Cmd{Command: dockerCli.Command("save", fmt.Sprintf("docker/cnab-app-base:%s", internal.Version), "-o", tmpDir.Join("cnab-app-base.tar.gz"))}
45-
icmd.RunCmd(saveCmd).Assert(t, icmd.Success)
77+
runDockerCmd("save", fmt.Sprintf("docker/cnab-app-base:%s", internal.Version), "-o", tmpDir.Join("cnab-app-base.tar.gz"))
4678

4779
// Busybox is used in a few e2e test, let's pre-load it
48-
cmd.Command = dockerCli.Command("pull", "busybox:1.30.1")
49-
icmd.RunCmd(cmd).Assert(t, icmd.Success)
50-
saveCmd = icmd.Cmd{Command: dockerCli.Command("save", "busybox:1.30.1", "-o", tmpDir.Join("busybox.tar.gz"))}
51-
icmd.RunCmd(saveCmd).Assert(t, icmd.Success)
80+
runDockerCmd("pull", "busybox:1.30.1")
81+
runDockerCmd("save", "busybox:1.30.1", "-o", tmpDir.Join("busybox.tar.gz"))
5282

5383
// we have a difficult constraint here:
5484
// - the registry must be reachable from the client side (for cnab-to-oci, which does not use the docker daemon to access the registry)
@@ -57,38 +87,40 @@ func runWithDindSwarmAndRegistry(t *testing.T, todo func(dindSwarmAndRegistryInf
5787
// Solution found is: use host external IP (not loopback) so accessing from within installer container will reach the right container
5888

5989
registry := NewContainer("registry:2", 5000)
60-
registry.Start(t, "-e", "REGISTRY_VALIDATION_MANIFESTS_URLS_ALLOW=[^http]",
90+
registry.Start(t, "--name", "registry", "-e", "REGISTRY_VALIDATION_MANIFESTS_URLS_ALLOW=[^http]",
6191
"-e", "REGISTRY_HTTP_ADDR=0.0.0.0:5000")
6292
defer registry.StopNoFail()
6393
registryAddress := registry.GetAddress(t)
6494

6595
swarm := NewContainer("docker:19.03.3-dind", 2375, "--insecure-registry", registryAddress)
66-
swarm.Start(t, "-e", "DOCKER_TLS_CERTDIR=") // Disable certificate generate on DinD startup
96+
swarm.Start(t, "--name", "dind", "-e", "DOCKER_TLS_CERTDIR=") // Disable certificate generate on DinD startup
6797
defer swarm.Stop(t)
6898
swarmAddress := swarm.GetAddress(t)
6999

70-
cmd.Command = dockerCli.Command("context", "create", "swarm-context", "--docker", fmt.Sprintf(`"host=tcp://%s"`, swarmAddress), "--default-stack-orchestrator", "swarm")
71-
icmd.RunCmd(cmd).Assert(t, icmd.Success)
100+
// Initialize the info struct
101+
runner.registryAddress = registryAddress
102+
runner.swarmAddress = swarmAddress
103+
runner.stopRegistry = registry.StopNoFail
104+
runner.registryLogs = registry.Logs(t)
105+
106+
runDockerCmd("context", "create", "swarm-context", "--docker", fmt.Sprintf(`"host=tcp://%s"`, swarmAddress), "--default-stack-orchestrator", "swarm")
107+
108+
runner.configuredCmd.Env = append(runner.configuredCmd.Env, "DOCKER_CONTEXT=swarm-context", "DOCKER_INSTALLER_CONTEXT=swarm-context")
72109

73-
cmd.Env = append(cmd.Env, "DOCKER_CONTEXT=swarm-context", "DOCKER_INSTALLER_CONTEXT=swarm-context")
74110
// Initialize the swarm
75-
cmd.Command = dockerCli.Command("swarm", "init")
76-
icmd.RunCmd(cmd).Assert(t, icmd.Success)
111+
runDockerCmd("swarm", "init")
77112
// Load the needed base cnab image into the swarm docker engine
78-
cmd.Command = dockerCli.Command("load", "-i", tmpDir.Join("cnab-app-base.tar.gz"))
79-
icmd.RunCmd(cmd).Assert(t, icmd.Success)
113+
runDockerCmd("load", "-i", tmpDir.Join("cnab-app-base.tar.gz"))
80114
// Pre-load busybox image used by a few e2e tests
81-
cmd.Command = dockerCli.Command("load", "-i", tmpDir.Join("busybox.tar.gz"))
82-
icmd.RunCmd(cmd).Assert(t, icmd.Success)
115+
runDockerCmd("load", "-i", tmpDir.Join("busybox.tar.gz"))
83116

84-
info := dindSwarmAndRegistryInfo{
85-
configuredCmd: cmd,
86-
registryAddress: registryAddress,
87-
swarmAddress: swarmAddress,
88-
stopRegistry: registry.StopNoFail,
89-
registryLogs: registry.Logs(t),
117+
runner.localCmd = runLocalCmd
118+
runner.dockerCmd = runDockerCmd
119+
runner.execCmd = func(params ...string) string {
120+
args := append([]string{"docker", "exec", "-t", "dind"}, params...)
121+
return runLocalCmd(args...)
90122
}
91-
todo(info)
123+
todo(runner)
92124
}
93125

94126
func build(t *testing.T, cmd icmd.Cmd, dockerCli dockerCliCommand, ref, path string) {

0 commit comments

Comments
 (0)