123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815 |
- package graph
- import (
- "compress/gzip"
- "encoding/json"
- "errors"
- "fmt"
- "io"
- "io/ioutil"
- "os"
- "path/filepath"
- "runtime"
- "strconv"
- "strings"
- "sync"
- "time"
- "github.com/Sirupsen/logrus"
- "github.com/docker/distribution/digest"
- "github.com/docker/docker/autogen/dockerversion"
- "github.com/docker/docker/daemon/graphdriver"
- "github.com/docker/docker/image"
- "github.com/docker/docker/pkg/archive"
- "github.com/docker/docker/pkg/idtools"
- "github.com/docker/docker/pkg/locker"
- "github.com/docker/docker/pkg/progressreader"
- "github.com/docker/docker/pkg/streamformatter"
- "github.com/docker/docker/pkg/stringid"
- "github.com/docker/docker/pkg/truncindex"
- "github.com/docker/docker/runconfig"
- "github.com/vbatts/tar-split/tar/asm"
- "github.com/vbatts/tar-split/tar/storage"
- )
- // v1Descriptor is a non-content-addressable image descriptor
- type v1Descriptor struct {
- img *image.Image
- }
- // ID returns the image ID specified in the image structure.
- func (img v1Descriptor) ID() string {
- return img.img.ID
- }
- // Parent returns the parent ID specified in the image structure.
- func (img v1Descriptor) Parent() string {
- return img.img.Parent
- }
- // MarshalConfig renders the image structure into JSON.
- func (img v1Descriptor) MarshalConfig() ([]byte, error) {
- return json.Marshal(img.img)
- }
- // The type is used to protect pulling or building related image
- // layers from deleteing when filtered by dangling=true
- // The key of layers is the images ID which is pulling or building
- // The value of layers is a slice which hold layer IDs referenced to
- // pulling or building images
- type retainedLayers struct {
- layerHolders map[string]map[string]struct{} // map[layerID]map[sessionID]
- sync.Mutex
- }
- func (r *retainedLayers) Add(sessionID string, layerIDs []string) {
- r.Lock()
- defer r.Unlock()
- for _, layerID := range layerIDs {
- if r.layerHolders[layerID] == nil {
- r.layerHolders[layerID] = map[string]struct{}{}
- }
- r.layerHolders[layerID][sessionID] = struct{}{}
- }
- }
- func (r *retainedLayers) Delete(sessionID string, layerIDs []string) {
- r.Lock()
- defer r.Unlock()
- for _, layerID := range layerIDs {
- holders, ok := r.layerHolders[layerID]
- if !ok {
- continue
- }
- delete(holders, sessionID)
- if len(holders) == 0 {
- delete(r.layerHolders, layerID) // Delete any empty reference set.
- }
- }
- }
- func (r *retainedLayers) Exists(layerID string) bool {
- r.Lock()
- _, exists := r.layerHolders[layerID]
- r.Unlock()
- return exists
- }
- // A Graph is a store for versioned filesystem images and the relationship between them.
- type Graph struct {
- root string
- idIndex *truncindex.TruncIndex
- driver graphdriver.Driver
- imagesMutex sync.Mutex
- imageMutex locker.Locker // protect images in driver.
- retained *retainedLayers
- tarSplitDisabled bool
- uidMaps []idtools.IDMap
- gidMaps []idtools.IDMap
- // access to parentRefs must be protected with imageMutex locking the image id
- // on the key of the map (e.g. imageMutex.Lock(img.ID), parentRefs[img.ID]...)
- parentRefs map[string]int
- }
- // file names for ./graph/<ID>/
- const (
- jsonFileName = "json"
- layersizeFileName = "layersize"
- digestFileName = "checksum"
- tarDataFileName = "tar-data.json.gz"
- v1CompatibilityFileName = "v1Compatibility"
- parentFileName = "parent"
- )
- var (
- // errDigestNotSet is used when request the digest for a layer
- // but the layer has no digest value or content to compute the
- // the digest.
- errDigestNotSet = errors.New("digest is not set for layer")
- )
- // NewGraph instantiates a new graph at the given root path in the filesystem.
- // `root` will be created if it doesn't exist.
- func NewGraph(root string, driver graphdriver.Driver, uidMaps, gidMaps []idtools.IDMap) (*Graph, error) {
- abspath, err := filepath.Abs(root)
- if err != nil {
- return nil, err
- }
- rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps)
- if err != nil {
- return nil, err
- }
- // Create the root directory if it doesn't exists
- if err := idtools.MkdirAllAs(root, 0700, rootUID, rootGID); err != nil && !os.IsExist(err) {
- return nil, err
- }
- graph := &Graph{
- root: abspath,
- idIndex: truncindex.NewTruncIndex([]string{}),
- driver: driver,
- retained: &retainedLayers{layerHolders: make(map[string]map[string]struct{})},
- uidMaps: uidMaps,
- gidMaps: gidMaps,
- parentRefs: make(map[string]int),
- }
- // Windows does not currently support tarsplit functionality.
- if runtime.GOOS == "windows" {
- graph.tarSplitDisabled = true
- }
- if err := graph.restore(); err != nil {
- return nil, err
- }
- return graph, nil
- }
- // IsHeld returns whether the given layerID is being used by an ongoing pull or build.
- func (graph *Graph) IsHeld(layerID string) bool {
- return graph.retained.Exists(layerID)
- }
- func (graph *Graph) restore() error {
- dir, err := ioutil.ReadDir(graph.root)
- if err != nil {
- return err
- }
- var ids = []string{}
- for _, v := range dir {
- id := v.Name()
- if graph.driver.Exists(id) {
- img, err := graph.loadImage(id)
- if err != nil {
- logrus.Warnf("ignoring image %s, it could not be restored: %v", id, err)
- continue
- }
- graph.imageMutex.Lock(img.Parent)
- graph.parentRefs[img.Parent]++
- graph.imageMutex.Unlock(img.Parent)
- ids = append(ids, id)
- }
- }
- graph.idIndex = truncindex.NewTruncIndex(ids)
- logrus.Debugf("Restored %d elements", len(ids))
- return nil
- }
- // IsNotExist detects whether an image exists by parsing the incoming error
- // message.
- func (graph *Graph) IsNotExist(err error, id string) bool {
- // FIXME: Implement error subclass instead of looking at the error text
- // Note: This is the way golang implements os.IsNotExists on Plan9
- return err != nil && (strings.Contains(strings.ToLower(err.Error()), "does not exist") || strings.Contains(strings.ToLower(err.Error()), "no such")) && strings.Contains(err.Error(), id)
- }
- // Exists returns true if an image is registered at the given id.
- // If the image doesn't exist or if an error is encountered, false is returned.
- func (graph *Graph) Exists(id string) bool {
- if _, err := graph.Get(id); err != nil {
- return false
- }
- return true
- }
- // Get returns the image with the given id, or an error if the image doesn't exist.
- func (graph *Graph) Get(name string) (*image.Image, error) {
- id, err := graph.idIndex.Get(name)
- if err != nil {
- if err == truncindex.ErrNotExist {
- return nil, fmt.Errorf("image %s does not exist", name)
- }
- return nil, err
- }
- img, err := graph.loadImage(id)
- if err != nil {
- return nil, err
- }
- if img.ID != id {
- return nil, fmt.Errorf("Image stored at '%s' has wrong id '%s'", id, img.ID)
- }
- if img.Size < 0 {
- size, err := graph.driver.DiffSize(img.ID, img.Parent)
- if err != nil {
- return nil, fmt.Errorf("unable to calculate size of image id %q: %s", img.ID, err)
- }
- img.Size = size
- if err := graph.saveSize(graph.imageRoot(id), img.Size); err != nil {
- return nil, err
- }
- }
- return img, nil
- }
- // Create creates a new image and registers it in the graph.
- func (graph *Graph) Create(layerData io.Reader, containerID, containerImage, comment, author string, containerConfig, config *runconfig.Config) (*image.Image, error) {
- img := &image.Image{
- ID: stringid.GenerateRandomID(),
- Comment: comment,
- Created: time.Now().UTC(),
- DockerVersion: dockerversion.VERSION,
- Author: author,
- Config: config,
- Architecture: runtime.GOARCH,
- OS: runtime.GOOS,
- }
- if containerID != "" {
- img.Parent = containerImage
- img.Container = containerID
- img.ContainerConfig = *containerConfig
- }
- if err := graph.Register(v1Descriptor{img}, layerData); err != nil {
- return nil, err
- }
- return img, nil
- }
- // Register imports a pre-existing image into the graph.
- // Returns nil if the image is already registered.
- func (graph *Graph) Register(im image.Descriptor, layerData io.Reader) (err error) {
- imgID := im.ID()
- if err := image.ValidateID(imgID); err != nil {
- return err
- }
- // this is needed cause pull_v2 attemptIDReuse could deadlock
- graph.imagesMutex.Lock()
- defer graph.imagesMutex.Unlock()
- // We need this entire operation to be atomic within the engine. Note that
- // this doesn't mean Register is fully safe yet.
- graph.imageMutex.Lock(imgID)
- defer graph.imageMutex.Unlock(imgID)
- return graph.register(im, layerData)
- }
- func (graph *Graph) register(im image.Descriptor, layerData io.Reader) (err error) {
- imgID := im.ID()
- // Skip register if image is already registered
- if graph.Exists(imgID) {
- return nil
- }
- // The returned `error` must be named in this function's signature so that
- // `err` is not shadowed in this deferred cleanup.
- defer func() {
- // If any error occurs, remove the new dir from the driver.
- // Don't check for errors since the dir might not have been created.
- if err != nil {
- graph.driver.Remove(imgID)
- }
- }()
- // Ensure that the image root does not exist on the filesystem
- // when it is not registered in the graph.
- // This is common when you switch from one graph driver to another
- if err := os.RemoveAll(graph.imageRoot(imgID)); err != nil && !os.IsNotExist(err) {
- return err
- }
- // If the driver has this ID but the graph doesn't, remove it from the driver to start fresh.
- // (the graph is the source of truth).
- // Ignore errors, since we don't know if the driver correctly returns ErrNotExist.
- // (FIXME: make that mandatory for drivers).
- graph.driver.Remove(imgID)
- tmp, err := graph.mktemp()
- if err != nil {
- return err
- }
- defer os.RemoveAll(tmp)
- parent := im.Parent()
- // Create root filesystem in the driver
- if err := createRootFilesystemInDriver(graph, imgID, parent); err != nil {
- return err
- }
- // Apply the diff/layer
- config, err := im.MarshalConfig()
- if err != nil {
- return err
- }
- if err := graph.storeImage(imgID, parent, config, layerData, tmp); err != nil {
- return err
- }
- // Commit
- if err := os.Rename(tmp, graph.imageRoot(imgID)); err != nil {
- return err
- }
- graph.idIndex.Add(imgID)
- graph.imageMutex.Lock(parent)
- graph.parentRefs[parent]++
- graph.imageMutex.Unlock(parent)
- return nil
- }
- func createRootFilesystemInDriver(graph *Graph, id, parent string) error {
- if err := graph.driver.Create(id, parent); err != nil {
- return fmt.Errorf("Driver %s failed to create image rootfs %s: %s", graph.driver, id, err)
- }
- return nil
- }
- // TempLayerArchive creates a temporary archive of the given image's filesystem layer.
- // The archive is stored on disk and will be automatically deleted as soon as has been read.
- // If output is not nil, a human-readable progress bar will be written to it.
- func (graph *Graph) tempLayerArchive(id string, sf *streamformatter.StreamFormatter, output io.Writer) (*archive.TempArchive, error) {
- image, err := graph.Get(id)
- if err != nil {
- return nil, err
- }
- tmp, err := graph.mktemp()
- if err != nil {
- return nil, err
- }
- defer os.RemoveAll(tmp)
- a, err := graph.tarLayer(image)
- if err != nil {
- return nil, err
- }
- progressReader := progressreader.New(progressreader.Config{
- In: a,
- Out: output,
- Formatter: sf,
- Size: 0,
- NewLines: false,
- ID: stringid.TruncateID(id),
- Action: "Buffering to disk",
- })
- defer progressReader.Close()
- return archive.NewTempArchive(progressReader, tmp)
- }
- // mktemp creates a temporary sub-directory inside the graph's filesystem.
- func (graph *Graph) mktemp() (string, error) {
- dir := filepath.Join(graph.root, "_tmp", stringid.GenerateNonCryptoID())
- rootUID, rootGID, err := idtools.GetRootUIDGID(graph.uidMaps, graph.gidMaps)
- if err != nil {
- return "", err
- }
- if err := idtools.MkdirAllAs(dir, 0700, rootUID, rootGID); err != nil {
- return "", err
- }
- return dir, nil
- }
- // Delete atomically removes an image from the graph.
- func (graph *Graph) Delete(name string) error {
- id, err := graph.idIndex.Get(name)
- if err != nil {
- return err
- }
- img, err := graph.Get(id)
- if err != nil {
- return err
- }
- graph.idIndex.Delete(id)
- tmp, err := graph.mktemp()
- if err != nil {
- tmp = graph.imageRoot(id)
- } else {
- if err := os.Rename(graph.imageRoot(id), tmp); err != nil {
- // On err make tmp point to old dir and cleanup unused tmp dir
- os.RemoveAll(tmp)
- tmp = graph.imageRoot(id)
- }
- }
- // Remove rootfs data from the driver
- graph.driver.Remove(id)
- graph.imageMutex.Lock(img.Parent)
- graph.parentRefs[img.Parent]--
- if graph.parentRefs[img.Parent] == 0 {
- delete(graph.parentRefs, img.Parent)
- }
- graph.imageMutex.Unlock(img.Parent)
- // Remove the trashed image directory
- return os.RemoveAll(tmp)
- }
- // Map returns a list of all images in the graph, addressable by ID.
- func (graph *Graph) Map() map[string]*image.Image {
- images := make(map[string]*image.Image)
- graph.walkAll(func(image *image.Image) {
- images[image.ID] = image
- })
- return images
- }
- // walkAll iterates over each image in the graph, and passes it to a handler.
- // The walking order is undetermined.
- func (graph *Graph) walkAll(handler func(*image.Image)) {
- graph.idIndex.Iterate(func(id string) {
- img, err := graph.Get(id)
- if err != nil {
- return
- }
- if handler != nil {
- handler(img)
- }
- })
- }
- // ByParent returns a lookup table of images by their parent.
- // If an image of key ID has 3 children images, then the value for key ID
- // will be a list of 3 images.
- // If an image has no children, it will not have an entry in the table.
- func (graph *Graph) ByParent() map[string][]*image.Image {
- byParent := make(map[string][]*image.Image)
- graph.walkAll(func(img *image.Image) {
- parent, err := graph.Get(img.Parent)
- if err != nil {
- return
- }
- if children, exists := byParent[parent.ID]; exists {
- byParent[parent.ID] = append(children, img)
- } else {
- byParent[parent.ID] = []*image.Image{img}
- }
- })
- return byParent
- }
- // HasChildren returns whether the given image has any child images.
- func (graph *Graph) HasChildren(imgID string) bool {
- graph.imageMutex.Lock(imgID)
- count := graph.parentRefs[imgID]
- graph.imageMutex.Unlock(imgID)
- return count > 0
- }
- // Retain keeps the images and layers that are in the pulling chain so that
- // they are not deleted. If not retained, they may be deleted by rmi.
- func (graph *Graph) Retain(sessionID string, layerIDs ...string) {
- graph.retained.Add(sessionID, layerIDs)
- }
- // Release removes the referenced image ID from the provided set of layers.
- func (graph *Graph) Release(sessionID string, layerIDs ...string) {
- graph.retained.Delete(sessionID, layerIDs)
- }
- // heads returns all heads in the graph, keyed by id.
- // A head is an image which is not the parent of another image in the graph.
- func (graph *Graph) heads() map[string]*image.Image {
- heads := make(map[string]*image.Image)
- graph.walkAll(func(image *image.Image) {
- // if it has no children, then it's not a parent, so it's an head
- if !graph.HasChildren(image.ID) {
- heads[image.ID] = image
- }
- })
- return heads
- }
- // tarLayer returns a tar archive of the image's filesystem layer.
- func (graph *Graph) tarLayer(img *image.Image) (arch io.ReadCloser, err error) {
- rdr, err := graph.assembleTarLayer(img)
- if err != nil {
- logrus.Debugf("[graph] tarLayer with traditional differ: %s", img.ID)
- return graph.driver.Diff(img.ID, img.Parent)
- }
- return rdr, nil
- }
- func (graph *Graph) imageRoot(id string) string {
- return filepath.Join(graph.root, id)
- }
- // loadImage fetches the image with the given id from the graph.
- func (graph *Graph) loadImage(id string) (*image.Image, error) {
- root := graph.imageRoot(id)
- // Open the JSON file to decode by streaming
- jsonSource, err := os.Open(jsonPath(root))
- if err != nil {
- return nil, err
- }
- defer jsonSource.Close()
- img := &image.Image{}
- dec := json.NewDecoder(jsonSource)
- // Decode the JSON data
- if err := dec.Decode(img); err != nil {
- return nil, err
- }
- if img.ID == "" {
- img.ID = id
- }
- if img.Parent == "" && img.ParentID != "" && img.ParentID.Validate() == nil {
- img.Parent = img.ParentID.Hex()
- }
- // compatibilityID for parent
- parent, err := ioutil.ReadFile(filepath.Join(root, parentFileName))
- if err == nil && len(parent) > 0 {
- img.Parent = string(parent)
- }
- if err := image.ValidateID(img.ID); err != nil {
- return nil, err
- }
- if buf, err := ioutil.ReadFile(filepath.Join(root, layersizeFileName)); err != nil {
- if !os.IsNotExist(err) {
- return nil, err
- }
- // If the layersize file does not exist then set the size to a negative number
- // because a layer size of 0 (zero) is valid
- img.Size = -1
- } else {
- // Using Atoi here instead would temporarily convert the size to a machine
- // dependent integer type, which causes images larger than 2^31 bytes to
- // display negative sizes on 32-bit machines:
- size, err := strconv.ParseInt(string(buf), 10, 64)
- if err != nil {
- return nil, err
- }
- img.Size = int64(size)
- }
- return img, nil
- }
- // saveSize stores the `size` in the provided graph `img` directory `root`.
- func (graph *Graph) saveSize(root string, size int64) error {
- if err := ioutil.WriteFile(filepath.Join(root, layersizeFileName), []byte(strconv.FormatInt(size, 10)), 0600); err != nil {
- return fmt.Errorf("Error storing image size in %s/%s: %s", root, layersizeFileName, err)
- }
- return nil
- }
- // setLayerDigestWithLock sets the digest for the image layer to the provided value.
- func (graph *Graph) setLayerDigestWithLock(id string, dgst digest.Digest) error {
- graph.imageMutex.Lock(id)
- defer graph.imageMutex.Unlock(id)
- return graph.setLayerDigest(id, dgst)
- }
- func (graph *Graph) setLayerDigest(id string, dgst digest.Digest) error {
- root := graph.imageRoot(id)
- if err := ioutil.WriteFile(filepath.Join(root, digestFileName), []byte(dgst.String()), 0600); err != nil {
- return fmt.Errorf("Error storing digest in %s/%s: %s", root, digestFileName, err)
- }
- return nil
- }
- // getLayerDigestWithLock gets the digest for the provide image layer id.
- func (graph *Graph) getLayerDigestWithLock(id string) (digest.Digest, error) {
- graph.imageMutex.Lock(id)
- defer graph.imageMutex.Unlock(id)
- return graph.getLayerDigest(id)
- }
- func (graph *Graph) getLayerDigest(id string) (digest.Digest, error) {
- root := graph.imageRoot(id)
- cs, err := ioutil.ReadFile(filepath.Join(root, digestFileName))
- if err != nil {
- if os.IsNotExist(err) {
- return "", errDigestNotSet
- }
- return "", err
- }
- return digest.ParseDigest(string(cs))
- }
- // setV1CompatibilityConfig stores the v1Compatibility JSON data associated
- // with the image in the manifest to the disk
- func (graph *Graph) setV1CompatibilityConfig(id string, data []byte) error {
- root := graph.imageRoot(id)
- return ioutil.WriteFile(filepath.Join(root, v1CompatibilityFileName), data, 0600)
- }
- // getV1CompatibilityConfig reads the v1Compatibility JSON data for the image
- // from the disk
- func (graph *Graph) getV1CompatibilityConfig(id string) ([]byte, error) {
- root := graph.imageRoot(id)
- return ioutil.ReadFile(filepath.Join(root, v1CompatibilityFileName))
- }
- // generateV1CompatibilityChain makes sure v1Compatibility JSON data exists
- // for the image. If it doesn't it generates and stores it for the image and
- // all of it's parents based on the image config JSON.
- func (graph *Graph) generateV1CompatibilityChain(id string) ([]byte, error) {
- graph.imageMutex.Lock(id)
- defer graph.imageMutex.Unlock(id)
- if v1config, err := graph.getV1CompatibilityConfig(id); err == nil {
- return v1config, nil
- }
- // generate new, store it to disk
- img, err := graph.Get(id)
- if err != nil {
- return nil, err
- }
- digestPrefix := string(digest.Canonical) + ":"
- img.ID = strings.TrimPrefix(img.ID, digestPrefix)
- if img.Parent != "" {
- parentConfig, err := graph.generateV1CompatibilityChain(img.Parent)
- if err != nil {
- return nil, err
- }
- var parent struct{ ID string }
- err = json.Unmarshal(parentConfig, &parent)
- if err != nil {
- return nil, err
- }
- img.Parent = parent.ID
- }
- json, err := json.Marshal(img)
- if err != nil {
- return nil, err
- }
- if err := graph.setV1CompatibilityConfig(id, json); err != nil {
- return nil, err
- }
- return json, nil
- }
- func jsonPath(root string) string {
- return filepath.Join(root, jsonFileName)
- }
- // storeImage stores file system layer data for the given image to the
- // graph's storage driver. Image metadata is stored in a file
- // at the specified root directory.
- func (graph *Graph) storeImage(id, parent string, config []byte, layerData io.Reader, root string) (err error) {
- var size int64
- // Store the layer. If layerData is not nil, unpack it into the new layer
- if layerData != nil {
- if size, err = graph.disassembleAndApplyTarLayer(id, parent, layerData, root); err != nil {
- return err
- }
- }
- if err := graph.saveSize(root, size); err != nil {
- return err
- }
- if err := ioutil.WriteFile(jsonPath(root), config, 0600); err != nil {
- return err
- }
- // If image is pointing to a parent via CompatibilityID write the reference to disk
- img, err := image.NewImgJSON(config)
- if err != nil {
- return err
- }
- if img.ParentID.Validate() == nil && parent != img.ParentID.Hex() {
- if err := ioutil.WriteFile(filepath.Join(root, parentFileName), []byte(parent), 0600); err != nil {
- return err
- }
- }
- return nil
- }
- func (graph *Graph) disassembleAndApplyTarLayer(id, parent string, layerData io.Reader, root string) (size int64, err error) {
- var ar io.Reader
- if graph.tarSplitDisabled {
- ar = layerData
- } else {
- // this is saving the tar-split metadata
- mf, err := os.OpenFile(filepath.Join(root, tarDataFileName), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0600))
- if err != nil {
- return 0, err
- }
- mfz := gzip.NewWriter(mf)
- metaPacker := storage.NewJSONPacker(mfz)
- defer mf.Close()
- defer mfz.Close()
- inflatedLayerData, err := archive.DecompressStream(layerData)
- if err != nil {
- return 0, err
- }
- // we're passing nil here for the file putter, because the ApplyDiff will
- // handle the extraction of the archive
- rdr, err := asm.NewInputTarStream(inflatedLayerData, metaPacker, nil)
- if err != nil {
- return 0, err
- }
- ar = archive.Reader(rdr)
- }
- if size, err = graph.driver.ApplyDiff(id, parent, ar); err != nil {
- return 0, err
- }
- return
- }
- func (graph *Graph) assembleTarLayer(img *image.Image) (io.ReadCloser, error) {
- root := graph.imageRoot(img.ID)
- mFileName := filepath.Join(root, tarDataFileName)
- mf, err := os.Open(mFileName)
- if err != nil {
- if !os.IsNotExist(err) {
- logrus.Errorf("failed to open %q: %s", mFileName, err)
- }
- return nil, err
- }
- pR, pW := io.Pipe()
- // this will need to be in a goroutine, as we are returning the stream of a
- // tar archive, but can not close the metadata reader early (when this
- // function returns)...
- go func() {
- defer mf.Close()
- // let's reassemble!
- logrus.Debugf("[graph] TarLayer with reassembly: %s", img.ID)
- mfz, err := gzip.NewReader(mf)
- if err != nil {
- pW.CloseWithError(fmt.Errorf("[graph] error with %s: %s", mFileName, err))
- return
- }
- defer mfz.Close()
- // get our relative path to the container
- fsLayer, err := graph.driver.Get(img.ID, "")
- if err != nil {
- pW.CloseWithError(err)
- return
- }
- defer graph.driver.Put(img.ID)
- metaUnpacker := storage.NewJSONUnpacker(mfz)
- fileGetter := storage.NewPathFileGetter(fsLayer)
- logrus.Debugf("[graph] %s is at %q", img.ID, fsLayer)
- ots := asm.NewOutputTarStream(fileGetter, metaUnpacker)
- defer ots.Close()
- if _, err := io.Copy(pW, ots); err != nil {
- pW.CloseWithError(err)
- return
- }
- pW.Close()
- }()
- return pR, nil
- }
|