// StoreImage stores file system layer data for the given image to the // image's registered storage driver. Image metadata is stored in a file // at the specified root directory. This function also computes the TarSum // of `layerData` (currently using tarsum.dev). func StoreImage(img *Image, layerData archive.ArchiveReader, root string) error { // Store the layer var ( size int64 err error driver = img.graph.Driver() layerTarSum tarsum.TarSum ) // If layerData is not nil, unpack it into the new layer if layerData != nil { // If the image doesn't have a checksum, we should add it. The layer // checksums are verified when they are pulled from a remote, but when // a container is committed it should be added here. if img.Checksum == "" { layerDataDecompressed, err := archive.DecompressStream(layerData) if err != nil { return err } defer layerDataDecompressed.Close() if layerTarSum, err = tarsum.NewTarSum(layerDataDecompressed, true, tarsum.VersionDev); err != nil { return err } if size, err = driver.ApplyDiff(img.ID, img.Parent, layerTarSum); err != nil { return err } img.Checksum = layerTarSum.Sum(nil) } else if size, err = driver.ApplyDiff(img.ID, img.Parent, layerData); err != nil { return err } } img.Size = size if err := img.SaveSize(root); err != nil { return err } f, err := os.OpenFile(jsonPath(root), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0600)) if err != nil { return err } defer f.Close() return json.NewEncoder(f).Encode(img) }
// StoreImage stores file system layer data for the given image to the // image's registered storage driver. Image metadata is stored in a file // at the specified root directory. This function also computes the TarSum // of `layerData` (currently using tarsum.dev). func StoreImage(img *Image, layerData archive.ArchiveReader, root string) error { // Store the layer var ( size int64 err error driver = img.graph.Driver() layerTarSum tarsum.TarSum ) // If layerData is not nil, unpack it into the new layer if layerData != nil { layerDataDecompressed, err := archive.DecompressStream(layerData) if err != nil { return err } defer layerDataDecompressed.Close() if layerTarSum, err = tarsum.NewTarSum(layerDataDecompressed, true, tarsum.VersionDev); err != nil { return err } if size, err = driver.ApplyDiff(img.ID, img.Parent, layerTarSum); err != nil { return err } checksum := layerTarSum.Sum(nil) if img.Checksum != "" && img.Checksum != checksum { log.Warnf("image layer checksum mismatch: computed %q, expected %q", checksum, img.Checksum) } img.Checksum = checksum } img.Size = size if err := img.SaveSize(root); err != nil { return err } f, err := os.OpenFile(jsonPath(root), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0600)) if err != nil { return err } defer f.Close() return json.NewEncoder(f).Encode(img) }
func copyFiles(b *Build, args []string, cmdName string) (s State, err error) { s = b.state if len(args) < 2 { return s, fmt.Errorf("Invalid %s format - at least two arguments required", cmdName) } var ( tarSum tarsum.TarSum src = args[0 : len(args)-1] dest = filepath.FromSlash(args[len(args)-1]) // last one is always the dest u *upload excludes = s.NoCache.Dockerignore ) // If destination is not a directory (no trailing slash) hasTrailingSlash := strings.HasSuffix(dest, string(os.PathSeparator)) if !hasTrailingSlash && len(src) > 1 { return s, fmt.Errorf("When using %s with more than one source file, the destination must be a directory and end with a /", cmdName) } if !filepath.IsAbs(dest) { dest = filepath.Join(s.Config.WorkingDir, dest) // Add the trailing slash back if we had it before if hasTrailingSlash { dest += string(os.PathSeparator) } } if u, err = makeTarStream(b.cfg.ContextDir, dest, cmdName, src, excludes, b.urlFetcher); err != nil { return s, err } // skip COPY if no files matched if len(u.files) == 0 { log.Infof("| No files matched") return s, nil } log.Infof("| Calculating tarsum for %d files (%s total)", len(u.files), units.HumanSize(float64(u.size))) if tarSum, err = tarsum.NewTarSum(u.tar, true, tarsum.Version1); err != nil { return s, err } if _, err = io.Copy(ioutil.Discard, tarSum); err != nil { return s, err } u.tar.Close() // TODO: useful commit comment? message := fmt.Sprintf("%s %s to %s", cmdName, tarSum.Sum(nil), dest) s.Commit(message) // Check cache s, hit, err := b.probeCache(s) if err != nil { return s, err } if hit { return s, nil } origCmd := s.Config.Cmd s.Config.Cmd = []string{"/bin/sh", "-c", "#(nop) " + message} if s.NoCache.ContainerID, err = b.client.CreateContainer(s); err != nil { return s, err } s.Config.Cmd = origCmd // We need to make a new tar stream, because the previous one has been // read by the tarsum; maybe, optimize this in future if u, err = makeTarStream(b.cfg.ContextDir, dest, cmdName, src, excludes, b.urlFetcher); err != nil { return s, err } // Copy to "/" because we made the prefix inside the tar archive // Do that because we are not able to reliably create directories inside the container if err = b.client.UploadToContainer(s.NoCache.ContainerID, u.tar, "/"); err != nil { return s, err } return s, nil }