Skip to content

Commit

Permalink
Support additional layer store
Browse files Browse the repository at this point in the history
Signed-off-by: Kohei Tokunaga <[email protected]>
  • Loading branch information
ktock committed Feb 2, 2021
1 parent a774873 commit 83ce465
Show file tree
Hide file tree
Showing 5 changed files with 299 additions and 9 deletions.
27 changes: 27 additions & 0 deletions drivers/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,19 @@ type LayerIDMapUpdater interface {
SupportsShifting() bool
}

// AdditionalLayerStore is the directory where exploded layer diff is stored.
// Each layer can be accessed at `Path/<layerdigest>/`.
type AdditionalLayerStore struct {

// Path is the directory where AdditionalLayerStore is available on the host.
Path string

// WithReference is true when the store contains image reference information (base64-encoded)
// in its layer search path so the path to the diff will be
// `Path/base64(reference)/<layerdigest>/`
WithReference bool
}

// Driver is the interface for layered/snapshot file system drivers.
type Driver interface {
ProtoDriver
Expand All @@ -173,6 +186,20 @@ type CapabilityDriver interface {
Capabilities() Capabilities
}

// AdditionalLayerStoreDriver is the interface for driver that supports
// additional layer store functionality.
type AdditionalLayerStoreDriver interface {
Driver

// CreateFromDirectory creates a new filesystem layer with the specified id and
// parent. Parent may be "". The contents of the layer (diff) will be the directory
// specified by targetDiffDir.
CreateFromDirectory(id, parent string, targetDiffDir string) error

// AdditionalLayerStores returns additional layer stores supported by the driver
AdditionalLayerStores() []AdditionalLayerStore
}

// DiffGetterDriver is the interface for layered file system drivers that
// provide a specialized function for getting file contents for tar-split.
type DiffGetterDriver interface {
Expand Down
114 changes: 105 additions & 9 deletions drivers/overlay/overlay.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ const (

type overlayOptions struct {
imageStores []string
layerStores []graphdriver.AdditionalLayerStore
quota quota.Quota
mountProgram string
skipMountHome bool
Expand Down Expand Up @@ -316,6 +317,36 @@ func parseOptions(options []string) (*overlayOptions, error) {
}
o.imageStores = append(o.imageStores, store)
}
case "layerstore", "additionallayerstore":
logrus.Debugf("overlay: layerstore=%s", val)
// Additional read only layer stores to use for lower paths
if val == "" {
continue
}
for _, store := range strings.Split(val, ",") {
elems := strings.Split(val, ":")
store = filepath.Clean(elems[0])
if !filepath.IsAbs(store) {
return nil, fmt.Errorf("overlay: layerstore path %q is not absolute. Can not be relative", store)
}
st, err := os.Stat(store)
if err != nil {
return nil, fmt.Errorf("overlay: can't stat layerStore dir %s: %v", store, err)
}
if !st.IsDir() {
return nil, fmt.Errorf("overlay: layerstore path %q must be a directory", store)
}
var withReferece bool
for _, e := range elems[1:] {
if e == "ref" {
withReferece = true
}
}
o.layerStores = append(o.layerStores, graphdriver.AdditionalLayerStore{
Path: store,
WithReference: withReferece,
})
}
case "mount_program":
logrus.Debugf("overlay: mount_program=%s", val)
_, err := os.Stat(val)
Expand Down Expand Up @@ -528,6 +559,31 @@ func (d *Driver) Cleanup() error {
return mount.Unmount(d.home)
}

// AdditionalLayerStores returns additional layer stores supported by the driver
func (d *Driver) AdditionalLayerStores() []graphdriver.AdditionalLayerStore {
return d.options.layerStores
}

// CreateFromDirectory creates a new filesystem layer with the specified id and
// parent. Parent may be "". The contents of the layer (diff) will be the directory
// specified by targetDiffDir.
func (d *Driver) CreateFromDirectory(id, parent string, targetDiffDir string) error {
if !path.IsAbs(targetDiffDir) {
return fmt.Errorf("path of target diff directory must be absolute but %q", targetDiffDir)
}

// TODO: support opts
if err := d.Create(id, parent, nil); err != nil {
return err
}
dir := d.dir(id)
diffDir := path.Join(dir, "diff")
if err := os.RemoveAll(diffDir); err != nil {
return err
}
return os.Symlink(targetDiffDir, diffDir)
}

// CreateFromTemplate creates a layer with the same contents and parent as another layer.
func (d *Driver) CreateFromTemplate(id, template string, templateIDMappings *idtools.IDMappings, parent string, parentIDMappings *idtools.IDMappings, opts *graphdriver.CreateOpts, readWrite bool) error {
if readWrite {
Expand Down Expand Up @@ -1158,7 +1214,10 @@ func (f fileGetNilCloser) Close() error {
// DiffGetter returns a FileGetCloser that can read files from the directory that
// contains files for the layer differences. Used for direct access for tar-split.
func (d *Driver) DiffGetter(id string) (graphdriver.FileGetCloser, error) {
p := d.getDiffPath(id)
p, err := d.getDiffPath(id)
if err != nil {
return nil, err
}
return fileGetNilCloser{storage.NewPathFileGetter(p)}, nil
}

Expand All @@ -1180,7 +1239,10 @@ func (d *Driver) ApplyDiff(id, parent string, options graphdriver.ApplyDiffOpts)
idMappings = &idtools.IDMappings{}
}

applyDir := d.getDiffPath(id)
applyDir, err := d.getDiffPath(id)
if err != nil {
return 0, err
}

logrus.Debugf("Applying tar in %s", applyDir)
// Overlay doesn't need the parent id to apply the diff
Expand All @@ -1198,10 +1260,34 @@ func (d *Driver) ApplyDiff(id, parent string, options graphdriver.ApplyDiffOpts)
return directory.Size(applyDir)
}

func (d *Driver) getDiffPath(id string) string {
func (d *Driver) getDiffPath(id string) (string, error) {
dir := d.dir(id)
diffPath := path.Join(dir, "diff")
if ld, err := os.Readlink(diffPath); err == nil {
// `diff` of additional layer store is symlink to the actual `diff` directory.
if !path.IsAbs(ld) {
return "", fmt.Errorf("path of the diff directory must be absolute but %q", ld)
}
diffPath = ld
}
return diffPath, nil
}

return path.Join(dir, "diff")
func (d *Driver) getLowerDiffPaths(id string) ([]string, error) {
layers, err := d.getLowerDirs(id)
if err != nil {
return nil, err
}
for i, l := range layers {
if ld, err := os.Readlink(l); err == nil {
// `diff` of additional layer store is symlink to the actual `diff` directory.
if !path.IsAbs(ld) {
return nil, fmt.Errorf("path of the diff directory must be absolute but %q", ld)
}
layers[i] = ld
}
}
return layers, nil
}

// DiffSize calculates the changes between the specified id
Expand All @@ -1211,7 +1297,11 @@ func (d *Driver) DiffSize(id string, idMappings *idtools.IDMappings, parent stri
if d.useNaiveDiff() || !d.isParent(id, parent) {
return d.naiveDiff.DiffSize(id, idMappings, parent, parentMappings, mountLabel)
}
return directory.Size(d.getDiffPath(id))
p, err := d.getDiffPath(id)
if err != nil {
return 0, err
}
return directory.Size(p)
}

// Diff produces an archive of the changes between the specified
Expand All @@ -1225,12 +1315,15 @@ func (d *Driver) Diff(id string, idMappings *idtools.IDMappings, parent string,
idMappings = &idtools.IDMappings{}
}

lowerDirs, err := d.getLowerDirs(id)
lowerDirs, err := d.getLowerDiffPaths(id)
if err != nil {
return nil, err
}

diffPath := d.getDiffPath(id)
diffPath, err := d.getDiffPath(id)
if err != nil {
return nil, err
}
logrus.Debugf("Tar with options on %s", diffPath)
return archive.TarWithOptions(diffPath, &archive.TarOptions{
Compression: archive.Uncompressed,
Expand All @@ -1249,8 +1342,11 @@ func (d *Driver) Changes(id string, idMappings *idtools.IDMappings, parent strin
}
// Overlay doesn't have snapshots, so we need to get changes from all parent
// layers.
diffPath := d.getDiffPath(id)
layers, err := d.getLowerDirs(id)
diffPath, err := d.getDiffPath(id)
if err != nil {
return nil, err
}
layers, err := d.getLowerDiffPaths(id)
if err != nil {
return nil, err
}
Expand Down
64 changes: 64 additions & 0 deletions layers.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,10 @@ type LayerStore interface {
// LoadLocked wraps Load in a locked state. This means it loads the store
// and cleans-up invalid layers if needed.
LoadLocked() error

// PutFromAdditionalLayerStore creates a layer using the diff contained in the additional layer
// store.
PutFromAdditionalLayerStore(id string, parentLayer *Layer, d digest.Digest, imageref string) (layer *Layer, err error)
}

type layerStore struct {
Expand Down Expand Up @@ -602,6 +606,66 @@ func (r *layerStore) Status() ([][2]string, error) {
return r.driver.Status(), nil
}

func (r *layerStore) PutFromAdditionalLayerStore(id string, parentLayer *Layer, d digest.Digest, imageref string) (layer *Layer, err error) {
adriver, ok := r.driver.(drivers.AdditionalLayerStoreDriver)
if !ok {
return nil, ErrLayerUnknown
}

var diffDir string
var layerInfo *Layer
for _, ls := range adriver.AdditionalLayerStores() {
if layerInfo, diffDir, err = searchLayer(ls, d, imageref); err == nil {
break
} else if err != ErrLayerUnknown {
return nil, err
}
}

parent := ""
if parentLayer != nil {
parent = parentLayer.ID
}
if err = adriver.CreateFromDirectory(id, parent, diffDir); err != nil {
if id != "" {
return nil, errors.Wrapf(err, "error creating layer from dir with ID %q", id)
}
return nil, errors.Wrapf(err, "error creating layer from dir")
}

compressedsums := make(map[digest.Digest][]string)
uncompressedsums := make(map[digest.Digest][]string)
names := make(map[string]*Layer)
layer = copyLayer(layerInfo)
layer.ID = id
layer.Parent = parent
layer.Created = time.Now().UTC()
if layer.CompressedDigest != "" {
compressedsums[layer.CompressedDigest] = append(compressedsums[layer.CompressedDigest], layer.ID)
}
if layer.UncompressedDigest != "" {
uncompressedsums[layer.UncompressedDigest] = append(uncompressedsums[layer.UncompressedDigest], layer.ID)
}
for _, name := range layer.Names {
if conflict, ok := names[name]; ok {
r.removeName(conflict, name)
}
names[name] = layer
}
// TODO: check if necessary fields are filled
r.layers = append(r.layers, layer)
r.idindex.Add(id)
r.byid[id] = layer
r.byname = names
r.bycompressedsum = compressedsums
r.byuncompressedsum = uncompressedsums
if err := r.Save(); err != nil {
adriver.Remove(id)
return nil, err
}
return copyLayer(layer), nil
}

func (r *layerStore) Put(id string, parentLayer *Layer, names []string, mountLabel string, options map[string]string, moreOptions *LayerOptions, writeable bool, flags map[string]interface{}, diff io.Reader) (layer *Layer, size int64, err error) {
if !r.IsReadWrite() {
return nil, -1, errors.Wrapf(ErrStoreIsReadOnly, "not allowed to create new layers at %q", r.layerspath())
Expand Down
5 changes: 5 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ type OptionsConfig struct {
// for shared image content
AdditionalImageStores []string `toml:"additionalimagestores"`

// AdditionalLayerStores is the location of additional read/only
// Layer stores. Usually used to access Networked File System
// for shared image content
AdditionalLayerStores []string `toml:"additionallayerstores"`

// Size
Size string `toml:"size"`

Expand Down
Loading

0 comments on commit 83ce465

Please sign in to comment.