github.com/eikeon/docker@v1.5.0-rc4/integration-cli/docker_api_containers_test.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "io" 7 "io/ioutil" 8 "os" 9 "os/exec" 10 "strings" 11 "testing" 12 "time" 13 14 "github.com/docker/docker/api/stats" 15 "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar" 16 ) 17 18 func TestContainerApiGetAll(t *testing.T) { 19 startCount, err := getContainerCount() 20 if err != nil { 21 t.Fatalf("Cannot query container count: %v", err) 22 } 23 24 name := "getall" 25 runCmd := exec.Command(dockerBinary, "run", "--name", name, "busybox", "true") 26 out, _, err := runCommandWithOutput(runCmd) 27 if err != nil { 28 t.Fatalf("Error on container creation: %v, output: %q", err, out) 29 } 30 31 body, err := sockRequest("GET", "/containers/json?all=1", nil) 32 if err != nil { 33 t.Fatalf("GET all containers sockRequest failed: %v", err) 34 } 35 36 var inspectJSON []struct { 37 Names []string 38 } 39 if err = json.Unmarshal(body, &inspectJSON); err != nil { 40 t.Fatalf("unable to unmarshal response body: %v", err) 41 } 42 43 if len(inspectJSON) != startCount+1 { 44 t.Fatalf("Expected %d container(s), %d found (started with: %d)", startCount+1, len(inspectJSON), startCount) 45 } 46 47 if actual := inspectJSON[0].Names[0]; actual != "/"+name { 48 t.Fatalf("Container Name mismatch. Expected: %q, received: %q\n", "/"+name, actual) 49 } 50 51 deleteAllContainers() 52 53 logDone("container REST API - check GET json/all=1") 54 } 55 56 func TestContainerApiGetExport(t *testing.T) { 57 name := "exportcontainer" 58 runCmd := exec.Command(dockerBinary, "run", "--name", name, "busybox", "touch", "/test") 59 out, _, err := runCommandWithOutput(runCmd) 60 if err != nil { 61 t.Fatalf("Error on container creation: %v, output: %q", err, out) 62 } 63 64 body, err := sockRequest("GET", "/containers/"+name+"/export", nil) 65 if err != nil { 66 t.Fatalf("GET containers/export sockRequest failed: %v", err) 67 } 68 69 found := false 70 for tarReader := tar.NewReader(bytes.NewReader(body)); ; { 71 h, err := tarReader.Next() 72 if err != nil { 73 if err == io.EOF { 74 break 75 } 76 t.Fatal(err) 77 } 78 if h.Name == "test" { 79 found = true 80 break 81 } 82 } 83 84 if !found { 85 t.Fatalf("The created test file has not been found in the exported image") 86 } 87 deleteAllContainers() 88 89 logDone("container REST API - check GET containers/export") 90 } 91 92 func TestContainerApiGetChanges(t *testing.T) { 93 name := "changescontainer" 94 runCmd := exec.Command(dockerBinary, "run", "--name", name, "busybox", "rm", "/etc/passwd") 95 out, _, err := runCommandWithOutput(runCmd) 96 if err != nil { 97 t.Fatalf("Error on container creation: %v, output: %q", err, out) 98 } 99 100 body, err := sockRequest("GET", "/containers/"+name+"/changes", nil) 101 if err != nil { 102 t.Fatalf("GET containers/changes sockRequest failed: %v", err) 103 } 104 105 changes := []struct { 106 Kind int 107 Path string 108 }{} 109 if err = json.Unmarshal(body, &changes); err != nil { 110 t.Fatalf("unable to unmarshal response body: %v", err) 111 } 112 113 // Check the changelog for removal of /etc/passwd 114 success := false 115 for _, elem := range changes { 116 if elem.Path == "/etc/passwd" && elem.Kind == 2 { 117 success = true 118 } 119 } 120 if !success { 121 t.Fatalf("/etc/passwd has been removed but is not present in the diff") 122 } 123 124 deleteAllContainers() 125 126 logDone("container REST API - check GET containers/changes") 127 } 128 129 func TestContainerApiStartVolumeBinds(t *testing.T) { 130 defer deleteAllContainers() 131 name := "testing" 132 config := map[string]interface{}{ 133 "Image": "busybox", 134 "Volumes": map[string]struct{}{"/tmp": {}}, 135 } 136 137 if _, err := sockRequest("POST", "/containers/create?name="+name, config); err != nil && !strings.Contains(err.Error(), "201 Created") { 138 t.Fatal(err) 139 } 140 141 bindPath, err := ioutil.TempDir(os.TempDir(), "test") 142 if err != nil { 143 t.Fatal(err) 144 } 145 146 config = map[string]interface{}{ 147 "Binds": []string{bindPath + ":/tmp"}, 148 } 149 if _, err := sockRequest("POST", "/containers/"+name+"/start", config); err != nil && !strings.Contains(err.Error(), "204 No Content") { 150 t.Fatal(err) 151 } 152 153 pth, err := inspectFieldMap(name, "Volumes", "/tmp") 154 if err != nil { 155 t.Fatal(err) 156 } 157 158 if pth != bindPath { 159 t.Fatalf("expected volume host path to be %s, got %s", bindPath, pth) 160 } 161 162 logDone("container REST API - check volume binds on start") 163 } 164 165 func TestContainerApiStartVolumesFrom(t *testing.T) { 166 defer deleteAllContainers() 167 volName := "voltst" 168 volPath := "/tmp" 169 170 if out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-d", "--name", volName, "-v", volPath, "busybox")); err != nil { 171 t.Fatal(out, err) 172 } 173 174 name := "testing" 175 config := map[string]interface{}{ 176 "Image": "busybox", 177 "Volumes": map[string]struct{}{volPath: {}}, 178 } 179 180 if _, err := sockRequest("POST", "/containers/create?name="+name, config); err != nil && !strings.Contains(err.Error(), "201 Created") { 181 t.Fatal(err) 182 } 183 184 config = map[string]interface{}{ 185 "VolumesFrom": []string{volName}, 186 } 187 if _, err := sockRequest("POST", "/containers/"+name+"/start", config); err != nil && !strings.Contains(err.Error(), "204 No Content") { 188 t.Fatal(err) 189 } 190 191 pth, err := inspectFieldMap(name, "Volumes", volPath) 192 if err != nil { 193 t.Fatal(err) 194 } 195 pth2, err := inspectFieldMap(volName, "Volumes", volPath) 196 if err != nil { 197 t.Fatal(err) 198 } 199 200 if pth != pth2 { 201 t.Fatalf("expected volume host path to be %s, got %s", pth, pth2) 202 } 203 204 logDone("container REST API - check VolumesFrom on start") 205 } 206 207 // Ensure that volumes-from has priority over binds/anything else 208 // This is pretty much the same as TestRunApplyVolumesFromBeforeVolumes, except with passing the VolumesFrom and the bind on start 209 func TestVolumesFromHasPriority(t *testing.T) { 210 defer deleteAllContainers() 211 volName := "voltst" 212 volPath := "/tmp" 213 214 if out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-d", "--name", volName, "-v", volPath, "busybox")); err != nil { 215 t.Fatal(out, err) 216 } 217 218 name := "testing" 219 config := map[string]interface{}{ 220 "Image": "busybox", 221 "Volumes": map[string]struct{}{volPath: {}}, 222 } 223 224 if _, err := sockRequest("POST", "/containers/create?name="+name, config); err != nil && !strings.Contains(err.Error(), "201 Created") { 225 t.Fatal(err) 226 } 227 228 bindPath, err := ioutil.TempDir(os.TempDir(), "test") 229 if err != nil { 230 t.Fatal(err) 231 } 232 233 config = map[string]interface{}{ 234 "VolumesFrom": []string{volName}, 235 "Binds": []string{bindPath + ":/tmp"}, 236 } 237 if _, err := sockRequest("POST", "/containers/"+name+"/start", config); err != nil && !strings.Contains(err.Error(), "204 No Content") { 238 t.Fatal(err) 239 } 240 241 pth, err := inspectFieldMap(name, "Volumes", volPath) 242 if err != nil { 243 t.Fatal(err) 244 } 245 pth2, err := inspectFieldMap(volName, "Volumes", volPath) 246 if err != nil { 247 t.Fatal(err) 248 } 249 250 if pth != pth2 { 251 t.Fatalf("expected volume host path to be %s, got %s", pth, pth2) 252 } 253 254 logDone("container REST API - check VolumesFrom has priority") 255 } 256 257 func TestGetContainerStats(t *testing.T) { 258 defer deleteAllContainers() 259 var ( 260 name = "statscontainer" 261 runCmd = exec.Command(dockerBinary, "run", "-d", "--name", name, "busybox", "top") 262 ) 263 out, _, err := runCommandWithOutput(runCmd) 264 if err != nil { 265 t.Fatalf("Error on container creation: %v, output: %q", err, out) 266 } 267 type b struct { 268 body []byte 269 err error 270 } 271 bc := make(chan b, 1) 272 go func() { 273 body, err := sockRequest("GET", "/containers/"+name+"/stats", nil) 274 bc <- b{body, err} 275 }() 276 277 // allow some time to stream the stats from the container 278 time.Sleep(4 * time.Second) 279 if _, err := runCommand(exec.Command(dockerBinary, "rm", "-f", name)); err != nil { 280 t.Fatal(err) 281 } 282 283 // collect the results from the stats stream or timeout and fail 284 // if the stream was not disconnected. 285 select { 286 case <-time.After(2 * time.Second): 287 t.Fatal("stream was not closed after container was removed") 288 case sr := <-bc: 289 if sr.err != nil { 290 t.Fatal(err) 291 } 292 293 dec := json.NewDecoder(bytes.NewBuffer(sr.body)) 294 var s *stats.Stats 295 // decode only one object from the stream 296 if err := dec.Decode(&s); err != nil { 297 t.Fatal(err) 298 } 299 } 300 logDone("container REST API - check GET containers/stats") 301 } 302 303 func TestBuildApiDockerfilePath(t *testing.T) { 304 // Test to make sure we stop people from trying to leave the 305 // build context when specifying the path to the dockerfile 306 buffer := new(bytes.Buffer) 307 tw := tar.NewWriter(buffer) 308 defer tw.Close() 309 310 dockerfile := []byte("FROM busybox") 311 if err := tw.WriteHeader(&tar.Header{ 312 Name: "Dockerfile", 313 Size: int64(len(dockerfile)), 314 }); err != nil { 315 t.Fatalf("failed to write tar file header: %v", err) 316 } 317 if _, err := tw.Write(dockerfile); err != nil { 318 t.Fatalf("failed to write tar file content: %v", err) 319 } 320 if err := tw.Close(); err != nil { 321 t.Fatalf("failed to close tar archive: %v", err) 322 } 323 324 out, err := sockRequestRaw("POST", "/build?dockerfile=../Dockerfile", buffer, "application/x-tar") 325 if err == nil { 326 t.Fatalf("Build was supposed to fail: %s", out) 327 } 328 329 if !strings.Contains(string(out), "must be within the build context") { 330 t.Fatalf("Didn't complain about leaving build context: %s", out) 331 } 332 333 logDone("container REST API - check build w/bad Dockerfile path") 334 } 335 336 func TestBuildApiDockerfileSymlink(t *testing.T) { 337 // Test to make sure we stop people from trying to leave the 338 // build context when specifying a symlink as the path to the dockerfile 339 buffer := new(bytes.Buffer) 340 tw := tar.NewWriter(buffer) 341 defer tw.Close() 342 343 if err := tw.WriteHeader(&tar.Header{ 344 Name: "Dockerfile", 345 Typeflag: tar.TypeSymlink, 346 Linkname: "/etc/passwd", 347 }); err != nil { 348 t.Fatalf("failed to write tar file header: %v", err) 349 } 350 if err := tw.Close(); err != nil { 351 t.Fatalf("failed to close tar archive: %v", err) 352 } 353 354 out, err := sockRequestRaw("POST", "/build", buffer, "application/x-tar") 355 if err == nil { 356 t.Fatalf("Build was supposed to fail: %s", out) 357 } 358 359 // The reason the error is "Cannot locate specified Dockerfile" is because 360 // in the builder, the symlink is resolved within the context, therefore 361 // Dockerfile -> /etc/passwd becomes etc/passwd from the context which is 362 // a nonexistent file. 363 if !strings.Contains(string(out), "Cannot locate specified Dockerfile: Dockerfile") { 364 t.Fatalf("Didn't complain about leaving build context: %s", out) 365 } 366 367 logDone("container REST API - check build w/bad Dockerfile symlink path") 368 }