| // Package layer is package for managing read-only |
| // and read-write mounts on the union file system |
| // driver. Read-only mounts are referenced using a |
| // content hash and are protected from mutation in |
| // the exposed interface. The tar format is used |
| // to create read-only layers and export both |
| // read-only and writable layers. The exported |
| // tar data for a read-only layer should match |
| // the tar used to create the layer. |
| package layer // import "github.com/docker/docker/layer" |
| |
| import ( |
| "errors" |
| "io" |
| |
| "github.com/docker/distribution" |
| "github.com/docker/docker/pkg/archive" |
| "github.com/docker/docker/pkg/containerfs" |
| "github.com/opencontainers/go-digest" |
| "github.com/sirupsen/logrus" |
| ) |
| |
| var ( |
| // ErrLayerDoesNotExist is used when an operation is |
| // attempted on a layer which does not exist. |
| ErrLayerDoesNotExist = errors.New("layer does not exist") |
| |
| // ErrLayerNotRetained is used when a release is |
| // attempted on a layer which is not retained. |
| ErrLayerNotRetained = errors.New("layer not retained") |
| |
| // ErrMountDoesNotExist is used when an operation is |
| // attempted on a mount layer which does not exist. |
| ErrMountDoesNotExist = errors.New("mount does not exist") |
| |
| // ErrMountNameConflict is used when a mount is attempted |
| // to be created but there is already a mount with the name |
| // used for creation. |
| ErrMountNameConflict = errors.New("mount already exists with name") |
| |
| // ErrActiveMount is used when an operation on a |
| // mount is attempted but the layer is still |
| // mounted and the operation cannot be performed. |
| ErrActiveMount = errors.New("mount still active") |
| |
| // ErrNotMounted is used when requesting an active |
| // mount but the layer is not mounted. |
| ErrNotMounted = errors.New("not mounted") |
| |
| // ErrMaxDepthExceeded is used when a layer is attempted |
| // to be created which would result in a layer depth |
| // greater than the 125 max. |
| ErrMaxDepthExceeded = errors.New("max depth exceeded") |
| |
| // ErrNotSupported is used when the action is not supported |
| // on the current host operating system. |
| ErrNotSupported = errors.New("not support on this host operating system") |
| ) |
| |
| // ChainID is the content-addressable ID of a layer. |
| type ChainID digest.Digest |
| |
| // String returns a string rendition of a layer ID |
| func (id ChainID) String() string { |
| return string(id) |
| } |
| |
| // DiffID is the hash of an individual layer tar. |
| type DiffID digest.Digest |
| |
| // String returns a string rendition of a layer DiffID |
| func (diffID DiffID) String() string { |
| return string(diffID) |
| } |
| |
| // TarStreamer represents an object which may |
| // have its contents exported as a tar stream. |
| type TarStreamer interface { |
| // TarStream returns a tar archive stream |
| // for the contents of a layer. |
| TarStream() (io.ReadCloser, error) |
| } |
| |
| // Layer represents a read-only layer |
| type Layer interface { |
| TarStreamer |
| |
| // TarStreamFrom returns a tar archive stream for all the layer chain with |
| // arbitrary depth. |
| TarStreamFrom(ChainID) (io.ReadCloser, error) |
| |
| // ChainID returns the content hash of the entire layer chain. The hash |
| // chain is made up of DiffID of top layer and all of its parents. |
| ChainID() ChainID |
| |
| // DiffID returns the content hash of the layer |
| // tar stream used to create this layer. |
| DiffID() DiffID |
| |
| // Parent returns the next layer in the layer chain. |
| Parent() Layer |
| |
| // Size returns the size of the entire layer chain. The size |
| // is calculated from the total size of all files in the layers. |
| Size() (int64, error) |
| |
| // DiffSize returns the size difference of the top layer |
| // from parent layer. |
| DiffSize() (int64, error) |
| |
| // Metadata returns the low level storage metadata associated |
| // with layer. |
| Metadata() (map[string]string, error) |
| } |
| |
| // RWLayer represents a layer which is |
| // read and writable |
| type RWLayer interface { |
| TarStreamer |
| |
| // Name of mounted layer |
| Name() string |
| |
| // Parent returns the layer which the writable |
| // layer was created from. |
| Parent() Layer |
| |
| // Mount mounts the RWLayer and returns the filesystem path |
| // the to the writable layer. |
| Mount(mountLabel string) (containerfs.ContainerFS, error) |
| |
| // Unmount unmounts the RWLayer. This should be called |
| // for every mount. If there are multiple mount calls |
| // this operation will only decrement the internal mount counter. |
| Unmount() error |
| |
| // Size represents the size of the writable layer |
| // as calculated by the total size of the files |
| // changed in the mutable layer. |
| Size() (int64, error) |
| |
| // Changes returns the set of changes for the mutable layer |
| // from the base layer. |
| Changes() ([]archive.Change, error) |
| |
| // Metadata returns the low level metadata for the mutable layer |
| Metadata() (map[string]string, error) |
| } |
| |
| // Metadata holds information about a |
| // read-only layer |
| type Metadata struct { |
| // ChainID is the content hash of the layer |
| ChainID ChainID |
| |
| // DiffID is the hash of the tar data used to |
| // create the layer |
| DiffID DiffID |
| |
| // Size is the size of the layer and all parents |
| Size int64 |
| |
| // DiffSize is the size of the top layer |
| DiffSize int64 |
| } |
| |
| // MountInit is a function to initialize a |
| // writable mount. Changes made here will |
| // not be included in the Tar stream of the |
| // RWLayer. |
| type MountInit func(root containerfs.ContainerFS) error |
| |
| // CreateRWLayerOpts contains optional arguments to be passed to CreateRWLayer |
| type CreateRWLayerOpts struct { |
| MountLabel string |
| InitFunc MountInit |
| StorageOpt map[string]string |
| } |
| |
| // Store represents a backend for managing both |
| // read-only and read-write layers. |
| type Store interface { |
| Register(io.Reader, ChainID) (Layer, error) |
| Get(ChainID) (Layer, error) |
| Map() map[ChainID]Layer |
| Release(Layer) ([]Metadata, error) |
| |
| CreateRWLayer(id string, parent ChainID, opts *CreateRWLayerOpts) (RWLayer, error) |
| GetRWLayer(id string) (RWLayer, error) |
| GetMountID(id string) (string, error) |
| ReleaseRWLayer(RWLayer) ([]Metadata, error) |
| |
| Cleanup() error |
| DriverStatus() [][2]string |
| DriverName() string |
| } |
| |
| // DescribableStore represents a layer store capable of storing |
| // descriptors for layers. |
| type DescribableStore interface { |
| RegisterWithDescriptor(io.Reader, ChainID, distribution.Descriptor) (Layer, error) |
| } |
| |
| // CreateChainID returns ID for a layerDigest slice |
| func CreateChainID(dgsts []DiffID) ChainID { |
| return createChainIDFromParent("", dgsts...) |
| } |
| |
| func createChainIDFromParent(parent ChainID, dgsts ...DiffID) ChainID { |
| if len(dgsts) == 0 { |
| return parent |
| } |
| if parent == "" { |
| return createChainIDFromParent(ChainID(dgsts[0]), dgsts[1:]...) |
| } |
| // H = "H(n-1) SHA256(n)" |
| dgst := digest.FromBytes([]byte(string(parent) + " " + string(dgsts[0]))) |
| return createChainIDFromParent(ChainID(dgst), dgsts[1:]...) |
| } |
| |
| // ReleaseAndLog releases the provided layer from the given layer |
| // store, logging any error and release metadata |
| func ReleaseAndLog(ls Store, l Layer) { |
| metadata, err := ls.Release(l) |
| if err != nil { |
| logrus.Errorf("Error releasing layer %s: %v", l.ChainID(), err) |
| } |
| LogReleaseMetadata(metadata) |
| } |
| |
| // LogReleaseMetadata logs a metadata array, uses this to |
| // ensure consistent logging for release metadata |
| func LogReleaseMetadata(metadatas []Metadata) { |
| for _, metadata := range metadatas { |
| logrus.Infof("Layer %s cleaned up", metadata.ChainID) |
| } |
| } |