From cb664684fbfccdc9e15c2a25c4358899461d225a Mon Sep 17 00:00:00 2001 From: David Pordomingo Date: Wed, 24 Jul 2019 15:11:50 +0200 Subject: [PATCH 1/6] Make sure that ~/.sourced dir is ready for all sourced commands Signed-off-by: David Pordomingo --- cmd/sourced/cmd/root.go | 31 +++++++++++++++----- cmd/sourced/dir/dir.go | 64 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 83 insertions(+), 12 deletions(-) diff --git a/cmd/sourced/cmd/root.go b/cmd/sourced/cmd/root.go index 9899fb8..40273a3 100644 --- a/cmd/sourced/cmd/root.go +++ b/cmd/sourced/cmd/root.go @@ -42,14 +42,31 @@ type Command struct { // Execute adds all child commands to the root command and sets flags appropriately. // This is called by main.main(). It only needs to happen once to the rootCmd. func Execute() { - if err := rootCmd.Run(os.Args); err != nil { - if workdir.ErrMalformed.Is(err) || dir.ErrNotExist.Is(err) { - fmt.Println(format.Colorize( - format.Red, - `Cannot perform this action, source{d} needs to be initialized first with the 'init' sub command`, - )) - } + if err := dir.Prepare(); err != nil { + fmt.Println(err) + log(err) + os.Exit(1) + } + if err := rootCmd.Run(os.Args); err != nil { + log(err) os.Exit(1) } } + +func log(err error) { + switch { + case workdir.ErrMalformed.Is(err) || dir.ErrNotExist.Is(err): + printRed("Cannot perform this action, source{d} needs to be initialized first with the 'init' sub command") + case dir.ErrNotValid.Is(err): + printRed("Cannot perform this action, config directory is not valid") + case fmt.Sprintf("%T", err) == "*flags.Error": + // syntax error is already logged by go-cli + default: + printRed("Unexpected error") + } +} + +func printRed(message string) { + fmt.Println(format.Colorize(format.Red, message)) +} diff --git a/cmd/sourced/dir/dir.go b/cmd/sourced/dir/dir.go index 7a94fb2..026510e 100644 --- a/cmd/sourced/dir/dir.go +++ b/cmd/sourced/dir/dir.go @@ -16,10 +16,31 @@ import ( // ErrNotExist is returned when .sourced dir does not exists var ErrNotExist = goerrors.NewKind("%s does not exist") +// ErrNotValid is returned when config dir is not valid +var ErrNotValid = goerrors.NewKind("%s is not a valid config directory: %s") + // Path returns the absolute path for $SOURCED_DIR, or $HOME/.sourced if unset func Path() (string, error) { + srcdDir, err := path() + if err != nil { + return "", err + } + + if err := validate(srcdDir); err != nil { + return "", err + } + + return srcdDir, nil +} + +func path() (string, error) { if d := os.Getenv("SOURCED_DIR"); d != "" { - return filepath.Abs(d) + abs, err := filepath.Abs(d) + if err != nil { + return "", errors.Wrap(err, fmt.Sprintf("could not resolve SOURCED_DIR='%s'", d)) + } + + return abs, nil } homedir, err := os.UserHomeDir() @@ -27,13 +48,46 @@ func Path() (string, error) { return "", errors.Wrap(err, "could not detect home directory") } - srcdDir := filepath.Join(homedir, ".sourced") - _, err = os.Lstat(srcdDir) + return filepath.Join(homedir, ".sourced"), nil +} + +// Prepare tries to create the config directory, returning an error if it could not +// be created, or nill if already exist or was successfully created. +func Prepare() error { + srcdDir, err := path() + if err != nil { + return err + } + + err = validate(srcdDir) + if ErrNotExist.Is(err) { + return os.MkdirAll(srcdDir, os.ModePerm) + } + + return err +} + +// validate validates that the passed config dir path is valid +func validate(path string) error { + info, err := os.Stat(path) if os.IsNotExist(err) { - return "", ErrNotExist.New(srcdDir) + return ErrNotExist.New(path) } - return srcdDir, nil + if err != nil { + return ErrNotValid.New(path, err) + } + + readWriteAccessMode := os.FileMode(0700) + if info.Mode()&readWriteAccessMode != readWriteAccessMode { + return ErrNotValid.New(path, "it has no read-write access") + } + + if !info.IsDir() { + return ErrNotValid.New(path, "it is not a directory") + } + + return nil } // DownloadURL downloads the given url to a file to the From bf3694e50c1b1dbb85f766105d339211b0c764c8 Mon Sep 17 00:00:00 2001 From: David Pordomingo Date: Wed, 24 Jul 2019 12:17:05 +0200 Subject: [PATCH 2/6] Return proper error if the user tried to run web before init Signed-off-by: David Pordomingo --- cmd/sourced/cmd/web.go | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/cmd/sourced/cmd/web.go b/cmd/sourced/cmd/web.go index 9a4ea1c..244b46a 100644 --- a/cmd/sourced/cmd/web.go +++ b/cmd/sourced/cmd/web.go @@ -13,6 +13,8 @@ import ( "github.com/pkg/browser" "github.com/pkg/errors" "github.com/src-d/sourced-ce/cmd/sourced/compose" + "github.com/src-d/sourced-ce/cmd/sourced/compose/workdir" + "github.com/src-d/sourced-ce/cmd/sourced/dir" ) // The service name used in docker-compose.yml for the srcd/sourced-ui image @@ -34,17 +36,23 @@ func openUI() error { var stdout bytes.Buffer // wait for the container to start, it can take a while in some cases for { - if err := compose.RunWithIO(context.Background(), - os.Stdin, &stdout, nil, "port", containerName, "8088"); err == nil { + err := compose.RunWithIO(context.Background(), + os.Stdin, &stdout, nil, "port", containerName, "8088") + + if err == nil { break } + if workdir.ErrMalformed.Is(err) || dir.ErrNotExist.Is(err) || dir.ErrNotValid.Is(err) { + return err + } + time.Sleep(1 * time.Second) } address := strings.TrimSpace(stdout.String()) if address == "" { - return fmt.Errorf("no address found") + return fmt.Errorf("could not find the public port of %s", containerName) } // docker-compose returns 0.0.0.0 which is correct for the bind address @@ -61,7 +69,7 @@ func openUI() error { } if err := browser.OpenURL(url); err != nil { - errors.Wrap(err, "cannot open browser") + return errors.Wrap(err, "could not open the browser") } return nil @@ -88,7 +96,7 @@ Once source{d} is fully initialized, the UI will be available, by default at: select { case err := <-ch: - return errors.Wrap(err, "an error occurred while opening the UI") + return err case <-time.After(timeout): return fmt.Errorf("error opening the UI, the container is not running after %v", timeout) } From 150d8ec744ef559f82819ab93aa709efdcdceedf Mon Sep 17 00:00:00 2001 From: David Pordomingo Date: Wed, 24 Jul 2019 12:19:26 +0200 Subject: [PATCH 3/6] Return error if workdir.Active failed Signed-off-by: David Pordomingo --- cmd/sourced/cmd/workdirs.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cmd/sourced/cmd/workdirs.go b/cmd/sourced/cmd/workdirs.go index dfd6afc..706033c 100644 --- a/cmd/sourced/cmd/workdirs.go +++ b/cmd/sourced/cmd/workdirs.go @@ -16,8 +16,11 @@ func (c *workdirsCmd) Execute(args []string) error { return err } - // ignore errors if active dir doesn't exist or unavailable - active, _ := workdir.Active() + active, err := workdir.Active() + if err != nil { + return err + } + for _, dir := range dirs { if dir == active { fmt.Printf("* %s\n", dir) From eb8d56d8aaf6248f781579f1fa135957cf0e0264 Mon Sep 17 00:00:00 2001 From: David Pordomingo Date: Wed, 24 Jul 2019 13:01:59 +0200 Subject: [PATCH 4/6] Return proper error if __active__ does not exist Signed-off-by: David Pordomingo --- cmd/sourced/compose/workdir/common.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/sourced/compose/workdir/common.go b/cmd/sourced/compose/workdir/common.go index 4014eab..94ce006 100644 --- a/cmd/sourced/compose/workdir/common.go +++ b/cmd/sourced/compose/workdir/common.go @@ -125,7 +125,7 @@ func ActivePath() (string, error) { resolvedPath, err := filepath.EvalSymlinks(path) if os.IsNotExist(err) { - return "", ErrMalformed.New("active", "not found") + return "", ErrMalformed.New("active", err) } return resolvedPath, err From b5dd8cc57fb524ac4ffd231d514d8b430617c483 Mon Sep 17 00:00:00 2001 From: David Pordomingo Date: Wed, 24 Jul 2019 13:03:57 +0200 Subject: [PATCH 5/6] Enhance func and package docs Signed-off-by: David Pordomingo --- cmd/sourced/compose/workdir/common.go | 4 ++-- cmd/sourced/dir/dir.go | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cmd/sourced/compose/workdir/common.go b/cmd/sourced/compose/workdir/common.go index 94ce006..4e4ab85 100644 --- a/cmd/sourced/compose/workdir/common.go +++ b/cmd/sourced/compose/workdir/common.go @@ -353,8 +353,8 @@ func workdirsPath() (string, error) { return filepath.Join(path, "workdirs"), nil } -// function takes workdirs root and absolute path to workdir -// return human-readable name +// decodeName takes workdirs root and absolute path to workdir +// return human-readable name. It returns an error if the path could not be built func decodeName(base, target string) (string, error) { p, err := filepath.Rel(base, target) if err != nil { diff --git a/cmd/sourced/dir/dir.go b/cmd/sourced/dir/dir.go index 026510e..6ab4608 100644 --- a/cmd/sourced/dir/dir.go +++ b/cmd/sourced/dir/dir.go @@ -1,5 +1,4 @@ -// Package dir provides functions to manage the $HOME/.sourced and /tmp/srcd -// directories +// Package dir provides functions to manage the config directories. package dir import ( @@ -13,13 +12,14 @@ import ( goerrors "gopkg.in/src-d/go-errors.v1" ) -// ErrNotExist is returned when .sourced dir does not exists +// ErrNotExist is returned when config dir does not exists var ErrNotExist = goerrors.NewKind("%s does not exist") // ErrNotValid is returned when config dir is not valid var ErrNotValid = goerrors.NewKind("%s is not a valid config directory: %s") // Path returns the absolute path for $SOURCED_DIR, or $HOME/.sourced if unset +// and returns an error if it does not exist or it could not be read. func Path() (string, error) { srcdDir, err := path() if err != nil { From 78f80cd7e51ff00f6ebdfeee3800c51085e656f8 Mon Sep 17 00:00:00 2001 From: David Pordomingo Date: Tue, 30 Jul 2019 12:47:10 +0200 Subject: [PATCH 6/6] SQUASH over Make sure that ~/.sourced dir is ready for all sourced commands Signed-off-by: David Pordomingo --- cmd/sourced/cmd/root.go | 2 +- cmd/sourced/dir/dir.go | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/cmd/sourced/cmd/root.go b/cmd/sourced/cmd/root.go index 40273a3..53f1a20 100644 --- a/cmd/sourced/cmd/root.go +++ b/cmd/sourced/cmd/root.go @@ -63,7 +63,7 @@ func log(err error) { case fmt.Sprintf("%T", err) == "*flags.Error": // syntax error is already logged by go-cli default: - printRed("Unexpected error") + // unknown errors have no special message } } diff --git a/cmd/sourced/dir/dir.go b/cmd/sourced/dir/dir.go index 6ab4608..639559e 100644 --- a/cmd/sourced/dir/dir.go +++ b/cmd/sourced/dir/dir.go @@ -61,7 +61,11 @@ func Prepare() error { err = validate(srcdDir) if ErrNotExist.Is(err) { - return os.MkdirAll(srcdDir, os.ModePerm) + if err := os.MkdirAll(srcdDir, os.ModePerm); err != nil { + return ErrNotValid.New(srcdDir, err) + } + + return nil } return err