Browse code

api/server: StatPath, ArchivePath, ExtractToDir

Adds http handlers for new API endpoints:

GET ContainersArchivePath
Return a Tar Archive of the contents at the specified location in a
container. Deprecates POST ContainersCopy. Use a HEAD request to stat
the resource.

PUT ContainersExtractToDir
Extract the Tar Archive from the request body to the directory at the
specified location inside a container.

Docker-DCO-1.1-Signed-off-by: Josh Hawn <josh.hawn@docker.com> (github: jlhawn)

Josh Hawn authored on 2015/05/14 07:01:51
Showing 2 changed files
... ...
@@ -1309,6 +1309,7 @@ func (s *Server) postBuild(version version.Version, w http.ResponseWriter, r *ht
1309 1309
 	return nil
1310 1310
 }
1311 1311
 
1312
+// postContainersCopy is deprecated in favor of getContainersArchivePath.
1312 1313
 func (s *Server) postContainersCopy(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
1313 1314
 	if vars == nil {
1314 1315
 		return fmt.Errorf("Missing parameter")
... ...
@@ -1348,6 +1349,104 @@ func (s *Server) postContainersCopy(version version.Version, w http.ResponseWrit
1348 1348
 	return nil
1349 1349
 }
1350 1350
 
1351
+// // Encode the stat to JSON, base64 encode, and place in a header.
1352
+func setContainerPathStatHeader(stat *types.ContainerPathStat, header http.Header) error {
1353
+	statJSON, err := json.Marshal(stat)
1354
+	if err != nil {
1355
+		return err
1356
+	}
1357
+
1358
+	header.Set(
1359
+		"X-Docker-Container-Path-Stat",
1360
+		base64.StdEncoding.EncodeToString(statJSON),
1361
+	)
1362
+
1363
+	return nil
1364
+}
1365
+
1366
+func (s *Server) headContainersArchive(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
1367
+	if vars == nil {
1368
+		return fmt.Errorf("Missing parameter")
1369
+	}
1370
+	if err := parseForm(r); err != nil {
1371
+		return err
1372
+	}
1373
+
1374
+	name := vars["name"]
1375
+	path := r.Form.Get("path")
1376
+
1377
+	switch {
1378
+	case name == "":
1379
+		return fmt.Errorf("bad parameter: 'name' cannot be empty")
1380
+	case path == "":
1381
+		return fmt.Errorf("bad parameter: 'path' cannot be empty")
1382
+	}
1383
+
1384
+	stat, err := s.daemon.ContainerStatPath(name, path)
1385
+	if err != nil {
1386
+		return err
1387
+	}
1388
+
1389
+	return setContainerPathStatHeader(stat, w.Header())
1390
+}
1391
+
1392
+func (s *Server) getContainersArchive(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
1393
+	if vars == nil {
1394
+		return fmt.Errorf("Missing parameter")
1395
+	}
1396
+	if err := parseForm(r); err != nil {
1397
+		return err
1398
+	}
1399
+
1400
+	name := vars["name"]
1401
+	path := r.Form.Get("path")
1402
+
1403
+	switch {
1404
+	case name == "":
1405
+		return fmt.Errorf("bad parameter: 'name' cannot be empty")
1406
+	case path == "":
1407
+		return fmt.Errorf("bad parameter: 'path' cannot be empty")
1408
+	}
1409
+
1410
+	tarArchive, stat, err := s.daemon.ContainerArchivePath(name, path)
1411
+	if err != nil {
1412
+		return err
1413
+	}
1414
+	defer tarArchive.Close()
1415
+
1416
+	if err := setContainerPathStatHeader(stat, w.Header()); err != nil {
1417
+		return err
1418
+	}
1419
+
1420
+	w.Header().Set("Content-Type", "application/x-tar")
1421
+	_, err = io.Copy(w, tarArchive)
1422
+
1423
+	return err
1424
+}
1425
+
1426
+func (s *Server) putContainersArchive(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
1427
+	if vars == nil {
1428
+		return fmt.Errorf("Missing parameter")
1429
+	}
1430
+	if err := parseForm(r); err != nil {
1431
+		return err
1432
+	}
1433
+
1434
+	name := vars["name"]
1435
+	path := r.Form.Get("path")
1436
+
1437
+	noOverwriteDirNonDir := boolValue(r, "noOverwriteDirNonDir")
1438
+
1439
+	switch {
1440
+	case name == "":
1441
+		return fmt.Errorf("bad parameter: 'name' cannot be empty")
1442
+	case path == "":
1443
+		return fmt.Errorf("bad parameter: 'path' cannot be empty")
1444
+	}
1445
+
1446
+	return s.daemon.ContainerExtractToDir(name, path, noOverwriteDirNonDir, r.Body)
1447
+}
1448
+
1351 1449
 func (s *Server) postContainerExecCreate(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
1352 1450
 	if err := parseForm(r); err != nil {
1353 1451
 		return err
... ...
@@ -1536,6 +1635,9 @@ func createRouter(s *Server) *mux.Router {
1536 1536
 		ProfilerSetup(r, "/debug/")
1537 1537
 	}
1538 1538
 	m := map[string]map[string]HttpApiFunc{
1539
+		"HEAD": {
1540
+			"/containers/{name:.*}/archive": s.headContainersArchive,
1541
+		},
1539 1542
 		"GET": {
1540 1543
 			"/_ping":                          s.ping,
1541 1544
 			"/events":                         s.getEvents,
... ...
@@ -1557,6 +1659,7 @@ func createRouter(s *Server) *mux.Router {
1557 1557
 			"/containers/{name:.*}/stats":     s.getContainersStats,
1558 1558
 			"/containers/{name:.*}/attach/ws": s.wsContainersAttach,
1559 1559
 			"/exec/{id:.*}/json":              s.getExecByID,
1560
+			"/containers/{name:.*}/archive":   s.getContainersArchive,
1560 1561
 		},
1561 1562
 		"POST": {
1562 1563
 			"/auth":                         s.postAuth,
... ...
@@ -1582,6 +1685,9 @@ func createRouter(s *Server) *mux.Router {
1582 1582
 			"/exec/{name:.*}/resize":        s.postContainerExecResize,
1583 1583
 			"/containers/{name:.*}/rename":  s.postContainerRename,
1584 1584
 		},
1585
+		"PUT": {
1586
+			"/containers/{name:.*}/archive": s.putContainersArchive,
1587
+		},
1585 1588
 		"DELETE": {
1586 1589
 			"/containers/{name:.*}": s.deleteContainers,
1587 1590
 			"/images/{name:.*}":     s.deleteImages,
... ...
@@ -128,8 +128,10 @@ type CopyConfig struct {
128 128
 	Resource string
129 129
 }
130 130
 
131
-// ContainerPathStat is used to encode the response from
132
-// 	GET /containers/{name:.*}/stat-path
131
+// ContainerPathStat is used to encode the header from
132
+// 	GET /containers/{name:.*}/archive
133
+// "name" is the file or directory name.
134
+// "path" is the absolute path to the resource in the container.
133 135
 type ContainerPathStat struct {
134 136
 	Name  string      `json:"name"`
135 137
 	Path  string      `json:"path"`