containerd/fs/fstest/continuity_util.go
Derek McGowan 574862fd89 Add fs package
Add diff comparison with support for double walking
two trees for comparison or single walking a diff
tree. Single walking requires further implementation
for specific mount types.

Add directory copy function which is intended to provide
fastest possible local copy of file system directories
without hardlinking.

Add test package to make creating filesystems for
test easy and comparisons deep and informative.

Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
2017-02-03 11:27:40 -08:00

189 lines
3.7 KiB
Go

package fstest
import (
"bytes"
"fmt"
"github.com/stevvooe/continuity"
)
type resourceUpdate struct {
Original continuity.Resource
Updated continuity.Resource
}
func (u resourceUpdate) String() string {
return fmt.Sprintf("%s(mode: %o, uid: %s, gid: %s) -> %s(mode: %o, uid: %s, gid: %s)",
u.Original.Path(), u.Original.Mode(), u.Original.UID(), u.Original.GID(),
u.Updated.Path(), u.Updated.Mode(), u.Updated.UID(), u.Updated.GID(),
)
}
type resourceListDifference struct {
Additions []continuity.Resource
Deletions []continuity.Resource
Updates []resourceUpdate
}
func (l resourceListDifference) HasDiff() bool {
return len(l.Additions) > 0 || len(l.Deletions) > 0 || len(l.Updates) > 0
}
func (l resourceListDifference) String() string {
buf := bytes.NewBuffer(nil)
for _, add := range l.Additions {
fmt.Fprintf(buf, "+ %s\n", add.Path())
}
for _, del := range l.Deletions {
fmt.Fprintf(buf, "- %s\n", del.Path())
}
for _, upt := range l.Updates {
fmt.Fprintf(buf, "~ %s\n", upt.String())
}
return string(buf.Bytes())
}
// diffManifest compares two resource lists and returns the list
// of adds updates and deletes, resource lists are not reordered
// before doing difference.
func diffResourceList(r1, r2 []continuity.Resource) resourceListDifference {
i1 := 0
i2 := 0
var d resourceListDifference
for i1 < len(r1) && i2 < len(r2) {
p1 := r1[i1].Path()
p2 := r2[i2].Path()
switch {
case p1 < p2:
d.Deletions = append(d.Deletions, r1[i1])
i1++
case p1 == p2:
if !compareResource(r1[i1], r2[i2]) {
d.Updates = append(d.Updates, resourceUpdate{
Original: r1[i1],
Updated: r2[i2],
})
}
i1++
i2++
case p1 > p2:
d.Additions = append(d.Additions, r2[i2])
i2++
}
}
for i1 < len(r1) {
d.Deletions = append(d.Deletions, r1[i1])
i1++
}
for i2 < len(r2) {
d.Additions = append(d.Additions, r2[i2])
i2++
}
return d
}
func compareResource(r1, r2 continuity.Resource) bool {
if r1.Path() != r2.Path() {
return false
}
if r1.Mode() != r2.Mode() {
return false
}
if r1.UID() != r2.UID() {
return false
}
if r1.GID() != r2.GID() {
return false
}
// TODO(dmcgowan): Check if is XAttrer
return compareResourceTypes(r1, r2)
}
func compareResourceTypes(r1, r2 continuity.Resource) bool {
switch t1 := r1.(type) {
case continuity.RegularFile:
t2, ok := r2.(continuity.RegularFile)
if !ok {
return false
}
return compareRegularFile(t1, t2)
case continuity.Directory:
t2, ok := r2.(continuity.Directory)
if !ok {
return false
}
return compareDirectory(t1, t2)
case continuity.SymLink:
t2, ok := r2.(continuity.SymLink)
if !ok {
return false
}
return compareSymLink(t1, t2)
case continuity.NamedPipe:
t2, ok := r2.(continuity.NamedPipe)
if !ok {
return false
}
return compareNamedPipe(t1, t2)
case continuity.Device:
t2, ok := r2.(continuity.Device)
if !ok {
return false
}
return compareDevice(t1, t2)
default:
// TODO(dmcgowan): Should this panic?
return r1 == r2
}
}
func compareRegularFile(r1, r2 continuity.RegularFile) bool {
if r1.Size() != r2.Size() {
return false
}
p1 := r1.Paths()
p2 := r2.Paths()
if len(p1) != len(p2) {
return false
}
for i := range p1 {
if p1[i] != p2[i] {
return false
}
}
d1 := r1.Digests()
d2 := r2.Digests()
if len(d1) != len(d2) {
return false
}
for i := range d1 {
if d1[i] != d2[i] {
return false
}
}
return true
}
func compareSymLink(r1, r2 continuity.SymLink) bool {
return r1.Target() == r2.Target()
}
func compareDirectory(r1, r2 continuity.Directory) bool {
return true
}
func compareNamedPipe(r1, r2 continuity.NamedPipe) bool {
return true
}
func compareDevice(r1, r2 continuity.Device) bool {
return r1.Major() == r2.Major() && r1.Minor() == r2.Minor()
}