Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: pouch copy in client and daemon side #2182

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
62 changes: 62 additions & 0 deletions apis/server/container_bridge.go
Expand Up @@ -2,6 +2,7 @@ package server

import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"io"
Expand Down Expand Up @@ -517,6 +518,67 @@ func (s *Server) waitContainer(ctx context.Context, rw http.ResponseWriter, req
return EncodeResponse(rw, http.StatusOK, &waitStatus)
}

func (s *Server) headContainersArchive(ctx context.Context, rw http.ResponseWriter, req *http.Request) error {
name := mux.Vars(req)["name"]
path := req.Header.Get("SRCPATH")
stat, err := s.ContainerMgr.ContainerStatPath(ctx, name, path)
if err != nil {
return err
}

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

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

rw.WriteHeader(http.StatusCreated)
return nil
}

func (s *Server) putContainersArchive(ctx context.Context, rw http.ResponseWriter, req *http.Request) error {

noOverwriteDirNonDir := httputils.BoolValue(req, "noOverwriteDirNonDir")
path := req.Header.Get("SRCPATH")
name := mux.Vars(req)["name"]

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

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

tarArchive, stat, err := s.ContainerMgr.ContainerArchivePath(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-Pouch-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.StatusCreated)
return nil
}

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

Expand Down
3 changes: 3 additions & 0 deletions apis/server/router.go
Expand Up @@ -99,6 +99,9 @@ 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},
{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
103 changes: 103 additions & 0 deletions apis/swagger.yml
Expand Up @@ -951,6 +951,92 @@ paths:
$ref: "#/responses/500ErrorResponse"
tags: ["Container"]

/containers/{id}/archive:
head:
summary: "Get information about files in a container"
description: "A response header `X-Pouch-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-Pouch-Container-Path-Stat:
type: "string"
description: "A base64 - encoded JSON object with some filesystem header information about the path"
404:
$ref: "#/responses/404ErrorResponse"
500:
$ref: "#/responses/500ErrorResponse"
parameters:
- name: "id"
in: "path"
required: true
description: "ID or name of the container"
type: "string"
- name: "SRCPATH"
ZYecho marked this conversation as resolved.
Show resolved Hide resolved
in: "header"
required: true
description: "Resource in the container’s filesystem to archive."
type: "string"
tags: ["Container"]
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"
404:
$ref: "#/responses/404ErrorResponse"
500:
$ref: "#/responses/500ErrorResponse"
parameters:
- name: "id"
in: "path"
required: true
description: "ID or name of the container"
type: "string"
- name: "SRCPATH"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about using path here? it seems that there is no destPath in the description. in order to make it more clearly, I think path is good choice. BTW, there is no need to use all upper case for parameters .

in: "header"
required: true
description: "Resource in the container’s filesystem to archive."
type: "string"
tags: ["Container"]
put:
summary: "Extract an archive of files or folders to a directory in a container"
operationId: "PutContainerArchive"
consumes: ["application/x-tar", "application/octet-stream"]
responses:
200:
description: "The content was extracted successfully"
404:
$ref: "#/responses/404ErrorResponse"
500:
$ref: "#/responses/500ErrorResponse"
parameters:
- name: "id"
in: "path"
required: true
description: "ID or name of the container"
type: "string"
- name: "SRCPATH"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about using path here?

in: "header"
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: "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: ["Container"]

/exec/{id}/start:
post:
summary: "Start an exec instance"
Expand Down Expand Up @@ -4317,6 +4403,23 @@ definitions:
type: "string"
description: "ID uniquely identifies an image committed by a container"

ContainerPathStat:
description: "ContainerPathStat is used to describe the stat of file"
type: "object"
properties:
name:
type: "string"
size:
type: "string"
mode:
type: "integer"
format: "uint32"
mtime:
description: "modification time."
type: "string"
format: date-time
linkTarget:
type: "string"

parameters:
id:
Expand Down
39 changes: 39 additions & 0 deletions apis/types/container_copy.go
@@ -0,0 +1,39 @@
package types
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not manually edit this file, but generate it by swagger, you could see the reference: https://github.com/alibaba/pouch/blob/master/apis/README.md

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok


import (
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
)

// CopyConfig contains request body of Remote API:
// POST "/containers/"+containerID+"/copy"
type CopyConfig struct {
Resource string
}

// ContainerPathStat is used to encode the header from
// GET "/containers/{name:.*}/archive"
// "Name" is the file or directory name.
type ContainerPathStat struct {
Name string `json:"name"`
Size int64 `json:"size"`
Mode uint32 `json:"mode"`
Mtime strfmt.DateTime `json:"mtime"`
LinkTarget string `json:"linkTarget"`
}

// CopyToContainerOptions holds information
// about files to copy into a container
type CopyToContainerOptions struct {
AllowOverwriteDirWithFile bool
}

// Validate validates ContainerPathStat
func (m *ContainerPathStat) Validate(formats strfmt.Registry) error {
var res []error

if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}