Skip to content

Commit

Permalink
Merge pull request #775 from giuseppe/zstd-chunked
Browse files Browse the repository at this point in the history
Enable zstd:chunked support in containers/image
  • Loading branch information
rhatdan committed May 14, 2021
2 parents 1b86066 + a0ff89e commit 4f69205
Show file tree
Hide file tree
Showing 11 changed files with 1,884 additions and 0 deletions.
34 changes: 34 additions & 0 deletions drivers/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,40 @@ type Driver interface {
LayerIDMapUpdater
}

// DriverWithDifferOutput is the result of ApplyDiffWithDiffer
// This API is experimental and can be changed without bumping the major version number.
type DriverWithDifferOutput struct {
Differ Differ
Target string
Size int64
UIDs []uint32
GIDs []uint32
UncompressedDigest digest.Digest
Metadata string
BigData map[string][]byte
}

// Differ defines the interface for using a custom differ.
// This API is experimental and can be changed without bumping the major version number.
type Differ interface {
ApplyDiff(dest string, options *archive.TarOptions) (DriverWithDifferOutput, error)
}

// DriverWithDiffer is the interface for direct diff access.
// This API is experimental and can be changed without bumping the major version number.
type DriverWithDiffer interface {
Driver
// ApplyDiffWithDiffer applies the changes using the callback function.
// If id is empty, then a staging directory is created. The staging directory is guaranteed to be usable with ApplyDiffFromStagingDirectory.
ApplyDiffWithDiffer(id, parent string, options *ApplyDiffOpts, differ Differ) (output DriverWithDifferOutput, err error)
// ApplyDiffFromStagingDirectory applies the changes using the specified staging directory.
ApplyDiffFromStagingDirectory(id, parent, stagingDirectory string, diffOutput *DriverWithDifferOutput, options *ApplyDiffOpts) error
// CleanupStagingDirectory cleanups the staging directory. It can be used to cleanup the staging directory on errors
CleanupStagingDirectory(stagingDirectory string) error
// DifferTarget gets the location where files are stored for the layer.
DifferTarget(id string) (string, error)
}

// Capabilities defines a list of capabilities a driver may implement.
// These capabilities are not required; however, they do determine how a
// graphdriver can be used.
Expand Down
74 changes: 74 additions & 0 deletions drivers/overlay/overlay.go
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,7 @@ func (d *Driver) Metadata(id string) (map[string]string, error) {
// is being shutdown. For now, we just have to unmount the bind mounted
// we had created.
func (d *Driver) Cleanup() error {
_ = os.RemoveAll(d.getStagingDir())
return mount.Unmount(d.home)
}

Expand Down Expand Up @@ -1494,6 +1495,10 @@ func (f fileGetNilCloser) Close() error {
return nil
}

func (d *Driver) getStagingDir() string {
return filepath.Join(d.home, "staging")
}

// 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) {
Expand All @@ -1504,6 +1509,75 @@ func (d *Driver) DiffGetter(id string) (graphdriver.FileGetCloser, error) {
return fileGetNilCloser{storage.NewPathFileGetter(p)}, nil
}

// CleanupStagingDirectory cleanups the staging directory.
func (d *Driver) CleanupStagingDirectory(stagingDirectory string) error {
return os.RemoveAll(stagingDirectory)
}

// ApplyDiff applies the changes in the new layer using the specified function
func (d *Driver) ApplyDiffWithDiffer(id, parent string, options *graphdriver.ApplyDiffOpts, differ graphdriver.Differ) (output graphdriver.DriverWithDifferOutput, err error) {
var idMappings *idtools.IDMappings
if options != nil {
idMappings = options.Mappings
}
if idMappings == nil {
idMappings = &idtools.IDMappings{}
}

applyDir := ""

if id == "" {
err := os.MkdirAll(d.getStagingDir(), 0700)
if err != nil && !os.IsExist(err) {
return graphdriver.DriverWithDifferOutput{}, err
}
applyDir, err = ioutil.TempDir(d.getStagingDir(), "")
if err != nil {
return graphdriver.DriverWithDifferOutput{}, err
}

} else {
var err error
applyDir, err = d.getDiffPath(id)
if err != nil {
return graphdriver.DriverWithDifferOutput{}, err
}
}

logrus.Debugf("Applying differ in %s", applyDir)

out, err := differ.ApplyDiff(applyDir, &archive.TarOptions{
UIDMaps: idMappings.UIDs(),
GIDMaps: idMappings.GIDs(),
IgnoreChownErrors: d.options.ignoreChownErrors,
WhiteoutFormat: d.getWhiteoutFormat(),
InUserNS: rsystem.RunningInUserNS(),
})
out.Target = applyDir
return out, err
}

// ApplyDiffFromStagingDirectory applies the changes using the specified staging directory.
func (d *Driver) ApplyDiffFromStagingDirectory(id, parent, stagingDirectory string, diffOutput *graphdriver.DriverWithDifferOutput, options *graphdriver.ApplyDiffOpts) error {
if filepath.Dir(stagingDirectory) != d.getStagingDir() {
return fmt.Errorf("%q is not a staging directory", stagingDirectory)
}

diff, err := d.getDiffPath(id)
if err != nil {
return err
}
if err := os.RemoveAll(diff); err != nil && !os.IsNotExist(err) {
return err
}
return os.Rename(stagingDirectory, diff)
}

// DifferTarget gets the location where files are stored for the layer.
func (d *Driver) DifferTarget(id string) (string, error) {
return d.getDiffPath(id)
}

// ApplyDiff applies the new layer into a root
func (d *Driver) ApplyDiff(id, parent string, options graphdriver.ApplyDiffOpts) (size int64, err error) {

Expand Down
2 changes: 2 additions & 0 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,6 @@ var (
ErrSizeUnknown = types.ErrSizeUnknown
// ErrStoreIsReadOnly is returned when the caller makes a call to a read-only store that would require modifying its contents.
ErrStoreIsReadOnly = types.ErrStoreIsReadOnly
// ErrNotSupported is returned when the requested functionality is not supported.
ErrNotSupported = types.ErrNotSupported
)
100 changes: 100 additions & 0 deletions layers.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,19 @@ type LayerStore interface {
// applies its changes to a specified layer.
ApplyDiff(to string, diff io.Reader) (int64, error)

// ApplyDiffWithDiffer applies the changes through the differ callback function.
// If to is the empty string, then a staging directory is created by the driver.
ApplyDiffWithDiffer(to string, options *drivers.ApplyDiffOpts, differ drivers.Differ) (*drivers.DriverWithDifferOutput, error)

// CleanupStagingDirectory cleanups the staging directory. It can be used to cleanup the staging directory on errors
CleanupStagingDirectory(stagingDirectory string) error

// ApplyDiffFromStagingDirectory uses stagingDirectory to create the diff.
ApplyDiffFromStagingDirectory(id, stagingDirectory string, diffOutput *drivers.DriverWithDifferOutput, options *drivers.ApplyDiffOpts) error

// DifferTarget gets the location where files are stored for the layer.
DifferTarget(id string) (string, error)

// LoadLocked wraps Load in a locked state. This means it loads the store
// and cleans-up invalid layers if needed.
LoadLocked() error
Expand Down Expand Up @@ -1554,6 +1567,93 @@ func (r *layerStore) ApplyDiff(to string, diff io.Reader) (size int64, err error
return size, err
}

func (r *layerStore) DifferTarget(id string) (string, error) {
ddriver, ok := r.driver.(drivers.DriverWithDiffer)
if !ok {
return "", ErrNotSupported
}
layer, ok := r.lookup(id)
if !ok {
return "", ErrLayerUnknown
}
return ddriver.DifferTarget(layer.ID)
}

func (r *layerStore) ApplyDiffFromStagingDirectory(id, stagingDirectory string, diffOutput *drivers.DriverWithDifferOutput, options *drivers.ApplyDiffOpts) error {
ddriver, ok := r.driver.(drivers.DriverWithDiffer)
if !ok {
return ErrNotSupported
}
layer, ok := r.lookup(id)
if !ok {
return ErrLayerUnknown
}
if options == nil {
options = &drivers.ApplyDiffOpts{
Mappings: r.layerMappings(layer),
MountLabel: layer.MountLabel,
}
}
err := ddriver.ApplyDiffFromStagingDirectory(layer.ID, layer.Parent, stagingDirectory, diffOutput, options)
if err != nil {
return err
}
layer.UIDs = diffOutput.UIDs
layer.GIDs = diffOutput.GIDs
layer.UncompressedDigest = diffOutput.UncompressedDigest
layer.UncompressedSize = diffOutput.Size
layer.Metadata = diffOutput.Metadata
if err = r.Save(); err != nil {
return err
}
for k, v := range diffOutput.BigData {
if err := r.SetBigData(id, k, bytes.NewReader(v)); err != nil {
r.Delete(id)
return err
}
}
return err
}

func (r *layerStore) ApplyDiffWithDiffer(to string, options *drivers.ApplyDiffOpts, differ drivers.Differ) (*drivers.DriverWithDifferOutput, error) {
ddriver, ok := r.driver.(drivers.DriverWithDiffer)
if !ok {
return nil, ErrNotSupported
}

if to == "" {
output, err := ddriver.ApplyDiffWithDiffer("", "", options, differ)
return &output, err
}

layer, ok := r.lookup(to)
if !ok {
return nil, ErrLayerUnknown
}
if options == nil {
options = &drivers.ApplyDiffOpts{
Mappings: r.layerMappings(layer),
MountLabel: layer.MountLabel,
}
}
output, err := ddriver.ApplyDiffWithDiffer(layer.ID, layer.Parent, options, differ)
if err != nil {
return nil, err
}
layer.UIDs = output.UIDs
layer.GIDs = output.GIDs
err = r.Save()
return &output, err
}

func (r *layerStore) CleanupStagingDirectory(stagingDirectory string) error {
ddriver, ok := r.driver.(drivers.DriverWithDiffer)
if !ok {
return ErrNotSupported
}
return ddriver.CleanupStagingDirectory(stagingDirectory)
}

func (r *layerStore) layersByDigestMap(m map[digest.Digest][]string, d digest.Digest) ([]Layer, error) {
var layers []Layer
for _, layerID := range m[d] {
Expand Down
Loading

0 comments on commit 4f69205

Please sign in to comment.