summaryrefslogtreecommitdiffstats
path: root/runtime/run.go
diff options
context:
space:
mode:
authorAlex Goodman <wagoodman@gmail.com>2019-10-09 14:49:59 -0400
committerAlex Goodman <wagoodman@gmail.com>2019-10-09 14:49:59 -0400
commitc8ab7098d84bb647d48d2f8b20aa053e2870eeba (patch)
tree331a46eee139c0b6eda90168fd6f001da1fd761d /runtime/run.go
parent11a2473807e3442307ccabd7e63c4905293707fd (diff)
add tests to cover runtime entrypoint
Diffstat (limited to 'runtime/run.go')
-rw-r--r--runtime/run.go187
1 files changed, 105 insertions, 82 deletions
diff --git a/runtime/run.go b/runtime/run.go
index a55f583..0dbcfa8 100644
--- a/runtime/run.go
+++ b/runtime/run.go
@@ -2,126 +2,149 @@ package runtime
import (
"fmt"
+ "github.com/dustin/go-humanize"
"github.com/sirupsen/logrus"
+ "github.com/spf13/afero"
"github.com/wagoodman/dive/dive"
- "github.com/wagoodman/dive/runtime/ci"
- "github.com/wagoodman/dive/runtime/export"
- "os"
- "time"
-
- "github.com/dustin/go-humanize"
"github.com/wagoodman/dive/dive/filetree"
"github.com/wagoodman/dive/dive/image"
+ "github.com/wagoodman/dive/runtime/ci"
+ "github.com/wagoodman/dive/runtime/export"
"github.com/wagoodman/dive/runtime/ui"
"github.com/wagoodman/dive/utils"
+ "os"
+ "time"
)
-func runCi(analysis *image.AnalysisResult, options Options) {
-
- fmt.Printf(" efficiency: %2.4f %%\n", analysis.Efficiency*100)
- fmt.Printf(" wastedBytes: %d bytes (%s)\n", analysis.WastedBytes, humanize.Bytes(analysis.WastedBytes))
- fmt.Printf(" userWastedPercent: %2.4f %%\n", analysis.WastedUserPercent*100)
-
- evaluator := ci.NewCiEvaluator(options.CiConfig)
-
- pass := evaluator.Evaluate(analysis)
-
- // todo: report should return a string?
- evaluator.Report()
-
- if pass {
- os.Exit(0)
- }
- os.Exit(1)
-}
-
-// todo: give channel of strings which the caller uses for fmt.print? or a more complex type?
-// todo: return err? or treat like a go routine?
-// todo: should there be a run() so that Run() can do the above and run() be the go routine? Then we test the behavior of run() (not Run())
-func Run(options Options) {
+func run(enableUi bool, options Options, imageResolver image.Resolver, events eventChannel, filesystem afero.Fs) {
+ var img *image.Image
var err error
+ defer close(events)
+
doExport := options.ExportFile != ""
doBuild := len(options.BuildArgs) > 0
- // if an image option was provided, parse it and determine the container image...
- // otherwise, use the configs default value.
-
- // if build is given, get the handler based off of either the explicit runtime
-
- imageResolver, err := dive.GetImageResolver(options.Source)
- if err != nil {
- fmt.Printf("cannot determine image provider: %v\n", err)
- os.Exit(1)
- }
-
- var img *image.Image
-
if doBuild {
- fmt.Println(utils.TitleFormat("Building image..."))
+ events.message(utils.TitleFormat("Building image..."))
img, err = imageResolver.Build(options.BuildArgs)
if err != nil {
- fmt.Printf("cannot build image: %v\n", err)
- os.Exit(1)
+ events.exitWithErrorMessage("cannot build image", err)
+ return
}
} else {
- fmt.Println(utils.TitleFormat("Fetching image...") + " (this can take a while for large images)")
+ events.message(utils.TitleFormat("Fetching image...") + " (this can take a while for large images)")
img, err = imageResolver.Fetch(options.Image)
if err != nil {
- fmt.Printf("cannot fetch image: %v\n", err)
- os.Exit(1)
+ events.exitWithErrorMessage("cannot fetch image", err)
+ return
}
}
- // todo, cleanup on error
- // todo: image get should return error for cleanup?
-
- if doExport {
- fmt.Println(utils.TitleFormat(fmt.Sprintf("Analyzing image... (export to '%s')", options.ExportFile)))
- } else {
- fmt.Println(utils.TitleFormat("Analyzing image..."))
- }
-
- result, err := img.Analyze()
+ events.message(utils.TitleFormat("Analyzing image..."))
+ analysis, err := img.Analyze()
if err != nil {
- fmt.Printf("cannot analyze image: %v\n", err)
- os.Exit(1)
+ events.exitWithErrorMessage("cannot analyze image", err)
+ return
}
if doExport {
- err = export.NewExport(result).ToFile(options.ExportFile)
+ events.message(utils.TitleFormat(fmt.Sprintf("Exporting image to '%s'...", options.ExportFile)))
+ bytes, err := export.NewExport(analysis).Marshal()
if err != nil {
- fmt.Printf("cannot write export file: %v\n", err)
- os.Exit(1)
+ events.exitWithErrorMessage("cannot marshal export payload", err)
+ return
}
+
+ file, err := filesystem.OpenFile(options.ExportFile, os.O_RDWR|os.O_CREATE, 0644)
+ if err != nil {
+ events.exitWithErrorMessage("cannot open export file", err)
+ return
+ }
+ defer file.Close()
+
+ _, err = file.Write(bytes)
+ if err != nil {
+ events.exitWithErrorMessage("cannot write to export file", err)
+ }
+ return
}
if options.Ci {
- runCi(result, options)
- } else {
- if doExport {
- os.Exit(0)
+ events.message(fmt.Sprintf(" efficiency: %2.4f %%\n", analysis.Efficiency*100))
+ events.message(fmt.Sprintf(" wastedBytes: %d bytes (%s)\n", analysis.WastedBytes, humanize.Bytes(analysis.WastedBytes)))
+ events.message(fmt.Sprintf(" userWastedPercent: %2.4f %%\n", analysis.WastedUserPercent*100))
+
+ evaluator := ci.NewCiEvaluator(options.CiConfig)
+ pass := evaluator.Evaluate(analysis)
+ events.message(evaluator.Report())
+
+ if !pass {
+ events.exitWithError(nil)
}
- fmt.Println(utils.TitleFormat("Building cache..."))
- cache := filetree.NewFileTreeCache(result.RefTrees)
+ return
+
+ } else {
+ events.message(utils.TitleFormat("Building cache..."))
+ cache := filetree.NewFileTreeCache(analysis.RefTrees)
err := cache.Build()
if err != nil {
- logrus.Error(err)
- os.Exit(1)
+ events.exitWithErrorMessage("cannot build cache tree", err)
+ return
}
- // it appears there is a race condition where termbox.Init() will
- // block nearly indefinitely when running as the first process in
- // a Docker container when started within ~25ms of container startup.
- // I can't seem to determine the exact root cause, however, a large
- // enough sleep will prevent this behavior (todo: remove this hack)
- time.Sleep(100 * time.Millisecond)
+ if enableUi {
+ // it appears there is a race condition where termbox.Init() will
+ // block nearly indefinitely when running as the first process in
+ // a Docker container when started within ~25ms of container startup.
+ // I can't seem to determine the exact root cause, however, a large
+ // enough sleep will prevent this behavior (todo: remove this hack)
+ time.Sleep(100 * time.Millisecond)
+
+ err = ui.Run(analysis, cache)
+ if err != nil {
+ events.exitWithErrorMessage("runtime error", err)
+ return
+ }
+ }
+ }
+}
- err = ui.Run(result, cache)
- if err != nil {
- logrus.Error(err)
- os.Exit(1)
+func Run(options Options) {
+ var exitCode int
+ var events = make(eventChannel)
+
+ imageResolver, err := dive.GetImageResolver(options.Source)
+ if err != nil {
+ message := "cannot determine image provider"
+ logrus.Error(message)
+ logrus.Error(err)
+ fmt.Fprintf(os.Stderr, "%s: %+v\n", message, err)
+ os.Exit(1)
+ }
+
+ go run(true, options, imageResolver, events, afero.NewOsFs())
+
+ for event := range events {
+ if event.stdout != "" {
+ fmt.Println(event.stdout)
+ }
+
+ if event.stderr != "" {
+ logrus.Error(event.stderr)
+ _, err := fmt.Fprintln(os.Stderr, event.stderr)
+ if err != nil {
+ fmt.Println("error: could not write to buffer:", err)
+ }
+ }
+
+ if event.err != nil {
+ logrus.Error(event.err)
+ }
+
+ if event.errorOnExit {
+ exitCode = 1
}
- os.Exit(0)
}
+ os.Exit(exitCode)
}