github.com/vvnotw/moby@v1.13.1/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 := fmt.Sprintf("/containers/%s/archive", containerID)
    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  func (cli *Client) CopyToContainer(ctx context.Context, container, path string, content io.Reader, options types.CopyToContainerOptions) error {
    34  	query := url.Values{}
    35  	query.Set("path", filepath.ToSlash(path)) // Normalize the paths used in the API.
    36  	// Do not allow for an existing directory to be overwritten by a non-directory and vice versa.
    37  	if !options.AllowOverwriteDirWithFile {
    38  		query.Set("noOverwriteDirNonDir", "true")
    39  	}
    40  
    41  	apiPath := fmt.Sprintf("/containers/%s/archive", container)
    42  
    43  	response, err := cli.putRaw(ctx, apiPath, query, content, nil)
    44  	if err != nil {
    45  		return err
    46  	}
    47  	defer ensureReaderClosed(response)
    48  
    49  	if response.statusCode != http.StatusOK {
    50  		return fmt.Errorf("unexpected status code from daemon: %d", response.statusCode)
    51  	}
    52  
    53  	return nil
    54  }
    55  
    56  // CopyFromContainer gets the content from the container and returns it as a Reader
    57  // to manipulate it in the host. It's up to the caller to close the reader.
    58  func (cli *Client) CopyFromContainer(ctx context.Context, container, srcPath string) (io.ReadCloser, types.ContainerPathStat, error) {
    59  	query := make(url.Values, 1)
    60  	query.Set("path", filepath.ToSlash(srcPath)) // Normalize the paths used in the API.
    61  
    62  	apiPath := fmt.Sprintf("/containers/%s/archive", container)
    63  	response, err := cli.get(ctx, apiPath, query, nil)
    64  	if err != nil {
    65  		return nil, types.ContainerPathStat{}, err
    66  	}
    67  
    68  	if response.statusCode != http.StatusOK {
    69  		return nil, types.ContainerPathStat{}, fmt.Errorf("unexpected status code from daemon: %d", response.statusCode)
    70  	}
    71  
    72  	// In order to get the copy behavior right, we need to know information
    73  	// about both the source and the destination. The response headers include
    74  	// stat info about the source that we can use in deciding exactly how to
    75  	// copy it locally. Along with the stat info about the local destination,
    76  	// we have everything we need to handle the multiple possibilities there
    77  	// can be when copying a file/dir from one location to another file/dir.
    78  	stat, err := getContainerPathStatFromHeader(response.header)
    79  	if err != nil {
    80  		return nil, stat, fmt.Errorf("unable to get resource stat from response: %s", err)
    81  	}
    82  	return response.body, stat, err
    83  }
    84  
    85  func getContainerPathStatFromHeader(header http.Header) (types.ContainerPathStat, error) {
    86  	var stat types.ContainerPathStat
    87  
    88  	encodedStat := header.Get("X-Docker-Container-Path-Stat")
    89  	statDecoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(encodedStat))
    90  
    91  	err := json.NewDecoder(statDecoder).Decode(&stat)
    92  	if err != nil {
    93  		err = fmt.Errorf("unable to decode container path stat header: %s", err)
    94  	}
    95  
    96  	return stat, err
    97  }