mirror of
https://github.com/vbatts/go-mtree.git
synced 2024-09-28 13:19:47 +00:00
Merge 2cdb8c6930
into e6bdb36de3
This commit is contained in:
commit
a5980f2cb8
2 changed files with 174 additions and 15 deletions
181
check.go
181
check.go
|
@ -1,6 +1,7 @@
|
|||
package mtree
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
@ -13,17 +14,141 @@ type Result struct {
|
|||
Failures []Failure `json:"failures"`
|
||||
}
|
||||
|
||||
// Failure of a particular keyword for a path
|
||||
type Failure struct {
|
||||
Path string `json:"path"`
|
||||
Keyword string `json:"keyword"`
|
||||
Expected string `json:"expected"`
|
||||
Got string `json:"got"`
|
||||
// FailureType represents a type of Failure encountered when checking for
|
||||
// discrepancies between a manifest and a directory state.
|
||||
type FailureType string
|
||||
|
||||
const (
|
||||
// None means no discrepancy (unused).
|
||||
None FailureType = "none"
|
||||
|
||||
// Missing represents a discrepancy where an object is present in the
|
||||
// manifest but is not present in the directory state being checked.
|
||||
Missing FailureType = "missing"
|
||||
|
||||
// Modified represents a discrepancy where one or more of the keywords
|
||||
// present in the manifest do not match the keywords generated for the same
|
||||
// object in the directory state being checked.
|
||||
Modified FailureType = "modified"
|
||||
|
||||
// Present represents a discrepancy where an object is not present in the
|
||||
// manifest but is present in the directory state being checked.
|
||||
Present FailureType = "present"
|
||||
)
|
||||
|
||||
// Failure represents a discrepancy between a manifest and the state it is
|
||||
// being compared against. The discrepancy may take the form of a missing,
|
||||
// erroneous or otherwise modified object.
|
||||
type Failure interface {
|
||||
// String returns a "pretty" formatting for a Failure. It's based on the
|
||||
// BSD mtree(8) format, so that we are compatible.
|
||||
String() string
|
||||
|
||||
// MarshalJSON ensures that we fulfil the JSON Marshaler interface.
|
||||
MarshalJSON() ([]byte, error)
|
||||
|
||||
// Path returns the path (relative to the root of the tree) that this
|
||||
// discrepancy refers to.
|
||||
Path() string
|
||||
|
||||
// Type returns the type of failure that occurred.
|
||||
Type() FailureType
|
||||
}
|
||||
|
||||
// String returns a "pretty" formatting for a Failure
|
||||
func (f Failure) String() string {
|
||||
return fmt.Sprintf("%q: keyword %q: expected %s; got %s", f.Path, f.Keyword, f.Expected, f.Got)
|
||||
// An object modified from the manifest state.
|
||||
type modified struct {
|
||||
path string
|
||||
keyword string
|
||||
expected string
|
||||
got string
|
||||
}
|
||||
|
||||
func (m modified) String() string {
|
||||
return fmt.Sprintf("%q: keyword %q: expected %s; got %s", m.path, m.keyword, m.expected, m.got)
|
||||
}
|
||||
|
||||
func (m modified) MarshalJSON() ([]byte, error) {
|
||||
// Because of Go's reflection policies, we have to make an anonymous struct
|
||||
// with the fields exported.
|
||||
return json.Marshal(struct {
|
||||
Type FailureType `json:"type"`
|
||||
Path string `json:"path"`
|
||||
Keyword string `json:"keyword"`
|
||||
Expected string `json:"expected"`
|
||||
Got string `json:"got"`
|
||||
}{
|
||||
Type: m.Type(),
|
||||
Path: m.path,
|
||||
Keyword: m.keyword,
|
||||
Expected: m.expected,
|
||||
Got: m.got,
|
||||
})
|
||||
}
|
||||
|
||||
func (m modified) Path() string {
|
||||
return m.path
|
||||
}
|
||||
|
||||
func (m modified) Type() FailureType {
|
||||
return Modified
|
||||
}
|
||||
|
||||
// An object that is listed in the manifest but is not present in the state.
|
||||
type missing struct {
|
||||
path string
|
||||
}
|
||||
|
||||
func (m missing) String() string {
|
||||
return fmt.Sprintf("%q: expected object missing", m.path)
|
||||
}
|
||||
|
||||
func (m missing) MarshalJSON() ([]byte, error) {
|
||||
// Because of Go's reflection policies, we have to make an anonymous struct
|
||||
// with the fields exported.
|
||||
return json.Marshal(struct {
|
||||
Type FailureType `json:"type"`
|
||||
Path string `json:"path"`
|
||||
}{
|
||||
Type: m.Type(),
|
||||
Path: m.path,
|
||||
})
|
||||
}
|
||||
|
||||
func (m missing) Path() string {
|
||||
return m.path
|
||||
}
|
||||
|
||||
func (m missing) Type() FailureType {
|
||||
return Missing
|
||||
}
|
||||
|
||||
// An object that is not listed in the manifest but is present in the state.
|
||||
type present struct {
|
||||
path string
|
||||
}
|
||||
|
||||
func (p present) String() string {
|
||||
return fmt.Sprintf("%q: unexpected object present", p.path)
|
||||
}
|
||||
|
||||
func (p present) MarshalJSON() ([]byte, error) {
|
||||
// Because of Go's reflection policies, we have to make an anonymous struct
|
||||
// with the fields exported.
|
||||
return json.Marshal(struct {
|
||||
Type FailureType `json:"type"`
|
||||
Path string `json:"path"`
|
||||
}{
|
||||
Type: p.Type(),
|
||||
Path: p.path,
|
||||
})
|
||||
}
|
||||
|
||||
func (p present) Path() string {
|
||||
return p.path
|
||||
}
|
||||
|
||||
func (p present) Type() FailureType {
|
||||
return Missing
|
||||
}
|
||||
|
||||
// Check a root directory path against the DirectoryHierarchy, regarding only
|
||||
|
@ -42,6 +167,9 @@ func Check(root string, dh *DirectoryHierarchy, keywords []string) (*Result, err
|
|||
sort.Sort(byPos(creator.DH.Entries))
|
||||
|
||||
var result Result
|
||||
seen := map[string]struct{}{
|
||||
".": struct{}{},
|
||||
}
|
||||
for i, e := range creator.DH.Entries {
|
||||
switch e.Type {
|
||||
case SpecialType:
|
||||
|
@ -53,9 +181,17 @@ func Check(root string, dh *DirectoryHierarchy, keywords []string) (*Result, err
|
|||
case RelativeType, FullType:
|
||||
info, err := os.Lstat(e.Path())
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
result.Failures = append(result.Failures, missing{
|
||||
path: e.Path(),
|
||||
})
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
seen[e.Path()] = struct{}{}
|
||||
|
||||
var kvs KeyVals
|
||||
if creator.curSet != nil {
|
||||
kvs = MergeSet(creator.curSet.Keywords, e.Keywords)
|
||||
|
@ -76,11 +212,34 @@ func Check(root string, dh *DirectoryHierarchy, keywords []string) (*Result, err
|
|||
return nil, err
|
||||
}
|
||||
if string(kv) != curKeyVal {
|
||||
failure := Failure{Path: e.Path(), Keyword: kv.Keyword(), Expected: kv.Value(), Got: KeyVal(curKeyVal).Value()}
|
||||
result.Failures = append(result.Failures, failure)
|
||||
result.Failures = append(result.Failures, modified{
|
||||
path: e.Path(),
|
||||
keyword: kv.Keyword(),
|
||||
expected: kv.Value(),
|
||||
got: KeyVal(curKeyVal).Value(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// To find any paths we haven't seen already.
|
||||
if err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
|
||||
/* We shouldn't get not-found errors. */
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, ok := seen[path]; !ok {
|
||||
result.Failures = append(result.Failures, present{
|
||||
path: path,
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, fmt.Errorf("Unexpected error ocurred while walking directory: %s", err)
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ var (
|
|||
flAddKeywords = flag.String("K", "", "Add the specified (delimited by comma or space) keywords to the current set of keywords")
|
||||
flUseKeywords = flag.String("k", "", "Use the specified (delimited by comma or space) keywords as the current set of keywords")
|
||||
flListKeywords = flag.Bool("list-keywords", false, "List the keywords available")
|
||||
flResultFormat = flag.String("result-format", "bsd", "output the validation results using the given format (bsd, json, path)")
|
||||
flResultFormat = flag.String("result-format", "bsd", "output the validation results using the given format (bsd, json, text)")
|
||||
)
|
||||
|
||||
var formats = map[string]func(*mtree.Result) string{
|
||||
|
@ -41,11 +41,11 @@ var formats = map[string]func(*mtree.Result) string{
|
|||
return buffer.String()
|
||||
},
|
||||
|
||||
// Outputs only the paths which failed to validate.
|
||||
"path": func(r *mtree.Result) string {
|
||||
// Outputs only the failure type and paths which failed to validate.
|
||||
"text": func(r *mtree.Result) string {
|
||||
var buffer bytes.Buffer
|
||||
for _, fail := range r.Failures {
|
||||
fmt.Fprintln(&buffer, fail.Path)
|
||||
fmt.Fprintf(&buffer, "%s\t%s\n", fail.Type(), fail.Path())
|
||||
}
|
||||
return buffer.String()
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue