Пример #1
0
func (b *Builder) checkCopyCache(srcPath string) bool {
	srcPath = fmt.Sprintf("%s%c%s", b.contextDirectory, filepath.Separator, srcPath)
	srcArchive, err := archive.TarResource(srcPath)
	if err != nil {
		log.Debugf("unable to archive source: %s", err)
		return false
	}
	defer srcArchive.Close()

	digester, err := tarsum.NewDigest(tarsum.Version1)
	if err != nil {
		log.Debugf("unable to get new tarsum digester: %s", err)
		return false
	}

	if _, err := io.Copy(digester, srcArchive); err != nil {
		log.Debugf("unable to digest source archive: %s", err)
		return false
	}

	copyDigest := fmt.Sprintf("%x", digester.Sum(nil))
	b.uncommittedCommands = append(b.uncommittedCommands, fmt.Sprintf("COPY digest: %s", copyDigest))

	return b.probeCache()
}
Пример #2
0
func (b *Builder) copyToContainer(srcPath, dstContainer, dstPath string) (err error) {
	// In order to get the copy behavior right, we need to know information
	// about both the source and destination. The API is a simple tar
	// archive/extract API but we can use the stat info header about the
	// destination to be more informed about exactly what the destination is.

	// Prepare destination copy info by stat-ing the container path.
	dstInfo := archive.CopyInfo{Path: dstPath}
	dstStat, err := b.statContainerPath(dstContainer, dstPath)
	if err == nil {
		dstInfo.Exists, dstInfo.IsDir = true, dstStat.Mode.IsDir()
	}
	// Ignore any other error and assume that the parent directory of the
	// destination path exists, in which case the copy may still succeed. If
	// there is any type of conflict (e.g., non-directory overwriting an
	// existing directory or vice versia) the extraction will fail. If the
	// destination simply did not exist, but the parent directory does, the
	// extraction will still succeed.

	srcPath = fmt.Sprintf("%s%c%s", b.contextDirectory, filepath.Separator, srcPath)

	srcArchive, err := archive.TarResource(srcPath)
	if err != nil {
		return err
	}
	defer srcArchive.Close()

	// With the stat info about the local source as well as the
	// destination, we have enough information to know whether we need to
	// alter the archive that we upload so that when the server extracts
	// it to the specified directory in the container we get the disired
	// copy behavior.

	// Prepare source copy info.
	srcInfo, err := archive.CopyInfoStatPath(srcPath, true)
	if err != nil {
		return err
	}

	// See comments in the implementation of `archive.PrepareArchiveCopy`
	// for exactly what goes into deciding how and whether the source
	// archive needs to be altered for the correct copy behavior when it is
	// extracted. This function also infers from the source and destination
	// info which directory to extract to, which may be the parent of the
	// destination that the user specified.
	dstDir, preparedArchive, err := archive.PrepareArchiveCopy(srcArchive, srcInfo, dstInfo)
	if err != nil {
		return err
	}
	defer preparedArchive.Close()

	dstPath = dstDir

	query := make(url.Values, 1)
	query.Set("path", filepath.ToSlash(dstPath)) // Normalize the paths used in the API.
	// Do not allow for an existing directory to be overwritten by a non-directory and vice versa.
	query.Set("noOverwriteDirNonDir", "true")

	urlPath := fmt.Sprintf("/containers/%s/archive?%s", dstContainer, query.Encode())
	req, err := http.NewRequest("PUT", b.client.URL.String()+urlPath, preparedArchive)
	if err != nil {
		return fmt.Errorf("unable to prepare request: %s", err)
	}

	req.Header.Set("Content-Type", "application/x-tar")

	resp, err := b.client.HTTPClient.Do(req)
	if err != nil {
		return fmt.Errorf("unable to make request: %s", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		// Read the body if possible.
		buf := bytes.NewBuffer(make([]byte, 0, resp.ContentLength))
		io.Copy(buf, resp.Body) // It's okay if this fails.

		return fmt.Errorf("request failed with status code %d: %s", resp.StatusCode, buf.String())
	}

	return nil
}