github.com/nathants/docker-trace@v0.0.0-20220831131939-668bc05a257b/lib/minify_test.go (about) 1 package lib 2 3 import ( 4 "bufio" 5 "bytes" 6 "crypto/md5" 7 "crypto/tls" 8 "encoding/hex" 9 "fmt" 10 "io" 11 "net/http" 12 "os" 13 "os/exec" 14 "path" 15 "runtime" 16 "strings" 17 "syscall" 18 "testing" 19 "time" 20 ) 21 22 func md5SumMinify(bytes []byte) string { 23 hash := md5.Sum(bytes) 24 return hex.EncodeToString(hash[:]) 25 } 26 27 func runStdinMinify(stdin string, command ...string) error { 28 cmd := exec.Command(command[0], command[1:]...) 29 cmd.Stderr = os.Stderr 30 cmd.Stdout = os.Stdout 31 cmd.Stdin = bytes.NewBufferString(stdin + "\n") 32 return cmd.Run() 33 } 34 35 func runStdoutMinify(command ...string) (string, error) { 36 cmd := exec.Command(command[0], command[1:]...) 37 var stdout bytes.Buffer 38 cmd.Stderr = os.Stderr 39 cmd.Stdout = &stdout 40 err := cmd.Run() 41 return strings.Trim(stdout.String(), "\n"), err 42 } 43 44 func runStdoutStderrChanMinify(command ...string) (<-chan string, <-chan string, func(), error) { 45 cmd := exec.Command(command[0], command[1:]...) 46 stderr, err := cmd.StderrPipe() 47 if err != nil { 48 return nil, nil, nil, err 49 } 50 stdout, err := cmd.StdoutPipe() 51 if err != nil { 52 return nil, nil, nil, err 53 } 54 stderrChan := make(chan string) 55 stdoutChan := make(chan string, 1024*1024) 56 tail := func(c chan<- string, r io.ReadCloser) { 57 // defer func() {}() 58 buf := bufio.NewReader(r) 59 for { 60 line, err := buf.ReadBytes('\n') 61 if err != nil { 62 close(c) 63 return 64 } 65 c <- strings.TrimRight(string(line), "\n") 66 } 67 } 68 go tail(stderrChan, stderr) 69 go tail(stdoutChan, stdout) 70 err = cmd.Start() 71 if err != nil { 72 return nil, nil, nil, err 73 } 74 go func() { 75 // defer func() {}() 76 _ = cmd.Wait() 77 }() 78 cancel := func() { 79 _ = syscall.Kill(cmd.Process.Pid, syscall.SIGINT) 80 } 81 return stdoutChan, stderrChan, cancel, err 82 } 83 84 func runMinify(command ...string) error { 85 cmd := exec.Command(command[0], command[1:]...) 86 cmd.Stderr = os.Stderr 87 cmd.Stdout = os.Stdout 88 return cmd.Run() 89 } 90 91 func runQuietMinify(command ...string) error { 92 cmd := exec.Command(command[0], command[1:]...) 93 return cmd.Run() 94 } 95 96 func climbGitRootMinify() { 97 _, filename, _, _ := runtime.Caller(1) 98 err := os.Chdir(path.Dir(filename)) 99 if err != nil { 100 panic(err) 101 } 102 outer: 103 for { 104 files, err := os.ReadDir(".") 105 if err != nil { 106 panic(err) 107 } 108 for _, file := range files { 109 if file.IsDir() && file.Name() == ".git" { 110 break outer 111 } 112 if file.Name() == "/" { 113 panic("/") 114 } 115 } 116 err = os.Chdir("..") 117 if err != nil { 118 panic(err) 119 } 120 } 121 } 122 123 func ensureDockerTraceMinify() { 124 err := runMinify("go", "build", ".") 125 if err != nil { 126 panic(err) 127 } 128 } 129 130 func ensureTestContainerMinify(app, kind string) string { 131 stdout, err := runStdoutMinify("bash", "-c", fmt.Sprintf("cat examples/%s/*", app)) 132 if err != nil { 133 panic(err) 134 } 135 hash := md5SumMinify([]byte(stdout)) 136 container := fmt.Sprintf("docker-trace:minify-%s-%s-%s", app, kind, hash) 137 if runQuietMinify("docker", "inspect", container) != nil { 138 err = runMinify("docker", "build", "-t", container, "--network", "host", "-f", "examples/"+app+"/Dockerfile."+kind, "examples/"+app) 139 if err != nil { 140 panic(err) 141 } 142 } 143 return container 144 } 145 146 func ensureSetupMinify(app, kind string) string { 147 _ = runQuietMinify("bash", "-c", "docker kill $(docker ps -q)") 148 climbGitRootMinify() 149 ensureDockerTraceMinify() 150 container := ensureTestContainerMinify(app, kind) 151 return container 152 } 153 154 func testWeb(t *testing.T, app, kind string) { 155 container := ensureSetupMinify(app, kind) 156 fmt.Println("start trace container") 157 stdoutChan, stderrChan, cancel, err := runStdoutStderrChanMinify("./docker-trace", "files") 158 if err != nil { 159 t.Error(err) 160 return 161 } 162 fmt.Println("wait for trace container ready") 163 line := <-stderrChan 164 if line != "ready" { 165 t.Error(line) 166 return 167 } 168 fmt.Println("trace container ready, start test container") 169 id, err := runStdoutMinify("docker", "run", "-d", "-t", "--rm", "--network", "host", container) 170 if err != nil { 171 t.Error(err) 172 return 173 } 174 tr := &http.Transport{ 175 TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 176 IdleConnTimeout: 1 * time.Second, 177 TLSHandshakeTimeout: 1 * time.Second, 178 } 179 client := &http.Client{ 180 Transport: tr, 181 Timeout: 1 * time.Second, 182 } 183 // 184 start := time.Now() 185 for { 186 if time.Since(start) > 10*time.Second { 187 t.Error("timeout") 188 return 189 } 190 out, err := client.Get("https://localhost:8080/hello/xyz") 191 if err == nil { 192 bytes, _ := io.ReadAll(out.Body) 193 _ = out.Body.Close() 194 fmt.Println(out.StatusCode, string(bytes)) 195 break 196 } 197 fmt.Println(err) 198 time.Sleep(1 * time.Second) 199 } 200 fmt.Println("test passed, kill test container") 201 // 202 err = runMinify("docker", "kill", id) 203 if err != nil { 204 t.Error(err) 205 return 206 } 207 // 208 fmt.Println("cancel trace container and drain output") 209 cancel() 210 var files []string 211 for line := range stdoutChan { 212 parts := strings.SplitN(line, " ", 2) 213 if len(parts) != 2 { 214 fmt.Println("skipping bad line:", line) 215 continue 216 } 217 fileID := parts[0] 218 file := parts[1] 219 if id == fileID { 220 files = append(files, file) 221 } 222 } 223 // 224 fmt.Println("check that test container is not reachable") 225 out, err := client.Get("https://localhost:8080/hello/xyz") 226 if err == nil { 227 _ = out.Body.Close() 228 t.Error("server should have been killed") 229 return 230 } 231 // 232 fmt.Println("start minification") 233 err = runStdinMinify(strings.Join(files, "\n"), "./docker-trace", "minify", container, container+"-min") 234 if err != nil { 235 t.Error(err) 236 return 237 } 238 // 239 fmt.Println("start minified container") 240 id, err = runStdoutMinify("docker", "run", "-d", "--network", "host", "--rm", container+"-min") 241 if err != nil { 242 t.Error(err) 243 return 244 } 245 // 246 start = time.Now() 247 for { 248 if time.Since(start) > 5*time.Second { 249 t.Error("timeout") 250 return 251 } 252 out, err := client.Get("https://localhost:8080/hello/xyz") 253 if err == nil { 254 bytes, _ := io.ReadAll(out.Body) 255 _ = out.Body.Close() 256 fmt.Println(out.StatusCode, string(bytes)) 257 break 258 } 259 fmt.Println(err) 260 time.Sleep(1 * time.Second) 261 } 262 fmt.Println("compare image sizes") 263 // 264 stdout, err := runStdoutMinify("docker", "inspect", container, "-f", "{{.Size}}") 265 if err != nil { 266 t.Error(err) 267 return 268 } 269 size := Atoi(stdout) 270 // 271 stdout, err = runStdoutMinify("docker", "inspect", container+"-min", "-f", "{{.Size}}") 272 if err != nil { 273 t.Error(err) 274 return 275 } 276 sizeMin := Atoi(stdout) 277 if !(sizeMin < size) { 278 t.Error("not smaller") 279 return 280 } 281 err = runMinify("docker", "kill", id) 282 if err != nil { 283 t.Error(err) 284 return 285 } 286 } 287 288 func TestGoWebArch(t *testing.T) { 289 testWeb(t, "go-web", "arch") 290 } 291 292 func TestGoWebAlpine(t *testing.T) { 293 testWeb(t, "go-web", "alpine") 294 } 295 296 func TestGoWebDebian(t *testing.T) { 297 testWeb(t, "go-web", "debian") 298 } 299 300 func TestGoWebUbuntu(t *testing.T) { 301 testWeb(t, "go-web", "ubuntu") 302 } 303 304 func TestGoWebAmzn(t *testing.T) { 305 testWeb(t, "go-web", "amzn") 306 } 307 308 func TestPythonWebArch(t *testing.T) { 309 testWeb(t, "python3-web", "arch") 310 } 311 312 func TestPythonWebAlpine(t *testing.T) { 313 testWeb(t, "python3-web", "alpine") 314 } 315 316 func TestPythonWebDebian(t *testing.T) { 317 testWeb(t, "python3-web", "debian") 318 } 319 320 func TestPythonWebUbuntu(t *testing.T) { 321 testWeb(t, "python3-web", "ubuntu") 322 } 323 324 func TestPythonWebAmzn(t *testing.T) { 325 testWeb(t, "python3-web", "amzn") 326 } 327 328 func TestNodeWebArch(t *testing.T) { 329 testWeb(t, "node-web", "arch") 330 } 331 332 func TestNodeWebAlpine(t *testing.T) { 333 testWeb(t, "node-web", "alpine") 334 } 335 336 func TestNodeWebDebian(t *testing.T) { 337 testWeb(t, "node-web", "debian") 338 } 339 340 func TestNodeWebUbuntu(t *testing.T) { 341 testWeb(t, "node-web", "ubuntu") 342 } 343 344 // func TestNodeWebAmzn(t *testing.T) { 345 // testWeb(t, "node-web", "amzn") // TODO this dockerfile is no longer building, debug. 346 // }