Skip to content

Commit

Permalink
Merge pull request #32 from adevinta/lava-scan
Browse files Browse the repository at this point in the history
cmd/lava: rename "lava run" command to "lava scan" and add tests
  • Loading branch information
jroimartin authored Nov 9, 2023
2 parents 0d72492 + 9413e3d commit 8c0c5fd
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 16 deletions.
29 changes: 17 additions & 12 deletions cmd/lava/internal/run/run.go → cmd/lava/internal/scan/scan.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright 2023 Adevinta

// Package run implements the run command.
package run
// Package scan implements the scan command.
package scan

import (
"errors"
Expand All @@ -19,15 +19,15 @@ import (
"github.com/adevinta/lava/internal/report"
)

// CmdRun represents the run command.
var CmdRun = &base.Command{
UsageLine: "run [flags]",
// CmdScan represents the scan command.
var CmdScan = &base.Command{
UsageLine: "scan [flags]",
Short: "run scan",
Long: `
Run a scan using the provided config file.
The -c flag allows to specify a configuration file. By default, "lava
run" looks for a configuration file with the name "lava.yaml" in the
scan" looks for a configuration file with the name "lava.yaml" in the
current directory.
The -forcecolor flag forces colorized output. By default, colorized
Expand All @@ -40,15 +40,18 @@ output is disabled in the following cases:
}

var (
cfgfile = CmdRun.Flag.String("c", "lava.yaml", "config file")
forceColor = CmdRun.Flag.Bool("forcecolor", false, "force colorized output")
cfgfile = CmdScan.Flag.String("c", "lava.yaml", "config file")
forceColor = CmdScan.Flag.Bool("forcecolor", false, "force colorized output")
)

func init() {
CmdRun.Run = run // Break initialization cycle.
CmdScan.Run = run // Break initialization cycle.
}

// run is the entry point of the run command.
// osExit is used by tests to capture the exit code.
var osExit = os.Exit

// run is the entry point of the scan command.
func run(args []string) error {
if len(args) > 0 {
return errors.New("too many arguments")
Expand All @@ -69,13 +72,14 @@ func run(args []string) error {
if err = os.Chdir(filepath.Dir(*cfgfile)); err != nil {
return fmt.Errorf("change directory: %w", err)
}

metrics.Collect("lava_version", cfg.LavaVersion)
metrics.Collect("targets", cfg.Targets)

base.LogLevel.Set(cfg.LogLevel)
er, err := engine.Run(cfg.ChecktypesURLs, cfg.Targets, cfg.AgentConfig)
if err != nil {
return fmt.Errorf("run: %w", err)
return fmt.Errorf("engine run: %w", err)
}

rw, err := report.NewWriter(cfg.ReportConfig)
Expand All @@ -98,7 +102,8 @@ func run(args []string) error {
return fmt.Errorf("write metrics: %w", err)
}
}
os.Exit(int(exitCode))

osExit(int(exitCode))

return nil
}
100 changes: 100 additions & 0 deletions cmd/lava/internal/scan/scan_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Copyright 2023 Adevinta

package scan

import (
"flag"
"log/slog"
"os"
"path/filepath"
"testing"

"github.com/jroimartin/clilog"

"github.com/adevinta/lava/internal/gitserver/gittest"
)

func TestMain(m *testing.M) {
flag.Parse()

level := slog.LevelError
if testing.Verbose() {
level = slog.LevelDebug
}

h := clilog.NewCLIHandler(os.Stderr, &clilog.HandlerOptions{Level: level})
slog.SetDefault(slog.New(h))

os.Exit(m.Run())
}

func TestRun(t *testing.T) {
tests := []struct {
name string
wantExitCode int
repo string
}{
{
name: "good repo",
wantExitCode: 0,
repo: "testdata/goodrepo.tar",
},
{
name: "vulnerable repo",
wantExitCode: 103,
repo: "testdata/vulnrepo.tar",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
oldPwd := mustGetwd()
oldCfgfile := *cfgfile
oldOsExit := osExit
defer func() {
mustChdir(oldPwd)
*cfgfile = oldCfgfile
osExit = oldOsExit
}()

tmpPath, err := gittest.ExtractTemp(tt.repo)
if err != nil {
t.Fatalf("unexpected error extracting test repository: %v", err)
}
defer os.RemoveAll(tmpPath)

*cfgfile = filepath.Join(tmpPath, "lava.yaml")

var exitCode int
osExit = func(status int) {
exitCode = status
}

if err := run(nil); err != nil {
t.Fatalf("run error: %v", err)
}

if exitCode != tt.wantExitCode {
t.Errorf("unexpected exit code: got: %v, want: %v", exitCode, tt.wantExitCode)
}
})
}
}

// mustGetwd returns a rooted path name corresponding to the current
// directory. It panics on error.
func mustGetwd() string {
wd, err := os.Getwd()
if err != nil {
panic(err)
}
return wd
}

// mustChdir changes the current working directory to the named
// directory. It panics on error.
func mustChdir(path string) {
if err := os.Chdir(path); err != nil {
panic(err)
}
}
Binary file added cmd/lava/internal/scan/testdata/goodrepo.tar
Binary file not shown.
Binary file added cmd/lava/internal/scan/testdata/vulnrepo.tar
Binary file not shown.
4 changes: 2 additions & 2 deletions cmd/lava/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ import (
"github.com/adevinta/lava/cmd/lava/internal/base"
"github.com/adevinta/lava/cmd/lava/internal/help"
"github.com/adevinta/lava/cmd/lava/internal/initialize"
"github.com/adevinta/lava/cmd/lava/internal/run"
"github.com/adevinta/lava/cmd/lava/internal/scan"
)

func init() {
base.Commands = []*base.Command{
run.CmdRun,
scan.CmdScan,
initialize.CmdInit,
}
}
Expand Down
10 changes: 8 additions & 2 deletions internal/report/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
type Writer struct {
prn printer
w io.WriteCloser
isStdout bool
minSeverity config.Severity
exclusions []config.Exclusion
}
Expand All @@ -41,17 +42,20 @@ func NewWriter(cfg config.ReportConfig) (Writer, error) {
}

w := os.Stdout
isStdout := true
if cfg.OutputFile != "" {
f, err := os.Create(cfg.OutputFile)
if err != nil {
return Writer{}, fmt.Errorf("create file: %w", err)
}
w = f
isStdout = false
}

return Writer{
prn: prn,
w: w,
isStdout: isStdout,
minSeverity: cfg.Severity,
exclusions: cfg.Exclusions,
}, nil
Expand Down Expand Up @@ -85,8 +89,10 @@ func (writer Writer) Write(er engine.Report) (ExitCode, error) {

// Close closes the [Writer].
func (writer Writer) Close() error {
if err := writer.w.Close(); err != nil {
return fmt.Errorf("close writer: %w", err)
if !writer.isStdout {
if err := writer.w.Close(); err != nil {
return fmt.Errorf("close writer: %w", err)
}
}
return nil
}
Expand Down

0 comments on commit 8c0c5fd

Please sign in to comment.