diff --git a/cmd/executor.go b/cmd/executor.go index 68173ca..ef8014e 100644 --- a/cmd/executor.go +++ b/cmd/executor.go @@ -61,7 +61,7 @@ func setupEnvVars(env *cli.Environment) { env.AddVarWithDefault("log_level", "set logging level: debug | warn | error", "warn") } -func (e *Executor) commandPreRun() error { +func (e *Executor) commandPreRun(ctx context.Context) error { var skipLoadConfig, skipLoadPlugins, skipCheckConfig bool e.opts.env = e.v.GetString("env") @@ -91,7 +91,7 @@ func (e *Executor) commandPreRun() error { cfgPath := fileutil.FindYAMLGoingUp(pwd, config.ProjectYAMLName) - v, err := e.opts.valueOpts.MergeValues(cmd.Context(), filepath.Dir(cfgPath), getter.All()) + v, err := e.opts.valueOpts.MergeValues(ctx, filepath.Dir(cfgPath), getter.All()) if err != nil && (len(e.opts.valueOpts.ValueFiles) != 1 || e.opts.valueOpts.ValueFiles[0] != defValuesYAML) { return err } @@ -99,7 +99,7 @@ func (e *Executor) commandPreRun() error { vals := map[string]interface{}{"var": v} // Load config file. - if err := e.loadProjectConfig(cmd.Context(), cfgPath, vals, skipLoadPlugins, skipCheckConfig); err != nil && !errors.Is(err, config.ErrProjectConfigNotFound) { + if err := e.loadProjectConfig(ctx, cfgPath, vals, skipLoadPlugins, skipCheckConfig); err != nil && !errors.Is(err, config.ErrProjectConfigNotFound) { return err } @@ -153,7 +153,7 @@ func (e *Executor) Execute(ctx context.Context) error { return err } - if err := e.commandPreRun(); err != nil { + if err := e.commandPreRun(ctx); err != nil { return err } diff --git a/go.mod b/go.mod index 1741b42..b379d32 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/mapstructure v1.4.1 // indirect github.com/otiai10/copy v1.6.0 - github.com/outblocks/outblocks-plugin-go v0.0.0-20210827220856-7521c046d330 + github.com/outblocks/outblocks-plugin-go v0.0.0-20210831215844-947852a3f59a github.com/pelletier/go-toml v1.9.1 // indirect github.com/pterm/pterm v0.12.29 github.com/spf13/afero v1.6.0 // indirect diff --git a/go.sum b/go.sum index 53a4df5..f7175cc 100644 --- a/go.sum +++ b/go.sum @@ -296,8 +296,8 @@ github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6 github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= github.com/otiai10/mint v1.3.2 h1:VYWnrP5fXmz1MXvjuUvcBrXSjGE6xjON+axB/UrpO3E= github.com/otiai10/mint v1.3.2/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= -github.com/outblocks/outblocks-plugin-go v0.0.0-20210827220856-7521c046d330 h1:4gB4dw6qZ1+HoJeteJzoDGQptbmTI2ru3czTnNbjOyc= -github.com/outblocks/outblocks-plugin-go v0.0.0-20210827220856-7521c046d330/go.mod h1:vAn4Vv7fXTyrjNEvAVcKtKJ2Bwaqk3Oy63lqnBRIct4= +github.com/outblocks/outblocks-plugin-go v0.0.0-20210831215844-947852a3f59a h1:+9QqJsvrNYvkh8WhEj1ocgHz1MpBvbblrkCFVxrRCmk= +github.com/outblocks/outblocks-plugin-go v0.0.0-20210831215844-947852a3f59a/go.mod h1:50dr9Bbwu7d8RpOAIU4+lS+wTchPBuAmPlt/D9gRgWA= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.1 h1:a6qW1EVNZWH9WGI6CsYdD8WAylkoXBS5yv0XHlh17Tc= diff --git a/pkg/actions/run.go b/pkg/actions/run.go index 871497a..7da20b9 100644 --- a/pkg/actions/run.go +++ b/pkg/actions/run.go @@ -2,6 +2,7 @@ package actions import ( "context" + "errors" "fmt" "net/http" "net/http/httputil" @@ -22,6 +23,7 @@ import ( plugin_go "github.com/outblocks/outblocks-plugin-go" "github.com/outblocks/outblocks-plugin-go/types" plugin_util "github.com/outblocks/outblocks-plugin-go/util" + "github.com/outblocks/outblocks-plugin-go/util/errgroup" "github.com/pterm/pterm" "github.com/txn2/txeh" ) @@ -54,10 +56,11 @@ type runInfo struct { } const ( - loopbackHost = "outblocks.host" - loopbackIP = "127.0.0.1" - cleanupTimeout = 10 * time.Second - healthcheckSleep = 1 * time.Second + loopbackHost = "outblocks.host" + loopbackIP = "127.0.0.1" + cleanupTimeout = 10 * time.Second + healthcheckSleep = 1 * time.Second + healthcheckTimeout = 3 * time.Second ) func NewRun(log logger.Logger, cfg *config.Project, opts *RunOptions) *Run { @@ -366,11 +369,11 @@ func (d *Run) runAll(ctx context.Context, runInfo *runInfo) ([]*run.PluginRunRes func (d *Run) waitAll(ctx context.Context, runInfo *runInfo) error { spinner, _ := d.log.Spinner().WithRemoveWhenDone(true).Start("Waiting for apps and dependencies to be up...") - var wg sync.WaitGroup - - httpClient := &http.Client{} + httpClient := &http.Client{ + Timeout: healthcheckTimeout, + } - wg.Add(len(runInfo.apps)) + g, _ := errgroup.WithContext(ctx) for _, app := range runInfo.apps { app := app @@ -380,51 +383,88 @@ func (d *Run) waitAll(ctx context.Context, runInfo *runInfo) error { return err } - go func() { + g.Go(func() error { for { resp, err := httpClient.Do(req) + if errors.Is(err, context.Canceled) { + return err + } + if err == nil { _ = resp.Body.Close() d.log.Printf("%s App '%s' is UP.\n", strings.Title(app.App.Type), app.App.Name) - wg.Done() - return + return nil } time.Sleep(healthcheckSleep) } - }() + }) } - wg.Wait() - + err := g.Wait() _ = spinner.Stop() - return nil + return err } func formatRunOutput(log logger.Logger, r *plugin_go.RunOutputResponse) { + msg := plugin_util.StripAnsi(r.Message) + switch r.Source { case plugin_go.RunOutpoutSourceApp: if r.IsStderr { - log.StderrPrintf("%s %s\n", pterm.FgRed.Sprintf("APP:%s:", r.Name), r.Message) + log.Printf("%s %s\n", pterm.FgRed.Sprintf("APP:%s:", r.Name), msg) } else { - log.Printf("%s %s\n", pterm.FgGreen.Sprintf("APP:%s:", r.Name), r.Message) + log.Printf("%s %s\n", pterm.FgGreen.Sprintf("APP:%s:", r.Name), msg) } case plugin_go.RunOutpoutSourceDependency: if r.IsStderr { - log.StderrPrintf("%s %s\n", pterm.FgRed.Sprintf("DEP:%s:", r.Name), r.Message) + log.Printf("%s %s\n", pterm.FgRed.Sprintf("DEP:%s:", r.Name), msg) } else { - log.Printf("%s %s\n", pterm.FgGreen.Sprintf("DEP:%s:", r.Name), r.Message) + log.Printf("%s %s\n", pterm.FgGreen.Sprintf("DEP:%s:", r.Name), msg) } } } +func (d *Run) addAllHosts(runInfo *runInfo) (map[*url.URL]*url.URL, error) { + hosts := map[string]struct{}{ + d.loopbackHost(): {}, + } + + routing := make(map[*url.URL]*url.URL) + + for _, s := range runInfo.apps { + u, _ := url.Parse(s.URL) + hosts[u.Hostname()] = struct{}{} + + uLocal := *u + uLocal.Host = fmt.Sprintf("%s:%d", s.IP, s.Port) + uLocal.Path = s.App.PathRedirect + + routing[u] = &uLocal + } + + hostsList := make([]string, 0, len(hosts)) + + for h := range hosts { + hostsList = append(hostsList, h) + } + + err := d.AddHosts(hostsList...) + if err != nil { + return nil, fmt.Errorf("are you running with sudo? or try running with hosts-routing disabled") + } + + return routing, nil +} + func (d *Run) start(ctx context.Context, runInfo *runInfo) (*sync.WaitGroup, error) { var ( wg sync.WaitGroup routing map[*url.URL]*url.URL + err error ) errCh := make(chan error, 1) @@ -433,32 +473,9 @@ func (d *Run) start(ctx context.Context, runInfo *runInfo) (*sync.WaitGroup, err defer runnerCancel() if d.opts.HostsRouting { - hosts := map[string]struct{}{ - d.loopbackHost(): {}, - } - - routing = make(map[*url.URL]*url.URL) - - for _, s := range runInfo.apps { - u, _ := url.Parse(s.URL) - hosts[u.Hostname()] = struct{}{} - - uLocal := *u - uLocal.Host = fmt.Sprintf("%s:%d", s.IP, s.Port) - uLocal.Path = s.App.PathRedirect - - routing[u] = &uLocal - } - - hostsList := make([]string, 0, len(hosts)) - - for h := range hosts { - hostsList = append(hostsList, h) - } - - err := d.AddHosts(hostsList...) + routing, err = d.addAllHosts(runInfo) if err != nil { - return &wg, fmt.Errorf("are you running with sudo? or try running with hosts-routing disabled") + return &wg, err } } @@ -543,9 +560,15 @@ func (d *Run) start(ctx context.Context, runInfo *runInfo) (*sync.WaitGroup, err } // Healthcheck. - err = d.waitAll(ctx, runInfo) + err = d.waitAll(runnerCtx, runInfo) if err != nil { - return nil, err + select { + case err := <-errCh: + return &wg, err + default: + } + + return &wg, err } // Show apps status. @@ -556,6 +579,8 @@ func (d *Run) start(ctx context.Context, runInfo *runInfo) (*sync.WaitGroup, err d.log.Printf("%s App '%s' listening at %s\n", strings.Title(a.App.Type), a.App.Name, a.URL) } + d.log.Println() + <-runnerCtx.Done() select { @@ -585,6 +610,10 @@ func (d *Run) Run(ctx context.Context) error { if err != nil { wg.Wait() + if errors.Is(err, context.Canceled) { + return nil + } + return err } diff --git a/pkg/actions/run/local.go b/pkg/actions/run/local.go index da4e1e8..9c73481 100644 --- a/pkg/actions/run/local.go +++ b/pkg/actions/run/local.go @@ -73,14 +73,14 @@ func RunLocal(ctx context.Context, localApps []*LocalApp, localDeps []*LocalDepe ret.Apps[app.App.ID] = info } - for _, dep := range localDeps { - info, err := dep.Run() - if err != nil { - return nil, err - } - - ret.Deps[dep.Dependency.ID] = info - } + // for _, dep := range localDeps { + // info, err := dep.Run() + // if err != nil { + // return nil, err + // } + + // ret.Deps[dep.Dependency.ID] = info + // } return ret, nil } diff --git a/pkg/plugins/downloader_vcs.go b/pkg/plugins/downloader_vcs.go index cf5a77d..0a78e32 100644 --- a/pkg/plugins/downloader_vcs.go +++ b/pkg/plugins/downloader_vcs.go @@ -47,7 +47,7 @@ func (d *VCSDownloader) fetch(_ context.Context, pi *pluginInfo) (vcs.Repo, erro return nil, fmt.Errorf("cannot find source repo: %w", err) } - return repo, nil + return repo, plugin_util.LchownRToUser(cachePath) } func (d *VCSDownloader) download(ctx context.Context, pi *pluginInfo) (*DownloadedPlugin, string, error) { diff --git a/pkg/plugins/loader.go b/pkg/plugins/loader.go index 8f2f64c..8753233 100644 --- a/pkg/plugins/loader.go +++ b/pkg/plugins/loader.go @@ -181,7 +181,7 @@ func (l *Loader) downloadPlugin(ctx context.Context, pi *pluginInfo) (string, *s return "", nil, fmt.Errorf("failed to copy downloaded plugin %s: %w", destPath, err) } - if err := plugin_util.ChownRToUser(destPath); err != nil { + if err := plugin_util.LchownRToUser(destPath); err != nil { return "", nil, fmt.Errorf("failed to set permissions on downloaded plugin %s: %w", destPath, err) } @@ -222,11 +222,9 @@ func (l *Loader) installPlugin(pi *pluginInfo, from string) error { if err := copy.Copy(from, dest); err != nil { return fmt.Errorf("failed to copy cached plugin %s: %w", from, err) } - - return plugin_util.ChownRToUser(dest) } - return nil + return plugin_util.LchownRToUser(dest) } func (l *Loader) loadPlugin(pi *pluginInfo, path string, ver *semver.Version) (*Plugin, error) {