Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🐛 update java provider settings + serialize test runner #192

Merged
merged 1 commit into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion cmd/analyze.go
Original file line number Diff line number Diff line change
Expand Up @@ -420,12 +420,13 @@ func (a *analyzeCommand) getConfigVolumes() (map[string]string, error) {

javaConfig := provider.Config{
Name: "java",
BinaryPath: "/jdtls/bin/jdtls",
BinaryPath: "/usr/local/bin/java-external-provider",
InitConfig: []provider.InitConfig{
{
Location: SourceMountPath,
AnalysisMode: provider.AnalysisMode(a.mode),
ProviderSpecificConfig: map[string]interface{}{
"lspServerName": "java",
"bundles": JavaBundlesLocation,
"depOpenSourceLabelsFile": "/usr/local/etc/maven.default.index",
provider.LspServerPathConfigKey: "/jdtls/bin/jdtls",
Expand Down
1 change: 1 addition & 0 deletions cmd/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ func NewTestCommand(log logr.Logger) *cobra.Command {
RunLocal: Settings.RunLocal,
ContainerImage: Settings.RunnerImage,
ProgressPrinter: testing.PrintProgress,
Log: log.V(3),
})
testing.PrintSummary(os.Stdout, results)
if err != nil {
Expand Down
158 changes: 62 additions & 96 deletions pkg/testing/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"path/filepath"
"sort"
"strings"
"sync"

"github.com/bombsimon/logrusr/v3"
"github.com/go-logr/logr"
Expand All @@ -35,18 +34,20 @@ type TestOptions struct {
RunLocal bool
ContainerImage string
ProgressPrinter ResultPrinter
Log logr.Logger
}

// TODO (pgaikwad): we need to move the default config to a common place
// to be shared between kantra analyze command and this
var defaultProviderConfig = []provider.Config{
{
Name: "java",
BinaryPath: "/jdtls/bin/jdtls",
BinaryPath: "/usr/local/bin/java-external-provider",
InitConfig: []provider.InitConfig{
{
AnalysisMode: provider.FullAnalysisMode,
ProviderSpecificConfig: map[string]interface{}{
"lspServerName": "java",
"bundles": "/jdtls/java-analyzer-bundle/java-analyzer-bundle.core/target/java-analyzer-bundle.core-1.0.0-SNAPSHOT.jar",
"depOpenSourceLabelsFile": "/usr/local/etc/maven.default.index",
provider.LspServerPathConfigKey: "/jdtls/bin/jdtls",
Expand Down Expand Up @@ -128,105 +129,39 @@ func NewRunner() Runner {
// groups tests within a file by analysisParams
type defaultRunner struct{}

type workerInput struct {
testsFile TestsFile
opts TestOptions
}

func (r defaultRunner) Run(testFiles []TestsFile, opts TestOptions) ([]Result, error) {
workerInputChan := make(chan workerInput, len(testFiles))
resChan := make(chan []Result)

wg := &sync.WaitGroup{}

workerCount := 5
// when running in container, we don't want to mount
// same base volumes concurrently in two different places
if !opts.RunLocal {
workerCount = 1
}
// setup workers
for idx := 0; idx < workerCount; idx += 1 {
wg.Add(1)
go runWorker(wg, workerInputChan, resChan)
if opts.Log.GetSink() == nil {
opts.Log = logr.Discard()
}
// send input
go func() {
for idx := range testFiles {
testFile := testFiles[idx]
workerInputChan <- workerInput{
testsFile: testFile,
opts: opts,
}
}
close(workerInputChan)
}()
// wait for workers to finish
go func() {
wg.Wait()
close(resChan)
}()
// process results
results := []Result{}

allResults := []Result{}
anyFailed := false
anyErrored := false
// sorting for stability of unit tests
defer sort.Slice(results, func(i, j int) bool {
return strings.Compare(results[i].RuleID, results[j].RuleID) > 0
})
resultWg := sync.WaitGroup{}
resultWg.Add(1)
go func() {
defer resultWg.Done()
for res := range resChan {
if opts.ProgressPrinter != nil {
opts.ProgressPrinter(os.Stdout, res)
}
for _, r := range res {
if r.Error != nil {
anyErrored = true
}
if !r.Passed {
anyFailed = true
}
}
results = append(results, res...)
}
}()
resultWg.Wait()
if anyErrored {
return results, fmt.Errorf("failed to execute one or more tests")
}
if anyFailed {
return results, fmt.Errorf("one or more tests failed")
}
return results, nil
}

func runWorker(wg *sync.WaitGroup, inChan chan workerInput, outChan chan []Result) {
defer wg.Done()
for input := range inChan {
results := []Result{}
for idx := range testFiles {
testsFile := testFiles[idx]
// users can override the base provider settings file
baseProviderConfig := defaultProviderConfig
if input.opts.BaseProviderConfig != nil {
baseProviderConfig = input.opts.BaseProviderConfig
if opts.BaseProviderConfig != nil {
baseProviderConfig = opts.BaseProviderConfig
}
// within a tests file, we group tests by analysis params
testGroups := groupTestsByAnalysisParams(input.testsFile.Tests)
testGroups := groupTestsByAnalysisParams(testsFile.Tests)
results := []Result{}
for _, tests := range testGroups {
tempDir, err := os.MkdirTemp(input.opts.TempDir, "rules-test-")
tempDir, err := os.MkdirTemp(opts.TempDir, "rules-test-")
if err != nil {
results = append(results, Result{
TestsFilePath: input.testsFile.Path,
TestsFilePath: testsFile.Path,
Error: fmt.Errorf("failed creating temp dir - %w", err)})
continue
}
opts.Log.Info("created temporary directory", "dir", tempDir, "tests", testsFile.Path)
// print analysis logs to a file
logFile, err := os.OpenFile(filepath.Join(tempDir, "analysis.log"), os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
logFile, err := os.OpenFile(filepath.Join(tempDir, "analysis.log"),
os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
if err != nil {
results = append(results, Result{
TestsFilePath: input.testsFile.Path,
TestsFilePath: testsFile.Path,
Error: fmt.Errorf("failed creating a log file - %w", err)})
logFile.Close()
continue
Expand All @@ -236,40 +171,42 @@ func runWorker(wg *sync.WaitGroup, inChan chan workerInput, outChan chan []Resul
baseLogger.SetLevel(logrus.InfoLevel)
logger := logrusr.New(baseLogger)
// write rules
err = ensureRules(input.testsFile.RulesPath, tempDir, tests)
err = ensureRules(testsFile.RulesPath, tempDir, tests)
if err != nil {
results = append(results, Result{
TestsFilePath: input.testsFile.Path,
TestsFilePath: testsFile.Path,
Error: fmt.Errorf("failed writing rules - %w", err)})
logFile.Close()
continue
}
// we already know in this group, all tcs have same params, use any
analysisParams := tests[0].TestCases[0].AnalysisParams
// write provider settings file
volumes, err := ensureProviderSettings(tempDir, input.opts.RunLocal, input.testsFile, baseProviderConfig, analysisParams)
volumes, err := ensureProviderSettings(
tempDir, opts.RunLocal, testsFile, baseProviderConfig, analysisParams)
if err != nil {
results = append(results, Result{
TestsFilePath: input.testsFile.Path,
TestsFilePath: testsFile.Path,
Error: fmt.Errorf("failed writing provider settings - %w", err)})
logFile.Close()
continue
}
volumes[tempDir] = "/shared/"
reproducerCmd := ""
switch {
case input.opts.RunLocal:
case opts.RunLocal:
if reproducerCmd, err = runLocal(logFile, tempDir, analysisParams); err != nil {
results = append(results, Result{
TestsFilePath: input.testsFile.Path,
TestsFilePath: testsFile.Path,
Error: err})
logFile.Close()
continue
}
default:
if reproducerCmd, err = runInContainer(logger, input.opts.ContainerImage, logFile, volumes, analysisParams); err != nil {
if reproducerCmd, err = runInContainer(
logger, opts.ContainerImage, logFile, volumes, analysisParams); err != nil {
results = append(results, Result{
TestsFilePath: input.testsFile.Path,
TestsFilePath: testsFile.Path,
Error: err})
logFile.Close()
continue
Expand All @@ -282,15 +219,15 @@ func runWorker(wg *sync.WaitGroup, inChan chan workerInput, outChan chan []Resul
content, err := os.ReadFile(filepath.Join(tempDir, "output.yaml"))
if err != nil {
results = append(results, Result{
TestsFilePath: input.testsFile.Path,
TestsFilePath: testsFile.Path,
Error: fmt.Errorf("failed reading output - %w", err)})
logFile.Close()
continue
}
err = yaml.Unmarshal(content, &outputRulesets)
if err != nil {
results = append(results, Result{
TestsFilePath: input.testsFile.Path,
TestsFilePath: testsFile.Path,
Error: fmt.Errorf("failed unmarshaling output %s", filepath.Join(tempDir, "output.yaml"))})
logFile.Close()
continue
Expand All @@ -300,7 +237,7 @@ func runWorker(wg *sync.WaitGroup, inChan chan workerInput, outChan chan []Resul
for _, test := range tests {
for _, tc := range test.TestCases {
result := Result{
TestsFilePath: input.testsFile.Path,
TestsFilePath: testsFile.Path,
RuleID: test.RuleID,
TestCaseName: tc.Name,
}
Expand All @@ -325,8 +262,33 @@ func runWorker(wg *sync.WaitGroup, inChan chan workerInput, outChan chan []Resul
}
logFile.Close()
}
outChan <- results
// print progress
if opts.ProgressPrinter != nil {
opts.ProgressPrinter(os.Stdout, results)
}
// result
for _, r := range results {
if r.Error != nil {
anyErrored = true
}
if !r.Passed {
anyFailed = true
}
}
allResults = append(allResults, results...)
}
// sorting for stability of unit tests
defer sort.Slice(allResults, func(i, j int) bool {
return strings.Compare(allResults[i].RuleID, allResults[j].RuleID) > 0
})

if anyErrored {
return allResults, fmt.Errorf("failed to execute one or more tests")
}
if anyFailed {
return allResults, fmt.Errorf("one or more tests failed")
}
return allResults, nil
}

func runLocal(logFile io.Writer, dir string, analysisParams AnalysisParams) (string, error) {
Expand All @@ -338,6 +300,8 @@ func runLocal(logFile io.Writer, dir string, analysisParams AnalysisParams) (str
filepath.Join(dir, "output.yaml"),
"--rules",
filepath.Join(dir, "rules.yaml"),
"--verbose",
"20",
}
if analysisParams.DepLabelSelector != "" {
args = append(args, []string{
Expand All @@ -363,6 +327,8 @@ func runInContainer(consoleLogger logr.Logger, image string, logFile io.Writer,
"/shared/output.yaml",
"--rules",
"/shared/rules.yaml",
"--verbose",
"20",
}
if analysisParams.DepLabelSelector != "" {
args = append(args, []string{
Expand Down
Loading