dotcloud/docker

View on GitHub
layer/ro_layer.go

Summary

Maintainability
A
40 mins
Test Coverage
package layer // import "github.com/docker/docker/layer"

import (
    "fmt"
    "io"

    "github.com/docker/distribution"
    "github.com/opencontainers/go-digest"
)

type roLayer struct {
    chainID    ChainID
    diffID     DiffID
    parent     *roLayer
    cacheID    string
    size       int64
    layerStore *layerStore
    descriptor distribution.Descriptor

    referenceCount int
    references     map[Layer]struct{}
}

// TarStream for roLayer guarantees that the data that is produced is the exact
// data that the layer was registered with.
func (rl *roLayer) TarStream() (io.ReadCloser, error) {
    rc, err := rl.layerStore.getTarStream(rl)
    if err != nil {
        return nil, err
    }

    vrc, err := newVerifiedReadCloser(rc, digest.Digest(rl.diffID))
    if err != nil {
        return nil, err
    }
    return vrc, nil
}

// TarStreamFrom does not make any guarantees to the correctness of the produced
// data. As such it should not be used when the layer content must be verified
// to be an exact match to the registered layer.
func (rl *roLayer) TarStreamFrom(parent ChainID) (io.ReadCloser, error) {
    var parentCacheID string
    for pl := rl.parent; pl != nil; pl = pl.parent {
        if pl.chainID == parent {
            parentCacheID = pl.cacheID
            break
        }
    }

    if parent != ChainID("") && parentCacheID == "" {
        return nil, fmt.Errorf("layer ID '%s' is not a parent of the specified layer: cannot provide diff to non-parent", parent)
    }
    return rl.layerStore.driver.Diff(rl.cacheID, parentCacheID)
}

func (rl *roLayer) CacheID() string {
    return rl.cacheID
}

func (rl *roLayer) ChainID() ChainID {
    return rl.chainID
}

func (rl *roLayer) DiffID() DiffID {
    return rl.diffID
}

func (rl *roLayer) Parent() Layer {
    if rl.parent == nil {
        return nil
    }
    return rl.parent
}

func (rl *roLayer) Size() int64 {
    size := rl.size
    if rl.parent != nil {
        size += rl.parent.Size()
    }

    return size
}

func (rl *roLayer) DiffSize() int64 {
    return rl.size
}

func (rl *roLayer) Metadata() (map[string]string, error) {
    return rl.layerStore.driver.GetMetadata(rl.cacheID)
}

type referencedCacheLayer struct {
    *roLayer
}

func (rl *roLayer) getReference() Layer {
    ref := &referencedCacheLayer{
        roLayer: rl,
    }
    rl.references[ref] = struct{}{}

    return ref
}

func (rl *roLayer) hasReference(ref Layer) bool {
    _, ok := rl.references[ref]
    return ok
}

func (rl *roLayer) hasReferences() bool {
    return len(rl.references) > 0
}

func (rl *roLayer) deleteReference(ref Layer) {
    delete(rl.references, ref)
}

func (rl *roLayer) depth() int {
    if rl.parent == nil {
        return 1
    }
    return rl.parent.depth() + 1
}

func storeLayer(tx *fileMetadataTransaction, layer *roLayer) error {
    if err := tx.SetDiffID(layer.diffID); err != nil {
        return err
    }
    if err := tx.SetSize(layer.size); err != nil {
        return err
    }
    if err := tx.SetCacheID(layer.cacheID); err != nil {
        return err
    }
    // Do not store empty descriptors
    if layer.descriptor.Digest != "" {
        if err := tx.SetDescriptor(layer.descriptor); err != nil {
            return err
        }
    }
    if layer.parent != nil {
        if err := tx.SetParent(layer.parent.chainID); err != nil {
            return err
        }
    }
    return nil
}

func newVerifiedReadCloser(rc io.ReadCloser, dgst digest.Digest) (io.ReadCloser, error) {
    return &verifiedReadCloser{
        rc:       rc,
        dgst:     dgst,
        verifier: dgst.Verifier(),
    }, nil
}

type verifiedReadCloser struct {
    rc       io.ReadCloser
    dgst     digest.Digest
    verifier digest.Verifier
}

func (vrc *verifiedReadCloser) Read(p []byte) (n int, err error) {
    n, err = vrc.rc.Read(p)
    if n > 0 {
        if n, err := vrc.verifier.Write(p[:n]); err != nil {
            return n, err
        }
    }
    if err == io.EOF {
        if !vrc.verifier.Verified() {
            err = fmt.Errorf("could not verify layer data for: %s. This may be because internal files in the layer store were modified. Re-pulling or rebuilding this image may resolve the issue", vrc.dgst)
        }
    }
    return
}

func (vrc *verifiedReadCloser) Close() error {
    return vrc.rc.Close()
}