diff --git a/context.go b/context.go index 9dd68b4..4b9ea72 100644 --- a/context.go +++ b/context.go @@ -19,6 +19,7 @@ import ( "errors" "fmt" "io" + "os" "path/filepath" "reflect" "runtime" @@ -793,6 +794,23 @@ func (c *Context) parseBlueprintsFile(filename string, scope *parser.Scope, root f, err := c.fs.Open(filename) if err != nil { + // couldn't open the file; see if we can provide a clearer error than "could not open file" + stats, statErr := c.fs.Lstat(filename) + if statErr == nil { + isSymlink := stats.Mode()&os.ModeSymlink != 0 + if isSymlink { + err = fmt.Errorf("could not open symlink %v : %v", filename, err) + target, readlinkErr := os.Readlink(filename) + if readlinkErr == nil { + _, targetStatsErr := c.fs.Lstat(target) + if targetStatsErr != nil { + err = fmt.Errorf("could not open symlink %v; its target (%v) cannot be opened", filename, target) + } + } + } else { + err = fmt.Errorf("%v exists but could not be opened: %v", filename, err) + } + } errsCh <- []error{err} return } diff --git a/pathtools/fs.go b/pathtools/fs.go index 4f98c6e..60d73b3 100644 --- a/pathtools/fs.go +++ b/pathtools/fs.go @@ -16,6 +16,7 @@ package pathtools import ( "bytes" + "errors" "fmt" "io" "io/ioutil" @@ -61,6 +62,7 @@ type FileSystem interface { Glob(pattern string, excludes []string) (matches, dirs []string, err error) glob(pattern string) (matches []string, err error) IsDir(name string) (bool, error) + Lstat(name string) (os.FileInfo, error) } // osFs implements FileSystem using the local disk. @@ -94,6 +96,10 @@ func (osFs) glob(pattern string) ([]string, error) { return filepath.Glob(pattern) } +func (osFs) Lstat(path string) (stats os.FileInfo, err error) { + return os.Lstat(path) +} + type mockFs struct { files map[string][]byte dirs map[string]bool @@ -150,3 +156,7 @@ func (m *mockFs) glob(pattern string) ([]string, error) { } return matches, nil } + +func (m *mockFs) Lstat(path string) (stats os.FileInfo, err error) { + return nil, errors.New("Lstat is not yet implemented in MockFs") +}