Skip to content

Commit

Permalink
SONARIAC-1612 Helm in SonarLint evaluate too much files (#1506)
Browse files Browse the repository at this point in the history
  • Loading branch information
mstachniuk authored Sep 16, 2024
1 parent 1023296 commit 45fdb41
Show file tree
Hide file tree
Showing 20 changed files with 341 additions and 220 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@
import java.nio.file.Path;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import javax.annotation.Nullable;
import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.iac.kubernetes.plugin.SonarLintFileListener;
import org.sonar.iac.kubernetes.plugin.filesystem.FileSystemProvider;
import org.sonar.iac.kubernetes.visitors.HelmInputFileContext;

Expand Down Expand Up @@ -60,13 +62,29 @@ public Map<String, String> getRelatedHelmFiles(HelmInputFileContext inputFileCon
return fileSystemProvider.inputFilesForHelm(inputFileContext);
}

/**
* Returns a path where Chart.yaml file is located.
* This is a version for SonarQube and SonarCloud context.
*/
public static Path retrieveHelmProjectFolder(Path inputFilePath, FileSystem fileSystem) {
return retrieveHelmProjectFolder(inputFilePath, fileSystem, Files::exists);
}

/**
* Returns a path where Chart.yaml file is located.
* This is a version for SonarLint context.
*/
public static Path retrieveHelmProjectFolder(Path inputFilePath, FileSystem fileSystem, SonarLintFileListener sonarLintFileListener) {
return retrieveHelmProjectFolder(inputFilePath, fileSystem, path -> fileExistInSonarLint(sonarLintFileListener, path));
}

private static Path retrieveHelmProjectFolder(Path inputFilePath, FileSystem fileSystem, Predicate<Path> chartYamlExist) {
var baseDirPath = fileSystem.baseDir().toPath();

var helmProjectDirectoryPath = inputFilePath;

while (helmProjectDirectoryPath != null && helmProjectDirectoryPath.startsWith(baseDirPath)) {
if (Files.exists(helmProjectDirectoryPath.resolve("Chart.yaml"))) {
while (helmProjectDirectoryPath != null) {
if (chartYamlExist.test(helmProjectDirectoryPath.resolve("Chart.yaml"))) {
break;
}
helmProjectDirectoryPath = helmProjectDirectoryPath.getParent();
Expand All @@ -76,4 +94,8 @@ public static Path retrieveHelmProjectFolder(Path inputFilePath, FileSystem file
}
return helmProjectDirectoryPath;
}

private static boolean fileExistInSonarLint(SonarLintFileListener sonarLintFileListener, Path helmProjectDirectoryPath) {
return sonarLintFileListener.inputFilesContents().containsKey(helmProjectDirectoryPath.toUri().toString());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,27 @@ public class KubernetesAnalyzer extends CrossFileAnalyzer {

private final HelmParser helmParser;
private final KubernetesParserStatistics kubernetesParserStatistics;
@Nullable
private final SonarLintFileListener sonarLintFileListener;

public KubernetesAnalyzer(String repositoryKey, TreeParser<? extends Tree> parser, List<TreeVisitor<InputFileContext>> visitors,
public KubernetesAnalyzer(String repositoryKey,
TreeParser<? extends Tree> parser,
List<TreeVisitor<InputFileContext>> visitors,
DurationStatistics statistics,
HelmParser helmParser, KubernetesParserStatistics kubernetesParserStatistics, TreeVisitor<InputFileContext> checksVisitor) {
HelmParser helmParser,
KubernetesParserStatistics kubernetesParserStatistics,
TreeVisitor<InputFileContext> checksVisitor,
@Nullable SonarLintFileListener sonarLintFileListener) {
super(repositoryKey, parser, visitors, checksVisitor, statistics);
this.helmParser = helmParser;
this.kubernetesParserStatistics = kubernetesParserStatistics;
this.sonarLintFileListener = sonarLintFileListener;
}

@Override
protected InputFileContext createInputFileContext(SensorContext sensorContext, InputFile inputFile) {
if (isHelmFile(inputFile)) {
return new HelmInputFileContext(sensorContext, inputFile);
return new HelmInputFileContext(sensorContext, inputFile, sonarLintFileListener);
}
return new InputFileContext(sensorContext, inputFile);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,13 @@ protected void initContext(SensorContext sensorContext) {
if (sonarLintFileListener != null) {
var statistics = new DurationStatistics(sensorContext.config());
var analyzer = createAnalyzerForUpdatingProjectContext(statistics);
sonarLintFileListener.initContext(sensorContext, analyzer, projectContext, (SonarLintFileSystemProvider) fileSystemProvider);
sonarLintFileListener.initContext(sensorContext, analyzer, projectContext);
}
}

private FileSystemProvider createFileSystemProvider(SensorContext sensorContext) {
if (sonarLintFileListener != null) {
return new SonarLintFileSystemProvider();
return new SonarLintFileSystemProvider(sonarLintFileListener);
}
return new DefaultFileSystemProvider(sensorContext.fileSystem());
}
Expand Down Expand Up @@ -157,7 +157,8 @@ protected Analyzer createAnalyzer(SensorContext sensorContext, DurationStatistic
statistics,
new HelmParser(helmProcessor),
kubernetesParserStatistics,
new KubernetesChecksVisitor(checks, statistics, projectContext));
new KubernetesChecksVisitor(checks, statistics, projectContext),
sonarLintFileListener);
}

/**
Expand All @@ -173,7 +174,8 @@ private KubernetesAnalyzer createAnalyzerForUpdatingProjectContext(DurationStati
statistics,
new HelmParser(helmProcessor),
kubernetesParserStatistics,
new EmptyChecksVisitor());
new EmptyChecksVisitor(),
sonarLintFileListener);
}

void setHelmProcessorForTesting(HelmProcessor helmProcessor) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,16 @@

import java.io.IOException;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.sensor.SensorContext;
import org.sonar.iac.common.extension.ParseException;
import org.sonar.iac.helm.HelmFileSystem;
import org.sonar.iac.kubernetes.plugin.filesystem.SonarLintFileSystemProvider;
import org.sonar.iac.kubernetes.plugin.predicates.KubernetesOrHelmFilePredicate;
import org.sonar.iac.kubernetes.visitors.ProjectContext;
import org.sonarsource.api.sonarlint.SonarLintSide;
Expand All @@ -46,56 +47,62 @@ public class SonarLintFileListener implements ModuleFileListener {
private SensorContext sensorContext;
private KubernetesAnalyzer analyzer;
private ProjectContext projectContext;
private SonarLintFileSystemProvider fileSystemProvider;
private Map<String, String> inputFilesContents = new HashMap<>();

public SonarLintFileListener(ModuleFileSystem moduleFileSystem) {
this.moduleFileSystem = moduleFileSystem;
}

public void initContext(SensorContext sensorContext, KubernetesAnalyzer analyzer, ProjectContext projectContext, SonarLintFileSystemProvider fileSystemProvider) {
public void initContext(SensorContext sensorContext, KubernetesAnalyzer analyzer, ProjectContext projectContext) {
this.sensorContext = sensorContext;
this.analyzer = analyzer;
this.projectContext = projectContext;
this.fileSystemProvider = fileSystemProvider;
var predicate = new KubernetesOrHelmFilePredicate(sensorContext);
var inputFiles = moduleFileSystem.files()
.filter(predicate::apply)
.toList();
if (inputFilesContents.isEmpty()) {
// The analysis is executed for the first time by SonarLint, the content of all relevant files has to be stored in inputFilesContents
var predicate = new KubernetesOrHelmFilePredicate(sensorContext);
var inputFiles = moduleFileSystem.files()
.filter(predicate::apply)
.toList();

storeInputFilesContent(inputFiles);
analyzer.analyseFiles(sensorContext, inputFiles, KubernetesLanguage.KEY);
LOG.info("Finished building Kubernetes Project Context");
inputFilesContents = inputFiles.stream()
.collect(Collectors.toMap(SonarLintFileListener::getPath, SonarLintFileListener::content));
// it will fill the projectContext with the data needed for cross-file analysis
analyzer.analyseFiles(sensorContext, inputFiles, KubernetesLanguage.KEY);
LOG.info("Finished building Kubernetes Project Context");
}
}

@Override
public void process(ModuleFileEvent moduleFileEvent) {
InputFile target = moduleFileEvent.getTarget();
String language = target.language();
if (language == null || !HelmFileSystem.INCLUDED_EXTENSIONS.contains(language)) {
LOG.debug("Module file event for {} for file {} has been ignored because it's not a Kubernetes file.",
LOG.info("Module file event for {} for file {} has been ignored because it's not a Kubernetes file.",
moduleFileEvent.getType(), moduleFileEvent.getTarget());
return;
}

LOG.debug("Module file event {} for file {}", moduleFileEvent.getType(), moduleFileEvent.getTarget());
LOG.info("Module file event {} for file {}", moduleFileEvent.getType(), moduleFileEvent.getTarget());
// the projectContext may be null if SonarLint calls this method before initContext()
// it happens when starting IDE
if (projectContext != null) {
var inputFilesContents = fileSystemProvider.getInputFilesContents();
var uri = getPath(moduleFileEvent);
projectContext.removeResource(uri);
inputFilesContents.remove(moduleFileEvent.getTarget().filename());
if (moduleFileEvent.getType() != ModuleFileEvent.Type.DELETED) {
inputFilesContents.put(moduleFileEvent.getTarget().filename(), content(moduleFileEvent.getTarget()));
analyzer.analyseFiles(sensorContext, List.of(moduleFileEvent.getTarget()), KubernetesLanguage.KEY);
}
fileSystemProvider.setInputFilesContents(inputFilesContents);
LOG.debug("Kubernetes Project Context updated");
LOG.info("Kubernetes Project Context updated");
} else {
LOG.debug("Kubernetes Project Context not updated");
LOG.info("Kubernetes Project Context not updated");
}
}

public Map<String, String> inputFilesContents() {
return inputFilesContents;
}

private static String getPath(ModuleFileEvent moduleFileEvent) {
return getPath(moduleFileEvent.getTarget());
}
Expand All @@ -104,12 +111,6 @@ private static String getPath(InputFile inputfile) {
return Path.of(inputfile.uri()).normalize().toUri().toString();
}

private void storeInputFilesContent(List<InputFile> inputFiles) {
var filenameToContent = inputFiles.stream()
.collect(Collectors.toMap(SonarLintFileListener::getPath, SonarLintFileListener::content));
fileSystemProvider.setInputFilesContents(filenameToContent);
}

private static String content(InputFile inputFile) {
try {
return inputFile.contents();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,34 +19,30 @@
*/
package org.sonar.iac.kubernetes.plugin.filesystem;

import java.util.HashMap;
import java.util.Map;
import org.sonar.iac.kubernetes.plugin.SonarLintFileListener;
import org.sonar.iac.kubernetes.visitors.HelmInputFileContext;

import static java.util.stream.Collectors.toMap;

public class SonarLintFileSystemProvider implements FileSystemProvider {

private Map<String, String> inputFilesContents = new HashMap<>();
private final SonarLintFileListener sonarLintFileListener;

public SonarLintFileSystemProvider(SonarLintFileListener sonarLintFileListener) {
this.sonarLintFileListener = sonarLintFileListener;
}

@Override
public Map<String, String> inputFilesForHelm(HelmInputFileContext inputFileContext) {
var helmProjectDirectory = inputFileContext.getHelmProjectDirectory();
if (helmProjectDirectory != null) {
var helmProjectDirectoryAsText = helmProjectDirectory.toUri().toString();
return inputFilesContents.entrySet().stream()
return sonarLintFileListener.inputFilesContents().entrySet().stream()
.filter(entry -> entry.getKey().startsWith(helmProjectDirectoryAsText))
.filter(entry -> !entry.getKey().equals(inputFileContext.inputFile.uri().toString()))
.collect(toMap(entry -> entry.getKey().substring(helmProjectDirectoryAsText.length()), Map.Entry::getValue));
}
return Map.of();
}

public void setInputFilesContents(Map<String, String> inputFilesContents) {
this.inputFilesContents = inputFilesContents;
}

public Map<String, String> getInputFilesContents() {
return inputFilesContents;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.sonar.iac.common.extension.visitors.InputFileContext;
import org.sonar.iac.helm.HelmFileSystem;
import org.sonar.iac.helm.tree.api.GoTemplateTree;
import org.sonar.iac.kubernetes.plugin.SonarLintFileListener;
import org.sonar.iac.kubernetes.visitors.LocationShifter.LinesShifting;

public class HelmInputFileContext extends InputFileContext {
Expand All @@ -43,9 +44,13 @@ public class HelmInputFileContext extends InputFileContext {

private final LinesShifting linesShifting = new LinesShifting();

public HelmInputFileContext(SensorContext sensorContext, InputFile inputFile) {
public HelmInputFileContext(SensorContext sensorContext, InputFile inputFile, @Nullable SonarLintFileListener sonarLintFileListener) {
super(sensorContext, inputFile);
this.helmProjectDirectory = HelmFileSystem.retrieveHelmProjectFolder(Path.of(inputFile.uri()), sensorContext.fileSystem());
if (sonarLintFileListener == null) {
this.helmProjectDirectory = HelmFileSystem.retrieveHelmProjectFolder(Path.of(inputFile.uri()), sensorContext.fileSystem());
} else {
this.helmProjectDirectory = HelmFileSystem.retrieveHelmProjectFolder(Path.of(inputFile.uri()), sensorContext.fileSystem(), sonarLintFileListener);
}
}

public void setAdditionalFiles(Map<String, String> additionalFiles) {
Expand Down
Loading

0 comments on commit 45fdb41

Please sign in to comment.