github.com/docker/app@v0.9.1-beta3.0.20210611140623-a48f773ab002/e2e/helper_test.go (about) 1 package e2e 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "net" 7 "strconv" 8 "strings" 9 "testing" 10 "time" 11 12 "github.com/docker/app/internal" 13 "github.com/jackpal/gateway" 14 "gotest.tools/assert" 15 "gotest.tools/fs" 16 "gotest.tools/icmd" 17 ) 18 19 // readFile returns the content of the file at the designated path normalizing 20 // line endings by removing any \r. 21 func readFile(t *testing.T, path string) string { 22 t.Helper() 23 content, err := ioutil.ReadFile(path) 24 assert.NilError(t, err, "missing '"+path+"' file") 25 return strings.Replace(string(content), "\r", "", -1) 26 } 27 28 type dindSwarmAndRegistryInfo struct { 29 swarmAddress string 30 registryAddress string 31 configuredCmd icmd.Cmd 32 configDir string 33 tmpDir *fs.Dir 34 stopRegistry func() 35 registryLogs func() string 36 dockerCmd func(...string) string 37 execCmd func(...string) string 38 localCmd func(...string) string 39 } 40 41 func runWithDindSwarmAndRegistry(t *testing.T, todo func(dindSwarmAndRegistryInfo)) { 42 cmd, cleanup := dockerCli.createTestCmd() 43 defer cleanup() 44 45 tmpDir := fs.NewDir(t, t.Name()) 46 defer tmpDir.Remove() 47 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 return result.Combined() 66 } 67 // Func to execute docker cli commands 68 runDockerCmd := func(params ...string) string { 69 runner.configuredCmd.Command = dockerCli.Command(params...) 70 result := icmd.RunCmd(runner.configuredCmd) 71 result.Assert(t, icmd.Success) 72 return result.Combined() 73 } 74 75 // The dind doesn't have the cnab-app-base image so we save it in order to load it later 76 runDockerCmd("save", fmt.Sprintf("docker/cnab-app-base:%s", internal.Version), "-o", tmpDir.Join("cnab-app-base.tar.gz")) 77 78 // Busybox is used in a few e2e test, let's pre-load it 79 runDockerCmd("pull", "busybox:1.30.1") 80 runDockerCmd("save", "busybox:1.30.1", "-o", tmpDir.Join("busybox.tar.gz")) 81 82 // we have a difficult constraint here: 83 // - the registry must be reachable from the client side (for cnab-to-oci, which does not use the docker daemon to access the registry) 84 // - the registry must be reachable from the dind daemon on the same address/port 85 // - the installer image need to target the same docker context (dind) as the client, while running on default (or another) context, which means we can't use 'localhost' 86 // Solution found is: use host external IP (not loopback) so accessing from within installer container will reach the right container 87 88 registry := NewContainer("registry:2", 5000) 89 registry.Start(t, "-e", "REGISTRY_VALIDATION_MANIFESTS_URLS_ALLOW=[^http]", 90 "-e", "REGISTRY_HTTP_ADDR=0.0.0.0:5000") 91 defer registry.StopNoFail() 92 registryAddress := registry.GetAddress(t) 93 94 swarm := NewContainer("docker:19.03.3-dind", 2375, "--insecure-registry", registryAddress) 95 swarm.Start(t, "-e", "DOCKER_TLS_CERTDIR=") // Disable certificate generate on DinD startup 96 defer swarm.Stop(t) 97 swarmAddress := swarm.GetAddress(t) 98 99 // Initialize the info struct 100 runner.registryAddress = registryAddress 101 runner.swarmAddress = swarmAddress 102 runner.stopRegistry = registry.StopNoFail 103 runner.registryLogs = registry.Logs(t) 104 105 runDockerCmd("context", "create", "swarm-context", "--docker", fmt.Sprintf(`"host=tcp://%s"`, swarmAddress), "--default-stack-orchestrator", "swarm") 106 107 runner.configuredCmd.Env = append(runner.configuredCmd.Env, "DOCKER_CONTEXT=swarm-context", "DOCKER_INSTALLER_CONTEXT=swarm-context") 108 109 // Initialize the swarm 110 runDockerCmd("swarm", "init") 111 // Load the needed base cnab image into the swarm docker engine 112 runDockerCmd("load", "-i", tmpDir.Join("cnab-app-base.tar.gz")) 113 // Pre-load busybox image used by a few e2e tests 114 runDockerCmd("load", "-i", tmpDir.Join("busybox.tar.gz")) 115 116 runner.localCmd = runLocalCmd 117 runner.dockerCmd = runDockerCmd 118 runner.execCmd = func(params ...string) string { 119 args := append([]string{"docker", "exec", "-t", swarm.container}, params...) 120 return runLocalCmd(args...) 121 } 122 todo(runner) 123 } 124 125 func build(t *testing.T, cmd icmd.Cmd, dockerCli dockerCliCommand, ref, path string) { 126 iidfile := fs.NewFile(t, "iid") 127 defer iidfile.Remove() 128 cmd.Command = dockerCli.Command("app", "build", "--iidfile", iidfile.Path(), "-t", ref, path) 129 icmd.RunCmd(cmd).Assert(t, icmd.Success) 130 _, err := ioutil.ReadFile(iidfile.Path()) 131 assert.NilError(t, err) 132 } 133 134 // Container represents a docker container 135 type Container struct { 136 image string 137 privatePort int 138 address string 139 container string 140 parentContainer string 141 args []string 142 } 143 144 // NewContainer creates a new Container 145 func NewContainer(image string, privatePort int, args ...string) *Container { 146 return &Container{ 147 image: image, 148 privatePort: privatePort, 149 args: args, 150 } 151 } 152 153 // Start starts a new docker container on a random port 154 func (c *Container) Start(t *testing.T, dockerArgs ...string) { 155 args := []string{"run", "--rm", "--privileged", "-d", "-P"} 156 args = append(args, dockerArgs...) 157 args = append(args, c.image) 158 args = append(args, c.args...) 159 result := icmd.RunCommand(dockerCli.path, args...).Assert(t, icmd.Success) 160 c.container = strings.Trim(result.Stdout(), " \r\n") 161 time.Sleep(time.Second * 3) 162 } 163 164 // StartWithContainerNetwork starts a new container using an existing container network 165 func (c *Container) StartWithContainerNetwork(t *testing.T, other *Container, dockerArgs ...string) { 166 args := []string{"run", "--rm", "--privileged", "-d", "--network=container:" + other.container} 167 args = append(args, dockerArgs...) 168 args = append(args, c.image) 169 args = append(args, c.args...) 170 result := icmd.RunCommand(dockerCli.path, args...).Assert(t, icmd.Success) 171 c.container = strings.Trim(result.Stdout(), " \r\n") 172 time.Sleep(time.Second * 3) 173 c.parentContainer = other.container 174 } 175 176 // Stop terminates this container 177 func (c *Container) Stop(t *testing.T) { 178 icmd.RunCommand(dockerCli.path, "stop", c.container).Assert(t, icmd.Success) 179 } 180 181 // StopNoFail terminates this container 182 func (c *Container) StopNoFail() { 183 icmd.RunCommand(dockerCli.path, "stop", c.container) 184 } 185 186 // GetAddress returns the host:port this container listens on 187 func (c *Container) GetAddress(t *testing.T) string { 188 if c.address != "" { 189 return c.address 190 } 191 ip := c.getIP(t) 192 port := c.getPort(t) 193 c.address = fmt.Sprintf("%s:%v", ip, port) 194 return c.address 195 } 196 197 func (c *Container) getPort(t *testing.T) string { 198 result := icmd.RunCommand(dockerCli.path, "port", c.container, strconv.Itoa(c.privatePort)).Assert(t, icmd.Success) 199 port := strings.Trim(strings.Split(result.Stdout(), ":")[1], " \r\n") 200 return port 201 } 202 203 var host string 204 205 func (c *Container) getIP(t *testing.T) string { 206 if host != "" { 207 return host 208 } 209 // Discover default gateway 210 gw, err := gateway.DiscoverGateway() 211 assert.NilError(t, err) 212 213 // Search for the interface configured on the same network as the gateway 214 addrs, err := net.InterfaceAddrs() 215 assert.NilError(t, err) 216 for _, a := range addrs { 217 if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { 218 net1 := ipnet.IP.Mask(ipnet.Mask).String() 219 net2 := gw.Mask(ipnet.Mask).String() 220 if net1 == net2 { 221 host = ipnet.IP.String() 222 break 223 } 224 } 225 } 226 return host 227 } 228 229 func (c *Container) Logs(t *testing.T) func() string { 230 return func() string { 231 return icmd.RunCommand(dockerCli.path, "logs", c.container).Assert(t, icmd.Success).Combined() 232 } 233 }