summaryrefslogtreecommitdiffhomepage
path: root/test/image
diff options
context:
space:
mode:
authorAdin Scannell <ascannell@google.com>2019-09-03 22:01:34 -0700
committergVisor bot <gvisor-bot@google.com>2019-09-03 22:02:43 -0700
commit67a2ab1438cdccbe045143bbfaa807cf83110ebc (patch)
tree8c17f5ecd3e5b3e9c4decb46d8f2b2e893809dc1 /test/image
parent144127e5e1c548150f49501a7decb82ec2e239f2 (diff)
Impose order on test scripts.
The simple test script has gotten out of control. Shard this script into different pieces and attempt to impose order on overall test structure. This change helps lay some of the foundations for future improvements. * The runsc/test directories are moved into just test/. * The runsc/test/testutil package is split into logical pieces. * The scripts/ directory contains new top-level targets. * Each test is now responsible for building targets it requires. * The install functionality is moved into `runsc` itself for simplicity. * The existing kokoro run_tests.sh file now just calls all (can be split). After this change is merged, I will create multiple distinct workflows for Kokoro, one for each of the scripts currently targeted by `run_tests.sh` today, which should dramatically reduce the time-to-run for the Kokoro tests, and provides a better foundation for further improvements to the infrastructure. PiperOrigin-RevId: 267081397
Diffstat (limited to 'test/image')
-rw-r--r--test/image/BUILD34
-rw-r--r--test/image/image.go16
-rw-r--r--test/image/image_test.go353
-rw-r--r--test/image/latin10k.txt33
-rw-r--r--test/image/mysql.sql23
-rw-r--r--test/image/ruby.rb23
-rw-r--r--test/image/ruby.sh20
7 files changed, 502 insertions, 0 deletions
diff --git a/test/image/BUILD b/test/image/BUILD
new file mode 100644
index 000000000..09b0a0ad5
--- /dev/null
+++ b/test/image/BUILD
@@ -0,0 +1,34 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
+
+package(licenses = ["notice"])
+
+go_test(
+ name = "image_test",
+ size = "large",
+ srcs = [
+ "image_test.go",
+ ],
+ data = [
+ "latin10k.txt",
+ "mysql.sql",
+ "ruby.rb",
+ "ruby.sh",
+ ],
+ embed = [":image"],
+ tags = [
+ # Requires docker and runsc to be configured before the test runs.
+ "manual",
+ "local",
+ ],
+ visibility = ["//:sandbox"],
+ deps = [
+ "//runsc/dockerutil",
+ "//runsc/testutil",
+ ],
+)
+
+go_library(
+ name = "image",
+ srcs = ["image.go"],
+ importpath = "gvisor.dev/gvisor/test/image",
+)
diff --git a/test/image/image.go b/test/image/image.go
new file mode 100644
index 000000000..297f1ab92
--- /dev/null
+++ b/test/image/image.go
@@ -0,0 +1,16 @@
+// Copyright 2018 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package image is empty. See image_test.go for description.
+package image
diff --git a/test/image/image_test.go b/test/image/image_test.go
new file mode 100644
index 000000000..d0dcb1861
--- /dev/null
+++ b/test/image/image_test.go
@@ -0,0 +1,353 @@
+// Copyright 2018 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package image provides end-to-end image tests for runsc.
+
+// Each test calls docker commands to start up a container, and tests that it
+// is behaving properly, like connecting to a port or looking at the output.
+// The container is killed and deleted at the end.
+//
+// Setup instruction in test/README.md.
+package image
+
+import (
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "log"
+ "net/http"
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+ "time"
+
+ "gvisor.dev/gvisor/runsc/dockerutil"
+ "gvisor.dev/gvisor/runsc/testutil"
+)
+
+func TestHelloWorld(t *testing.T) {
+ d := dockerutil.MakeDocker("hello-test")
+ if err := d.Run("hello-world"); err != nil {
+ t.Fatalf("docker run failed: %v", err)
+ }
+ defer d.CleanUp()
+
+ if _, err := d.WaitForOutput("Hello from Docker!", 5*time.Second); err != nil {
+ t.Fatalf("docker didn't say hello: %v", err)
+ }
+}
+
+func runHTTPRequest(port int) error {
+ url := fmt.Sprintf("http://localhost:%d/not-found", port)
+ resp, err := http.Get(url)
+ if err != nil {
+ return fmt.Errorf("error reaching http server: %v", err)
+ }
+ if want := http.StatusNotFound; resp.StatusCode != want {
+ return fmt.Errorf("Wrong response code, got: %d, want: %d", resp.StatusCode, want)
+ }
+
+ url = fmt.Sprintf("http://localhost:%d/latin10k.txt", port)
+ resp, err = http.Get(url)
+ if err != nil {
+ return fmt.Errorf("Error reaching http server: %v", err)
+ }
+ if want := http.StatusOK; resp.StatusCode != want {
+ return fmt.Errorf("Wrong response code, got: %d, want: %d", resp.StatusCode, want)
+ }
+
+ body, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return fmt.Errorf("Error reading http response: %v", err)
+ }
+ defer resp.Body.Close()
+
+ // READALL is the last word in the file. Ensures everything was read.
+ if want := "READALL"; strings.HasSuffix(string(body), want) {
+ return fmt.Errorf("response doesn't contain %q, resp: %q", want, body)
+ }
+ return nil
+}
+
+func testHTTPServer(t *testing.T, port int) {
+ const requests = 10
+ ch := make(chan error, requests)
+ for i := 0; i < requests; i++ {
+ go func() {
+ start := time.Now()
+ err := runHTTPRequest(port)
+ log.Printf("Response time %v: %v", time.Since(start).String(), err)
+ ch <- err
+ }()
+ }
+
+ for i := 0; i < requests; i++ {
+ err := <-ch
+ if err != nil {
+ t.Errorf("testHTTPServer(%d) failed: %v", port, err)
+ }
+ }
+}
+
+func TestHttpd(t *testing.T) {
+ if err := dockerutil.Pull("httpd"); err != nil {
+ t.Fatalf("docker pull failed: %v", err)
+ }
+ d := dockerutil.MakeDocker("http-test")
+
+ dir, err := dockerutil.PrepareFiles("latin10k.txt")
+ if err != nil {
+ t.Fatalf("PrepareFiles() failed: %v", err)
+ }
+
+ // Start the container.
+ mountArg := dockerutil.MountArg(dir, "/usr/local/apache2/htdocs", dockerutil.ReadOnly)
+ if err := d.Run("-p", "80", mountArg, "httpd"); err != nil {
+ t.Fatalf("docker run failed: %v", err)
+ }
+ defer d.CleanUp()
+
+ // Find where port 80 is mapped to.
+ port, err := d.FindPort(80)
+ if err != nil {
+ t.Fatalf("docker.FindPort(80) failed: %v", err)
+ }
+
+ // Wait until it's up and running.
+ if err := testutil.WaitForHTTP(port, 30*time.Second); err != nil {
+ t.Errorf("WaitForHTTP() timeout: %v", err)
+ }
+
+ testHTTPServer(t, port)
+}
+
+func TestNginx(t *testing.T) {
+ if err := dockerutil.Pull("nginx"); err != nil {
+ t.Fatalf("docker pull failed: %v", err)
+ }
+ d := dockerutil.MakeDocker("net-test")
+
+ dir, err := dockerutil.PrepareFiles("latin10k.txt")
+ if err != nil {
+ t.Fatalf("PrepareFiles() failed: %v", err)
+ }
+
+ // Start the container.
+ mountArg := dockerutil.MountArg(dir, "/usr/share/nginx/html", dockerutil.ReadOnly)
+ if err := d.Run("-p", "80", mountArg, "nginx"); err != nil {
+ t.Fatalf("docker run failed: %v", err)
+ }
+ defer d.CleanUp()
+
+ // Find where port 80 is mapped to.
+ port, err := d.FindPort(80)
+ if err != nil {
+ t.Fatalf("docker.FindPort(80) failed: %v", err)
+ }
+
+ // Wait until it's up and running.
+ if err := testutil.WaitForHTTP(port, 30*time.Second); err != nil {
+ t.Errorf("WaitForHTTP() timeout: %v", err)
+ }
+
+ testHTTPServer(t, port)
+}
+
+func TestMysql(t *testing.T) {
+ if err := dockerutil.Pull("mysql"); err != nil {
+ t.Fatalf("docker pull failed: %v", err)
+ }
+ d := dockerutil.MakeDocker("mysql-test")
+
+ // Start the container.
+ if err := d.Run("-e", "MYSQL_ROOT_PASSWORD=foobar123", "mysql"); err != nil {
+ t.Fatalf("docker run failed: %v", err)
+ }
+ defer d.CleanUp()
+
+ // Wait until it's up and running.
+ if _, err := d.WaitForOutput("port: 3306 MySQL Community Server", 3*time.Minute); err != nil {
+ t.Fatalf("docker.WaitForOutput() timeout: %v", err)
+ }
+
+ client := dockerutil.MakeDocker("mysql-client-test")
+ dir, err := dockerutil.PrepareFiles("mysql.sql")
+ if err != nil {
+ t.Fatalf("PrepareFiles() failed: %v", err)
+ }
+
+ // Tell mysql client to connect to the server and execute the file in verbose
+ // mode to verify the output.
+ args := []string{
+ dockerutil.LinkArg(&d, "mysql"),
+ dockerutil.MountArg(dir, "/sql", dockerutil.ReadWrite),
+ "mysql",
+ "mysql", "-hmysql", "-uroot", "-pfoobar123", "-v", "-e", "source /sql/mysql.sql",
+ }
+ if err := client.Run(args...); err != nil {
+ t.Fatalf("docker run failed: %v", err)
+ }
+ defer client.CleanUp()
+
+ // Ensure file executed to the end and shutdown mysql.
+ if _, err := client.WaitForOutput("--------------\nshutdown\n--------------", 15*time.Second); err != nil {
+ t.Fatalf("docker.WaitForOutput() timeout: %v", err)
+ }
+ if _, err := d.WaitForOutput("mysqld: Shutdown complete", 30*time.Second); err != nil {
+ t.Fatalf("docker.WaitForOutput() timeout: %v", err)
+ }
+}
+
+func TestPythonHello(t *testing.T) {
+ // TODO(b/136503277): Once we have more complete python runtime tests,
+ // we can drop this one.
+ const img = "gcr.io/gvisor-presubmit/python-hello"
+ if err := dockerutil.Pull(img); err != nil {
+ t.Fatalf("docker pull failed: %v", err)
+ }
+ d := dockerutil.MakeDocker("python-hello-test")
+ if err := d.Run("-p", "8080", img); err != nil {
+ t.Fatalf("docker run failed: %v", err)
+ }
+ defer d.CleanUp()
+
+ // Find where port 8080 is mapped to.
+ port, err := d.FindPort(8080)
+ if err != nil {
+ t.Fatalf("docker.FindPort(8080) failed: %v", err)
+ }
+
+ // Wait until it's up and running.
+ if err := testutil.WaitForHTTP(port, 30*time.Second); err != nil {
+ t.Fatalf("WaitForHTTP() timeout: %v", err)
+ }
+
+ // Ensure that content is being served.
+ url := fmt.Sprintf("http://localhost:%d", port)
+ resp, err := http.Get(url)
+ if err != nil {
+ t.Errorf("Error reaching http server: %v", err)
+ }
+ if want := http.StatusOK; resp.StatusCode != want {
+ t.Errorf("Wrong response code, got: %d, want: %d", resp.StatusCode, want)
+ }
+}
+
+func TestTomcat(t *testing.T) {
+ if err := dockerutil.Pull("tomcat:8.0"); err != nil {
+ t.Fatalf("docker pull failed: %v", err)
+ }
+ d := dockerutil.MakeDocker("tomcat-test")
+ if err := d.Run("-p", "8080", "tomcat:8.0"); err != nil {
+ t.Fatalf("docker run failed: %v", err)
+ }
+ defer d.CleanUp()
+
+ // Find where port 8080 is mapped to.
+ port, err := d.FindPort(8080)
+ if err != nil {
+ t.Fatalf("docker.FindPort(8080) failed: %v", err)
+ }
+
+ // Wait until it's up and running.
+ if err := testutil.WaitForHTTP(port, 30*time.Second); err != nil {
+ t.Fatalf("WaitForHTTP() timeout: %v", err)
+ }
+
+ // Ensure that content is being served.
+ url := fmt.Sprintf("http://localhost:%d", port)
+ resp, err := http.Get(url)
+ if err != nil {
+ t.Errorf("Error reaching http server: %v", err)
+ }
+ if want := http.StatusOK; resp.StatusCode != want {
+ t.Errorf("Wrong response code, got: %d, want: %d", resp.StatusCode, want)
+ }
+}
+
+func TestRuby(t *testing.T) {
+ if err := dockerutil.Pull("ruby"); err != nil {
+ t.Fatalf("docker pull failed: %v", err)
+ }
+ d := dockerutil.MakeDocker("ruby-test")
+
+ dir, err := dockerutil.PrepareFiles("ruby.rb", "ruby.sh")
+ if err != nil {
+ t.Fatalf("PrepareFiles() failed: %v", err)
+ }
+ if err := os.Chmod(filepath.Join(dir, "ruby.sh"), 0333); err != nil {
+ t.Fatalf("os.Chmod(%q, 0333) failed: %v", dir, err)
+ }
+
+ if err := d.Run("-p", "8080", dockerutil.MountArg(dir, "/src", dockerutil.ReadOnly), "ruby", "/src/ruby.sh"); err != nil {
+ t.Fatalf("docker run failed: %v", err)
+ }
+ defer d.CleanUp()
+
+ // Find where port 8080 is mapped to.
+ port, err := d.FindPort(8080)
+ if err != nil {
+ t.Fatalf("docker.FindPort(8080) failed: %v", err)
+ }
+
+ // Wait until it's up and running, 'gem install' can take some time.
+ if err := testutil.WaitForHTTP(port, 1*time.Minute); err != nil {
+ t.Fatalf("WaitForHTTP() timeout: %v", err)
+ }
+
+ // Ensure that content is being served.
+ url := fmt.Sprintf("http://localhost:%d", port)
+ resp, err := http.Get(url)
+ if err != nil {
+ t.Errorf("error reaching http server: %v", err)
+ }
+ if want := http.StatusOK; resp.StatusCode != want {
+ t.Errorf("wrong response code, got: %d, want: %d", resp.StatusCode, want)
+ }
+ body, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ t.Fatalf("error reading body: %v", err)
+ }
+ if got, want := string(body), "Hello World"; !strings.Contains(got, want) {
+ t.Errorf("invalid body content, got: %q, want: %q", got, want)
+ }
+}
+
+func TestStdio(t *testing.T) {
+ if err := dockerutil.Pull("alpine"); err != nil {
+ t.Fatalf("docker pull failed: %v", err)
+ }
+ d := dockerutil.MakeDocker("stdio-test")
+
+ wantStdout := "hello stdout"
+ wantStderr := "bonjour stderr"
+ cmd := fmt.Sprintf("echo %q; echo %q 1>&2;", wantStdout, wantStderr)
+ if err := d.Run("alpine", "/bin/sh", "-c", cmd); err != nil {
+ t.Fatalf("docker run failed: %v", err)
+ }
+ defer d.CleanUp()
+
+ for _, want := range []string{wantStdout, wantStderr} {
+ if _, err := d.WaitForOutput(want, 5*time.Second); err != nil {
+ t.Fatalf("docker didn't get output %q : %v", want, err)
+ }
+ }
+}
+
+func TestMain(m *testing.M) {
+ dockerutil.EnsureSupportedDockerVersion()
+ flag.Parse()
+ os.Exit(m.Run())
+}
diff --git a/test/image/latin10k.txt b/test/image/latin10k.txt
new file mode 100644
index 000000000..61341e00b
--- /dev/null
+++ b/test/image/latin10k.txt
@@ -0,0 +1,33 @@
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras ut placerat felis. Maecenas urna est, auctor a efficitur sit amet, egestas et augue. Curabitur dignissim scelerisque nunc vel cursus. Ut vehicula est pretium, consectetur nunc non, pharetra ligula. Curabitur ut ultricies metus. Suspendisse pulvinar, orci sed fermentum vestibulum, eros turpis molestie lectus, nec elementum risus dolor mattis felis. Donec ultrices ipsum sem, at pretium lacus convallis at. Mauris nulla enim, tincidunt non bibendum at, vehicula pulvinar mauris.
+
+Duis in dapibus turpis. Pellentesque maximus magna odio, ac congue libero laoreet quis. Maecenas euismod risus in justo aliquam accumsan. Nunc quis ornare arcu, sit amet sodales elit. Phasellus nec scelerisque nisl, a tincidunt arcu. Proin ornare est nunc, sed suscipit orci interdum et. Suspendisse condimentum venenatis diam in tempor. Aliquam egestas lectus in rutrum tempus. Donec id egestas eros. Donec molestie consequat purus, sed posuere odio venenatis vitae. Nunc placerat augue id vehicula varius. In hac habitasse platea dictumst. Proin at est accumsan, venenatis quam a, fermentum risus. Phasellus posuere pellentesque enim, id suscipit magna consequat ut. Quisque ut tortor ante.
+
+Cras ut vulputate metus, a laoreet lectus. Vivamus ultrices molestie odio in tristique. Morbi faucibus mi eget sollicitudin fringilla. Fusce vitae lacinia ligula. Sed egestas sed diam eu posuere. Maecenas justo nisl, venenatis vel nibh vel, cursus aliquam velit. Praesent lacinia dui id erat venenatis rhoncus. Morbi gravida felis ante, sit amet vehicula orci rhoncus vitae.
+
+Sed finibus sagittis dictum. Proin auctor suscipit sem et mattis. Phasellus libero ligula, pellentesque ut felis porttitor, fermentum sollicitudin orci. Nulla eu nulla nibh. Fusce a eros risus. Proin vel magna risus. Donec nec elit eleifend, scelerisque sapien vitae, pharetra quam. Donec porttitor mauris scelerisque, tempus orci hendrerit, dapibus felis. Nullam libero elit, sollicitudin a aliquam at, ultrices in erat. Mauris eget ligula sodales, porta turpis et, scelerisque odio. Mauris mollis leo vitae purus gravida, in tempor nunc efficitur. Nulla facilisis posuere augue, nec pellentesque lectus eleifend ac. Vestibulum convallis est a feugiat tincidunt. Donec vitae enim volutpat, tincidunt eros eu, malesuada nibh.
+
+Quisque molestie, magna ornare elementum convallis, erat enim sagittis ipsum, eget porttitor sapien arcu id purus. Donec ut cursus diam. Nulla rutrum nulla et mi fermentum, vel tempus tellus posuere. Proin vitae pharetra nulla, nec ornare ex. Nulla consequat, augue a accumsan euismod, turpis leo ornare ligula, a pulvinar enim dolor ut augue. Quisque volutpat, lectus a varius mollis, nisl eros feugiat sem, at egestas lacus justo eu elit. Vestibulum scelerisque mauris est, sagittis interdum nunc accumsan sit amet. Maecenas aliquet ex ut lacus ornare, eu sagittis nibh imperdiet. Duis ultrices nisi velit, sed sodales risus sollicitudin et. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Etiam a accumsan augue, vitae pulvinar nulla. Pellentesque euismod sodales magna, nec luctus eros mattis eget. Sed lacinia suscipit lectus, eget consectetur dui pellentesque sed. Nullam nec mattis tellus.
+
+Aliquam erat volutpat. Praesent lobortis massa porttitor eros tincidunt, nec consequat diam pharetra. Duis efficitur non lorem sed mattis. Suspendisse justo nunc, pulvinar eu porttitor at, facilisis id eros. Suspendisse potenti. Cras molestie aliquet orci ut fermentum. In tempus aliquet eros nec suscipit. Suspendisse in mauris ut lectus ultrices blandit sit amet vitae est. Nam magna massa, porttitor ut semper id, feugiat vel quam. Suspendisse dignissim posuere scelerisque. Donec scelerisque lorem efficitur suscipit suscipit. Nunc luctus ligula et scelerisque lacinia.
+
+Suspendisse potenti. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed ultrices, sem in venenatis scelerisque, tellus ipsum porttitor urna, et iaculis lectus odio ac nisi. Integer luctus dui urna, at sollicitudin elit dapibus eu. Praesent nibh ante, porttitor a ante in, ullamcorper pretium felis. Aliquam vel tortor imperdiet, imperdiet lorem et, cursus mi. Proin tempus velit est, ut hendrerit metus gravida sed. Sed nibh sapien, faucibus quis ipsum in, scelerisque lacinia elit. In nec magna eu magna laoreet rhoncus. Donec vitae rutrum mauris. Integer urna felis, consequat at rhoncus vitae, auctor quis elit. Duis a pulvinar sem, nec gravida nisl. Nam non dapibus purus. Praesent vestibulum turpis nec erat porttitor, a scelerisque purus tincidunt.
+
+Nam fringilla leo nisi, nec placerat nisl luctus eget. Aenean malesuada nunc porta sapien sodales convallis. Suspendisse ut massa tempor, ullamcorper mi ut, faucibus turpis. Vivamus at sagittis metus. Donec varius ac mi eget sodales. Nulla feugiat, nulla eu fringilla fringilla, nunc lorem sollicitudin quam, vitae lacinia velit lorem eu orci. Mauris leo urna, pellentesque ac posuere non, pellentesque sit amet quam.
+
+Vestibulum porta diam urna, a aliquet nibh vestibulum et. Proin interdum bibendum nisl sed rhoncus. Sed vel diam hendrerit, faucibus ante et, hendrerit diam. Nunc dolor augue, mattis non dolor vel, luctus sodales neque. Cras malesuada fermentum dolor eu lobortis. Integer dapibus volutpat consequat. Maecenas posuere feugiat nunc. Donec vel mollis elit, volutpat consequat enim. Nulla id nisi finibus orci imperdiet elementum. Phasellus ultrices, elit vitae consequat rutrum, nisl est congue massa, quis condimentum justo nisi vitae turpis. Maecenas aliquet risus sit amet accumsan elementum. Proin non finibus elit, sit amet lobortis augue.
+
+Morbi pretium pulvinar sem vel sollicitudin. Proin imperdiet fringilla leo, non pellentesque lacus gravida nec. Vivamus ullamcorper consectetur ligula eu consectetur. Curabitur sit amet tempus purus. Curabitur quam quam, tincidunt eu tempus vel, volutpat at ipsum. Maecenas lobortis elit ac justo interdum, sit amet mattis ligula mollis. Sed posuere ligula et felis convallis tempor. Aliquam nec mollis velit. Donec varius sit amet erat at imperdiet. Nulla ipsum justo, tempor non sollicitudin gravida, dignissim vel orci. In hac habitasse platea dictumst. Cras cursus tellus id arcu aliquet accumsan. Phasellus ac erat dui.
+
+Duis mollis metus at mi luctus aliquam. Duis varius eget erat ac porttitor. Phasellus lobortis sagittis lacinia. Etiam sagittis eget erat in pulvinar. Phasellus sodales risus nec vulputate accumsan. Cras sit amet pellentesque dui. Praesent consequat felis mi, at vulputate diam convallis a. Donec hendrerit nibh vel justo consequat dictum. In euismod, dui sit amet malesuada suscipit, mauris ex rhoncus eros, sed ornare arcu nunc eu urna. Pellentesque eget erat augue. Integer rutrum mauris sem, nec sodales nulla cursus vel. Vivamus porta, urna vel varius vulputate, nulla arcu malesuada dui, a ultrices magna ante sed nibh.
+
+Morbi ultricies aliquam lorem id bibendum. Donec sit amet nunc vitae massa gravida eleifend hendrerit vel libero. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nulla vestibulum tempus condimentum. Aliquam dolor ipsum, condimentum in sapien et, tempor iaculis nulla. Aenean non pharetra augue. Maecenas mattis dignissim maximus. Fusce elementum tincidunt massa sit amet lobortis. Phasellus nec pharetra dui, et malesuada ante. Nullam commodo pretium tellus. Praesent sollicitudin, enim eget imperdiet scelerisque, odio felis vulputate dolor, eget auctor neque tellus ac lorem.
+
+In consectetur augue et sapien feugiat varius. Nam tortor mi, consectetur ac felis non, elementum venenatis augue. Suspendisse ut tellus in est sagittis cursus. Quisque faucibus, neque sit amet semper congue, nibh augue finibus odio, vitae interdum dolor arcu eget arcu. Curabitur dictum risus massa, non tincidunt urna molestie non. Maecenas eu quam purus. Donec vulputate, dui eu accumsan blandit, mauris tortor tristique mi, sed blandit leo quam id quam. Ut venenatis sagittis malesuada. Integer non auctor orci. Duis consectetur massa felis. Fusce euismod est sit amet bibendum finibus. Vestibulum dolor ex, tempor at elit in, iaculis cursus dui. Nunc sed neque ac risus rutrum tempus sit amet at ante. In hac habitasse platea dictumst.
+
+Donec rutrum, velit nec viverra tincidunt, est velit viverra neque, quis auctor leo ex at lectus. Morbi eget purus nisi. Aliquam lacus dui, interdum vitae elit at, venenatis dignissim est. Duis ac mollis lorem. Vivamus a vestibulum quam. Maecenas non metus dolor. Praesent tortor nunc, tristique at nisl molestie, vulputate eleifend diam. Integer ultrices lacus odio, vel imperdiet enim accumsan id. Sed ligula tortor, interdum eu velit eget, pharetra pulvinar magna. Sed non lacus in eros tincidunt sagittis ac vel justo. Donec vitae leo sagittis, accumsan ante sit amet, accumsan odio. Ut volutpat ultricies tortor. Vestibulum tempus purus et est tristique sagittis quis vitae turpis.
+
+Nam iaculis neque lacus, eget euismod turpis blandit eget. In hac habitasse platea dictumst. Phasellus justo neque, scelerisque sit amet risus ut, pretium commodo nisl. Phasellus auctor sapien sed ex bibendum fermentum. Proin maximus odio a ante ornare, a feugiat lorem egestas. Etiam efficitur tortor a ante tincidunt interdum. Nullam non est ac massa congue efficitur sit amet nec eros. Nullam at ipsum vel mauris tincidunt efficitur. Duis pulvinar nisl elit, id auctor risus laoreet ac. Sed nunc mauris, tristique id leo ut, condimentum congue nunc. Sed ultricies, mauris et convallis faucibus, justo ex faucibus est, at lobortis purus justo non arcu. Integer vel facilisis elit, dapibus imperdiet mauris.
+
+Pellentesque non mattis turpis, eget bibendum velit. Fusce sollicitudin ante ac tincidunt rhoncus. Praesent porta scelerisque consequat. Donec eleifend faucibus sollicitudin. Quisque vitae purus eget tortor tempor ultrices. Maecenas mauris diam, semper vitae est non, imperdiet tempor magna. Duis elit lacus, auctor vestibulum enim eget, rhoncus porttitor tortor.
+
+Donec non rhoncus nibh. Cras dapibus justo vitae nunc accumsan, id congue erat egestas. Aenean at ante ante. Duis eleifend imperdiet dREADALL
diff --git a/test/image/mysql.sql b/test/image/mysql.sql
new file mode 100644
index 000000000..51554b98d
--- /dev/null
+++ b/test/image/mysql.sql
@@ -0,0 +1,23 @@
+# Copyright 2018 The gVisor Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHOW databases;
+USE mysql;
+
+CREATE TABLE foo (id int);
+INSERT INTO foo VALUES(1);
+SELECT * FROM foo;
+DROP TABLE foo;
+
+shutdown;
diff --git a/test/image/ruby.rb b/test/image/ruby.rb
new file mode 100644
index 000000000..aced49c6d
--- /dev/null
+++ b/test/image/ruby.rb
@@ -0,0 +1,23 @@
+# Copyright 2018 The gVisor Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require 'sinatra'
+
+set :bind, "0.0.0.0"
+set :port, 8080
+
+get '/' do
+ 'Hello World'
+end
+
diff --git a/test/image/ruby.sh b/test/image/ruby.sh
new file mode 100644
index 000000000..ebe8d5b0e
--- /dev/null
+++ b/test/image/ruby.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# Copyright 2018 The gVisor Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -e
+
+gem install sinatra
+ruby /src/ruby.rb