diff options
Diffstat (limited to 'sonar-scanner-engine/src/main/java/org/sonar/scanner/qualitygate/QualityGateCheck.java')
-rw-r--r-- | sonar-scanner-engine/src/main/java/org/sonar/scanner/qualitygate/QualityGateCheck.java | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/qualitygate/QualityGateCheck.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/qualitygate/QualityGateCheck.java new file mode 100644 index 00000000000..bae3d39b634 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/qualitygate/QualityGateCheck.java @@ -0,0 +1,168 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.scanner.qualitygate; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import java.util.EnumSet; +import java.util.Properties; +import org.picocontainer.Startable; +import org.sonar.api.utils.MessageException; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.sonar.scanner.bootstrap.DefaultScannerWsClient; +import org.sonar.scanner.scan.ScanProperties; +import org.sonarqube.ws.Ce; +import org.sonarqube.ws.Ce.TaskStatus; +import org.sonarqube.ws.MediaTypes; +import org.sonarqube.ws.Qualitygates; +import org.sonarqube.ws.Qualitygates.ProjectStatusResponse.Status; +import org.sonarqube.ws.client.GetRequest; +import org.sonarqube.ws.client.HttpException; +import org.sonarqube.ws.client.WsResponse; + +public class QualityGateCheck implements Startable { + + private static final Logger LOG = Loggers.get(QualityGateCheck.class); + private static final EnumSet<TaskStatus> TASK_TERMINAL_STATUSES = EnumSet.of(TaskStatus.SUCCESS, TaskStatus.FAILED, TaskStatus.CANCELED); + private static final int POLLING_INTERVAL_IN_MS = 5000; + + private final DefaultScannerWsClient wsClient; + private final ScanProperties properties; + + private long qualityGateTimeoutInMs; + private boolean enabled; + + public QualityGateCheck(ScanProperties properties, DefaultScannerWsClient wsClient) { + this.wsClient = wsClient; + this.properties = properties; + } + + @Override + public void start() { + this.enabled = properties.shouldWaitForQualityGate(); + this.qualityGateTimeoutInMs = Duration.of(properties.qualityGateWaitTimeout(), ChronoUnit.SECONDS).toMillis(); + } + + @Override + public void stop() { + // nothing to do + } + + public void await() { + if (!enabled) { + LOG.debug("Quality Gate Check disabled - skipping"); + return; + } + + String taskId = readTaskIdFromMetaDataFile(); + + Ce.Task task = waitForCeTaskToFinish(taskId); + + if (!TaskStatus.SUCCESS.equals(task.getStatus())) { + throw MessageException.of(String.format("CE Task finished abnormally with status: %s", task.getStatus().name())); + } + + Status qualityGateStatus = getQualityGateStatus(task.getAnalysisId()); + + if (Status.OK.equals(qualityGateStatus)) { + LOG.info("Quality Gate - OK"); + } else if (Status.NONE.equals(qualityGateStatus)) { + LOG.info("No Quality Gate is associated with the analysis - skipping"); + } else { + LOG.info("Quality Gate - FAILED"); + throw MessageException.of("Quality Gate - FAILED"); + } + } + + private String readTaskIdFromMetaDataFile() { + Path file = properties.metadataFilePath(); + try (Reader reader = Files.newBufferedReader(file, StandardCharsets.UTF_8)) { + Properties metadata = new Properties(); + metadata.load(reader); + return metadata.getProperty("ceTaskId"); + } catch (IOException e) { + throw new IllegalStateException("Unable to read metadata file: " + file, e); + } + } + + private Ce.Task waitForCeTaskToFinish(String taskId) { + GetRequest getTaskResultReq = new GetRequest("api/ce/task") + .setMediaType(MediaTypes.PROTOBUF) + .setParam("id", taskId); + + long currentTime = 0; + while (qualityGateTimeoutInMs > currentTime) { + try { + WsResponse getTaskResultResponse = wsClient.call(getTaskResultReq).failIfNotSuccessful(); + Ce.Task task = parseCeTaskResponse(getTaskResultResponse); + if (TASK_TERMINAL_STATUSES.contains(task.getStatus())) { + return task; + } + LOG.debug("Received CE task with status {} ", task.getStatus()); + LOG.info("Waiting {} ms for task to finish...", POLLING_INTERVAL_IN_MS); + + Thread.sleep(POLLING_INTERVAL_IN_MS); + currentTime += POLLING_INTERVAL_IN_MS; + } catch (HttpException e) { + throw MessageException.of(String.format("Failed to get CE Task status - %s", DefaultScannerWsClient.createErrorMessage(e))); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw MessageException.of("Quality Gate Check has been interrupted", e); + } + } + throw MessageException.of("Quality Gate Check timeout exceeded"); + } + + private static Ce.Task parseCeTaskResponse(WsResponse response) { + try (InputStream protobuf = response.contentStream()) { + return Ce.TaskResponse.parser().parseFrom(protobuf).getTask(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private Status getQualityGateStatus(String analysisId) { + GetRequest getQualityGateReq = new GetRequest("api/qualitygates/project_status") + .setMediaType(MediaTypes.PROTOBUF) + .setParam("analysisId", analysisId); + try { + WsResponse getTaskResultResponse = wsClient.call(getQualityGateReq).failIfNotSuccessful(); + Qualitygates.ProjectStatusResponse.ProjectStatus status = parseQualityGateResponse(getTaskResultResponse); + return status.getStatus(); + } catch (HttpException e) { + throw MessageException.of(String.format("Failed to get Quality Gate status - %s", DefaultScannerWsClient.createErrorMessage(e))); + } + } + + private static Qualitygates.ProjectStatusResponse.ProjectStatus parseQualityGateResponse(WsResponse response) { + try (InputStream protobuf = response.contentStream()) { + return Qualitygates.ProjectStatusResponse.parser().parseFrom(protobuf).getProjectStatus(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} |