Skip to content

Commit

Permalink
feature: add pouch cp daemon side
Browse files Browse the repository at this point in the history
Signed-off-by: zhangyue <zy675793960@yeah.net>
  • Loading branch information
zhangyue committed May 30, 2019
1 parent fa2723f commit b1e1d87
Show file tree
Hide file tree
Showing 8 changed files with 474 additions and 1 deletion.
97 changes: 97 additions & 0 deletions apis/server/copy_bridge.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package server

import (
"context"
"encoding/base64"
"encoding/json"
"errors"
"io"
"net/http"

"github.com/alibaba/pouch/pkg/httputils"

"github.com/gorilla/mux"
)

func (s *Server) putContainersArchive(ctx context.Context, rw http.ResponseWriter, req *http.Request) error {
name := mux.Vars(req)["name"]
path := req.FormValue("path")

if name == "" {
return httputils.NewHTTPError(errors.New("name can't be empty"), http.StatusBadRequest)
}
if path == "" {
return httputils.NewHTTPError(errors.New("path can't be empty"), http.StatusBadRequest)
}
noOverwriteDirNonDir := httputils.BoolValue(req, "noOverwriteDirNonDir")
copyUIDGID := httputils.BoolValue(req, "copyUIDGID")

return s.ContainerMgr.ExtractToDir(ctx, name, path, copyUIDGID, noOverwriteDirNonDir, req.Body)
}

func (s *Server) headContainersArchive(ctx context.Context, rw http.ResponseWriter, req *http.Request) error {
name := mux.Vars(req)["name"]
path := req.FormValue("path")

if name == "" {
return httputils.NewHTTPError(errors.New("name can't be empty"), http.StatusBadRequest)
}
if path == "" {
return httputils.NewHTTPError(errors.New("path can't be empty"), http.StatusBadRequest)
}

stat, err := s.ContainerMgr.StatPath(ctx, name, path)
if err != nil {
return err
}

statJSON, err := json.Marshal(stat)
if err != nil {
return err
}

rw.Header().Set(
"X-Docker-Container-Path-Stat",
base64.StdEncoding.EncodeToString(statJSON),
)

rw.WriteHeader(http.StatusOK)
return nil
}

func (s *Server) getContainersArchive(ctx context.Context, rw http.ResponseWriter, req *http.Request) error {
name := mux.Vars(req)["name"]
path := req.FormValue("path")

if name == "" {
return httputils.NewHTTPError(errors.New("name can't be empty"), http.StatusBadRequest)
}
if path == "" {
return httputils.NewHTTPError(errors.New("path can't be empty"), http.StatusBadRequest)
}

tarArchive, stat, err := s.ContainerMgr.ArchivePath(ctx, name, path)
if err != nil {
return err
}
defer tarArchive.Close()

statJSON, err := json.Marshal(stat)
if err != nil {
return err
}
rw.Header().Set(
"X-Docker-Container-Path-Stat",
base64.StdEncoding.EncodeToString(statJSON),
)

rw.Header().Set("Content-Type", "application/x-tar")
_, err = io.Copy(rw, tarArchive)

if err != nil {
return err
}

rw.WriteHeader(http.StatusOK)
return nil
}
5 changes: 5 additions & 0 deletions apis/server/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ func initRoute(s *Server) *mux.Router {
{Method: http.MethodPost, Path: "/attach/{token}", HandlerFunc: s.criAttach},
{Method: http.MethodGet, Path: "/portforward/{token}", HandlerFunc: s.criPortForward},
{Method: http.MethodPost, Path: "/portforward/{token}", HandlerFunc: s.criPortForward},

// copy
{Method: http.MethodPut, Path: "/containers/{name:.*}/archive", HandlerFunc: s.putContainersArchive},
{Method: http.MethodHead, Path: "/containers/{name:.*}/archive", HandlerFunc: s.headContainersArchive},
{Method: http.MethodGet, Path: "/containers/{name:.*}/archive", HandlerFunc: s.getContainersArchive},
}

if s.APIPlugin != nil {
Expand Down
137 changes: 136 additions & 1 deletion apis/swagger.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1510,6 +1510,134 @@ paths:
schema:
$ref: "#/definitions/ContainerCommitOptions"

/containers/{id}/archive:
head:
summary: "Get information about files in a container"
description: "A response header `X-Docker-Container-Path-Stat` is return containing a base64 - encoded JSON object with some filesystem header information about the path."
operationId: "ContainerArchiveInfo"
responses:
200:
description: "no error"
headers:
X-Docker-Container-Path-Stat:
type: "string"
description: "A base64 - encoded JSON object with some filesystem header information about the path"
400:
description: "Bad parameter"
schema:
$ref: "#/responses/400ErrorResponse"
404:
description: "Container or path does not exist"
schema:
$ref: "#/responses/404ErrorResponse"
examples:
application/json:
message: "No such container: c2ada9df5af8"
500:
description: "Server error"
schema:
$ref: "#/responses/500ErrorResponse"
parameters:
- name: "id"
in: "path"
required: true
description: "ID or name of the container"
type: "string"
- name: "path"
in: "query"
required: true
description: "Resource in the container’s filesystem to archive."
type: "string"
tags: ["Copy"]
get:
summary: "Get an archive of a filesystem resource in a container"
description: "Get a tar archive of a resource in the filesystem of container id."
operationId: "ContainerArchive"
produces: ["application/x-tar"]
responses:
200:
description: "no error"
400:
description: "Bad parameter"
schema:
$ref: "#/responses/400ErrorResponse"
404:
description: "Container or path does not exist"
schema:
$ref: "#/responses/404ErrorResponse"
examples:
application/json:
message: "No such container: c2ada9df5af8"
500:
description: "server error"
schema:
$ref: "#/responses/500ErrorResponse"
parameters:
- name: "id"
in: "path"
required: true
description: "ID or name of the container"
type: "string"
- name: "path"
in: "query"
required: true
description: "Resource in the container’s filesystem to archive."
type: "string"
tags: ["Copy"]
put:
summary: "Extract an archive of files or folders to a directory in a container"
description: "Upload a tar archive to be extracted to a path in the filesystem of container id."
operationId: "PutContainerArchive"
consumes: ["application/x-tar", "application/octet-stream"]
responses:
200:
description: "The content was extracted successfully"
400:
description: "Bad parameter"
schema:
$ref: "#/responses/400ErrorResponse"
403:
description: "Permission denied, the volume or container rootfs is marked as read-only."
schema:
$ref: "#/responses/403ErrorResponse"
404:
description: "No such container or path does not exist inside the container"
schema:
$ref: "#/responses/404ErrorResponse"
examples:
application/json:
message: "No such container: c2ada9df5af8"
500:
description: "Server error"
schema:
$ref: "#/responses/500ErrorResponse"
parameters:
- name: "id"
in: "path"
required: true
description: "ID or name of the container"
type: "string"
- name: "path"
in: "query"
required: true
description: "Path to a directory in the container to extract the archive’s contents into. "
type: "string"
- name: "noOverwriteDirNonDir"
in: "query"
description: "If “1”, “true”, or “True” then it will be an error if unpacking the given content would cause an existing directory to be replaced with a non-directory and vice versa."
type: "string"
- name: "copyUIDGID"
in: "query"
description: "If “1”, “true”, or “True” then it will copy UID/GID maps to dest file."
type: "string"
- name: "inputStream"
in: "body"
required: true
description: "The input stream must be a tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz."
schema:
type: "string"
tags: ["Copy"]

definitions:
Error:
type: "object"
Expand Down Expand Up @@ -4378,7 +4506,6 @@ definitions:
type: "string"
description: "ID uniquely identifies an image committed by a container"


parameters:
id:
name: id
Expand All @@ -4394,10 +4521,18 @@ parameters:
type: string

responses:
400ErrorResponse:
description: An unexpected 400 error occurred.
schema:
$ref: "#/definitions/Error"
401ErrorResponse:
description: An unexpected 401 error occurred.
schema:
$ref: "#/definitions/Error"
403ErrorResponse:
description: An unexpected 403 error occurred.
schema:
$ref: "#/definitions/Error"
404ErrorResponse:
description: An unexpected 404 error occurred.
schema:
Expand Down
10 changes: 10 additions & 0 deletions client/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func (e RespError) Code() int {
type Response struct {
StatusCode int
Status string
Header http.Header
Body io.ReadCloser
}

Expand All @@ -50,6 +51,14 @@ func (client *APIClient) post(ctx context.Context, path string, query url.Values
return client.sendRequest(ctx, "POST", path, query, body, headers)
}

func (client *APIClient) head(ctx context.Context, path string, query url.Values, headers map[string][]string) (*Response, error) {
return client.sendRequest(ctx, "HEAD", path, query, nil, headers)
}

func (client *APIClient) putRaw(ctx context.Context, path string, query url.Values, data io.Reader, headers map[string][]string) (*Response, error) {
return client.sendRequest(ctx, "PUT", path, query, data, headers)
}

func (client *APIClient) postRawData(ctx context.Context, path string, query url.Values, data io.Reader, headers map[string][]string) (*Response, error) {
return client.sendRequest(ctx, "POST", path, query, data, headers)
}
Expand Down Expand Up @@ -134,6 +143,7 @@ func (client *APIClient) sendRequest(ctx context.Context, method, path string, q
StatusCode: resp.StatusCode,
Status: resp.Status,
Body: resp.Body,
Header: resp.Header,
}, nil
}

Expand Down
10 changes: 10 additions & 0 deletions daemon/mgr/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import (
// 1. regular container management;
// 2. container exec management;
// 3. container network management.
// 4. container copy management
type ContainerMgr interface {
// 1. the following functions are related to regular container management

Expand Down Expand Up @@ -163,6 +164,15 @@ type ContainerMgr interface {

// Commit commits an image from a container.
Commit(ctx context.Context, name string, options *types.ContainerCommitOptions) (*types.ContainerCommitResp, error)

// StatPath stats the dir info at the specified path in the container.
StatPath(ctx context.Context, name, path string) (stat *ContainerPathStat, err error)

// ArchivePath return an archive and dir info at the specified path in the container.
ArchivePath(ctx context.Context, name, path string) (content io.ReadCloser, stat *ContainerPathStat, err error)

// ExtractToDir extracts the given archive at the specified path in the container.
ExtractToDir(ctx context.Context, name, path string, copyUIDGID, noOverwriteDirNonDir bool, content io.Reader) error
}

// ContainerManager is the default implement of interface ContainerMgr.
Expand Down
Loading

0 comments on commit b1e1d87

Please sign in to comment.