190 lines
3.7 KiB
Go
190 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()
|
||
|
}
|