github.com/skanehira/moby@v17.12.1-ce-rc2+incompatible/client/container_copy.go (about)

     1  package client
     2  
     3  import (
     4  	"encoding/base64"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"net/http"
     9  	"net/url"
    10  	"path/filepath"
    11  	"strings"
    12  
    13  	"golang.org/x/net/context"
    14  
    15  	"github.com/docker/docker/api/types"
    16  )
    17  
    18  // ContainerStatPath returns Stat information about a path inside the container filesystem.
    19  func (cli *Client) ContainerStatPath(ctx context.Context, containerID, path string) (types.ContainerPathStat, error) {
    20  	query := url.Values{}
    21  	query.Set("path", filepath.ToSlash(path)) // Normalize the paths used in the API.
    22  
    23  	urlStr := "/containers/" + containerID + "/archive"
    24  	response, err := cli.head(ctx, urlStr, query, nil)
    25  	if err != nil {
    26  		return types.ContainerPathStat{}, err
    27  	}
    28  	defer ensureReaderClosed(response)
    29  	return getContainerPathStatFromHeader(response.header)
    30  }
    31  
    32  // CopyToContainer copies content into the container filesystem.
    33  // Note that `content` must be a Reader for a TAR
    34  func (cli *Client) CopyToContainer(ctx context.Context, container, path string, content io.Reader, options types.CopyToContainerOptions) error {
    35  	query := url.Values{}
    36  	query.Set("path", filepath.ToSlash(path)) // Normalize the paths used in the API.
    37  	// Do not allow for an existing directory to be overwritten by a non-directory and vice versa.
    38  	if !options.AllowOverwriteDirWithFile {
    39  		query.Set("noOverwriteDirNonDir", "true")
    40  	}
    41  
    42  	if options.CopyUIDGID {
    43  		query.Set("copyUIDGID", "true")
    44  	}
    45  
    46  	apiPath := "/containers/" + container + "/archive"
    47  
    48  	response, err := cli.putRaw(ctx, apiPath, query, content, nil)
    49  	if err != nil {
    50  		return err
    51  	}
    52  	defer ensureReaderClosed(response)
    53  
    54  	if response.statusCode != http.StatusOK {
    55  		return fmt.Errorf("unexpected status code from daemon: %d", response.statusCode)
    56  	}
    57  
    58  	return nil
    59  }
    60  
    61  // CopyFromContainer gets the content from the container and returns it as a Reader
    62  // to manipulate it in the host. It's up to the caller to close the reader.
    63  func (cli *Client) CopyFromContainer(ctx context.Context, container, srcPath string) (io.ReadCloser, types.ContainerPathStat, error) {
    64  	query := make(url.Values, 1)
    65  	query.Set("path", filepath.ToSlash(srcPath)) // Normalize the paths used in the API.
    66  
    67  	apiPath := "/containers/" + container + "/archive"
    68  	response, err := cli.get(ctx, apiPath, query, nil)
    69  	if err != nil {
    70  		return nil, types.ContainerPathStat{}, err
    71  	}
    72  
    73  	if response.statusCode != http.StatusOK {
    74  		return nil, types.ContainerPathStat{}, fmt.Errorf("unexpected status code from daemon: %d", response.statusCode)
    75  	}
    76  
    77  	// In order to get the copy behavior right, we need to know information
    78  	// about both the source and the destination. The response headers include
    79  	// stat info about the source that we can use in deciding exactly how to
    80  	// copy it locally. Along with the stat info about the local destination,
    81  	// we have everything we need to handle the multiple possibilities there
    82  	// can be when copying a file/dir from one location to another file/dir.
    83  	stat, err := getContainerPathStatFromHeader(response.header)
    84  	if err != nil {
    85  		return nil, stat, fmt.Errorf("unable to get resource stat from response: %s", err)
    86  	}
    87  	return response.body, stat, err
    88  }
    89  
    90  func getContainerPathStatFromHeader(header http.Header) (types.ContainerPathStat, error) {
    91  	var stat types.ContainerPathStat
    92  
    93  	encodedStat := header.Get("X-Docker-Container-Path-Stat")
    94  	statDecoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(encodedStat))
    95  
    96  	err := json.NewDecoder(statDecoder).Decode(&stat)
    97  	if err != nil {
    98  		err = fmt.Errorf("unable to decode container path stat header: %s", err)
    99  	}
   100  
   101  	return stat, err
   102  }