Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable zstd:chunked support in containers/image #775

Merged
merged 4 commits into from
May 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions drivers/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,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 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m mostly unable to guess the data held in the individual fields (and if I’m not missing anything, most are not actually used). Either way, could they be documented, both as to purpose (“the result of $function” is not a purpose) and values/meaning?

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 @@ -1490,6 +1491,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 @@ -1500,6 +1505,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(), "")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How are these cleaned up if the pull is abruptly killed?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

they are cleaned up at shutdown, so if anything is left in the staging directory it will be cleaned up at some point.

It could still be a problem for long running processes like CRI-O though.

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 @@ -1553,6 +1566,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