From 4cb287aa6c183911526e7433b29563efa6244785 Mon Sep 17 00:00:00 2001 From: Joe Groocock Date: Tue, 24 Nov 2020 21:25:36 +0000 Subject: [PATCH 1/2] lang: Allow 'mgmt lang run metadata.yaml' Handle the case of passing 'metadata.yaml' as an input source to the input selector and parse the relative path to the file the same way as it would be handled if it were a relative path with a directory component, or an absolute path. Signed-off-by: Joe Groocock --- lang/inputs/inputs.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lang/inputs/inputs.go b/lang/inputs/inputs.go index bd22cddce..c50580578 100644 --- a/lang/inputs/inputs.go +++ b/lang/inputs/inputs.go @@ -160,7 +160,8 @@ func inputStdin(s string, fs engine.Fs) (*ParsedInput, error) { // inputMetadata checks to see if we have a metadata file path. func inputMetadata(s string, fs engine.Fs) (*ParsedInput, error) { // we've got a metadata.yaml file - if !strings.HasSuffix(s, "/"+interfaces.MetadataFilename) { + if !(s == interfaces.MetadataFilename || + strings.HasSuffix(s, "/"+interfaces.MetadataFilename)) { return nil, nil // not us, but no error } var err error From 5d3204446b88de749fccfd002e7f504d1f86048e Mon Sep 17 00:00:00 2001 From: Joe Groocock Date: Tue, 23 Nov 2021 14:11:15 +0000 Subject: [PATCH 2/2] lang: Correctly handle stat(2) errors on directories/files Differentiate between ENOENT and other errors returned by stat(2) to prevent confusing parse errors when the path provided is a directory path. ENOENT is silently ignored as file or directory absence can be expected depending on the specified input from the user. Handle directory paths without trailing slashes from the user input on the command line, correctly disambiguating directories from files with the stat(2) result. This fixes the rather confusing situation where mgmt would report a parse error on the input string of the directory name as if it didn't exist, but instead stat(2) just returned another error such as EACCES or otherwise. Now correctly report errors that aren't ENOENT. Before: $ mgmt run lang files 2020-11-24 20:48:33.350321 I | cli: lang: input from code source: files 2020-11-24 20:48:33.350389 I | cli: lang: lexing/parsing... 2020-11-24 20:48:33.350528 I | run: error: cli parse error: could not generate AST: parser: `syntax error: unexpected $end` @1:1 After: $ mgmt run lang files 2020-11-24 20:53:35.500436 I | run: error: cli parse error: could not activate an input parser: stat /home/frebib/mgmt/files/metadata.yaml: permission denied Signed-off-by: Joe Groocock --- lang/inputs/inputs.go | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/lang/inputs/inputs.go b/lang/inputs/inputs.go index c50580578..d6b9d8b79 100644 --- a/lang/inputs/inputs.go +++ b/lang/inputs/inputs.go @@ -24,9 +24,11 @@ package inputs import ( + "errors" "fmt" "io/ioutil" "os" + "path" "path/filepath" "strings" @@ -205,6 +207,7 @@ func inputMetadata(s string, fs engine.Fs) (*ParsedInput, error) { // real files/ directory if metadata.Files != "" { // TODO: nil pointer instead? filesDir := basePath + metadata.Files + // TODO: handle files that throw stat(2) errors if _, err := fs.Stat(filesDir); err == nil { files = append(files, filesDir) } @@ -274,25 +277,31 @@ func inputMcl(s string, fs engine.Fs) (*ParsedInput, error) { // inputDirectory checks if we're given the path to a directory. func inputDirectory(s string, fs engine.Fs) (*ParsedInput, error) { - if !strings.HasSuffix(s, "/") { - return nil, nil // not us, but no error - } - var err error - if s, err = absify(s); err != nil { // s is now absolute - return nil, err - } // does dir exist? fi, err := fs.Stat(s) if err != nil { - return nil, errwrap.Wrapf(err, "dir: `%s` does not exist", s) + if errors.Is(err, os.ErrNotExist) { + return nil, nil // not us, but no error + } + + return nil, errwrap.Wrapf(err, "could not stat dir `%s`", s) } if !fi.IsDir() { return nil, errwrap.Wrapf(err, "dir: `%s` is not a dir", s) } + if s, err = absify(s); err != nil { // s is now absolute + return nil, err + } + // try looking for a metadata file in the root - md := s + interfaces.MetadataFilename // absolute file - if _, err := fs.Stat(md); err == nil { + md := path.Join(s, interfaces.MetadataFilename) // absolute file + _, err = fs.Stat(md) + // only return errors if they're not ENOENT, e.g. EPERM, EACCES + // ignore ENOENT errors and assume that the file is deliberately absent + if err != nil && !errors.Is(err, os.ErrNotExist) { + return nil, err + } else if err == nil { if x, err := inputMetadata(md, fs); err != nil { // recurse return nil, err } else if x != nil { @@ -301,8 +310,12 @@ func inputDirectory(s string, fs engine.Fs) (*ParsedInput, error) { } // try looking for a main.mcl file in the root - mf := s + interfaces.MainFilename // absolute file - if _, err := fs.Stat(mf); err == nil { + mf := path.Join(s, interfaces.MainFilename) // absolute file + _, err = fs.Stat(mf) + // same error handling as above. ignore ENOENT and handle all others + if err != nil && !errors.Is(err, os.ErrNotExist) { + return nil, err + } else if err == nil { if x, err := inputMcl(mf, fs); err != nil { // recurse return nil, err } else if x != nil {