podman/libpod/diff.go
Adrian Reber d5f1caaf50
Add function to get a filtered tarstream diff
The newly added function GetDiffTarStream() mirrors the GetDiff()
function. It tries to get the correct layer ID from getLayerID()
and it filters out containerMounts from the tarstream. Thus the
behavior is the same as GetDiff(), but it returns a tarstream.

This also adds the function ApplyDiffTarStream() to apply the tarstream
generated by GetDiffTarStream().

These functions are targeted to support container migration with
root file-system changes.

Signed-off-by: Adrian Reber <areber@redhat.com>
2019-07-11 14:43:34 +02:00

124 lines
3.4 KiB
Go

package libpod
import (
"archive/tar"
"io"
"github.com/containers/libpod/libpod/layers"
"github.com/containers/storage/pkg/archive"
"github.com/pkg/errors"
)
var containerMounts = map[string]bool{
"/dev": true,
"/etc/hostname": true,
"/etc/hosts": true,
"/etc/resolv.conf": true,
"/proc": true,
"/run": true,
"/run/.containerenv": true,
"/run/secrets": true,
"/sys": true,
}
// GetDiff returns the differences between the two images, layers, or containers
func (r *Runtime) GetDiff(from, to string) ([]archive.Change, error) {
toLayer, err := r.getLayerID(to)
if err != nil {
return nil, err
}
fromLayer := ""
if from != "" {
fromLayer, err = r.getLayerID(from)
if err != nil {
return nil, err
}
}
var rchanges []archive.Change
changes, err := r.store.Changes(fromLayer, toLayer)
if err == nil {
for _, c := range changes {
if containerMounts[c.Path] {
continue
}
rchanges = append(rchanges, c)
}
}
return rchanges, err
}
// skipFileInTarAchive is an archive.TarModifierFunc function
// which tells archive.ReplaceFileTarWrapper to skip files
// from the tarstream
func skipFileInTarAchive(path string, header *tar.Header, content io.Reader) (*tar.Header, []byte, error) {
return nil, nil, nil
}
// GetDiffTarStream returns the differences between the two images, layers, or containers.
// It is the same functionality as GetDiff() except that it returns a tarstream
func (r *Runtime) GetDiffTarStream(from, to string) (io.ReadCloser, error) {
toLayer, err := r.getLayerID(to)
if err != nil {
return nil, err
}
fromLayer := ""
if from != "" {
fromLayer, err = r.getLayerID(from)
if err != nil {
return nil, err
}
}
rc, err := r.store.Diff(fromLayer, toLayer, nil)
if err != nil {
return nil, err
}
// Skip files in the tar archive which are listed
// in containerMounts map. Just as in the GetDiff()
// function from above
filterMap := make(map[string]archive.TarModifierFunc)
for key := range containerMounts {
filterMap[key[1:]] = skipFileInTarAchive
// In the tarstream directories always include a trailing '/'.
// For simplicity this duplicates every entry from
// containerMounts with a trailing '/', as containerMounts
// does not use trailing '/' for directories.
filterMap[key[1:]+"/"] = skipFileInTarAchive
}
filteredTarStream := archive.ReplaceFileTarWrapper(rc, filterMap)
return filteredTarStream, nil
}
// ApplyDiffTarStream applies the changes stored in 'diff' to the layer 'to'
func (r *Runtime) ApplyDiffTarStream(to string, diff io.Reader) error {
toLayer, err := r.getLayerID(to)
if err != nil {
return err
}
_, err = r.store.ApplyDiff(toLayer, diff)
return err
}
// GetLayerID gets a full layer id given a full or partial id
// If the id matches a container or image, the id of the top layer is returned
// If the id matches a layer, the top layer id is returned
func (r *Runtime) getLayerID(id string) (string, error) {
var toLayer string
toImage, err := r.imageRuntime.NewFromLocal(id)
if err != nil {
toCtr, err := r.store.Container(id)
if err != nil {
toLayer, err = layers.FullID(r.store, id)
if err != nil {
return "", errors.Errorf("layer, image, or container %s does not exist", id)
}
} else {
toLayer = toCtr.LayerID
}
} else {
toLayer = toImage.TopLayer()
}
return toLayer, nil
}