All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.blazemeter.ciworkflow.CiBuild Maven / Gradle / Ivy

The newest version!
/**
 * Copyright 2018 BlazeMeter Inc.
 * 

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.blazemeter.ciworkflow; import com.blazemeter.api.exception.InterruptRuntimeException; import com.blazemeter.api.exception.ValidationException; import com.blazemeter.api.explorer.Master; import com.blazemeter.api.explorer.test.AbstractTest; import com.blazemeter.api.explorer.test.MultiTest; import com.blazemeter.api.explorer.test.SingleTest; import com.blazemeter.api.explorer.test.TestDetector; import com.blazemeter.api.logging.Logger; import com.blazemeter.api.logging.UserNotifier; import com.blazemeter.api.utils.BlazeMeterUtils; import net.sf.json.JSONArray; import net.sf.json.JSONObject; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang.StringUtils; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.List; public class CiBuild { protected final UserNotifier notifier; protected final Logger logger; protected final BlazeMeterUtils utils; protected final String testId; protected File mainTestFile; protected List additionalTestFiles; protected final String properties; protected final String notes; protected final CiPostProcess ciPostProcess; protected String publicReport; protected AbstractTest currentTest; protected String workspaceId; protected String reportName; private final String FUNCTIONAL_GUI_TEST = "functionalGui"; private final String TEST_SUITE = "functionalTestSuite"; public CiBuild(BlazeMeterUtils utils, String testId, File mainTestFile, List additionalTestFiles, String properties, String notes, CiPostProcess ciPostProcess) { this(utils, testId, properties, notes, ciPostProcess); this.mainTestFile = mainTestFile; this.additionalTestFiles = additionalTestFiles; } public CiBuild(BlazeMeterUtils utils, String testId, String properties, String notes, CiPostProcess ciPostProcess) { this.utils = utils; this.testId = testId; this.properties = properties; this.notes = notes; this.ciPostProcess = ciPostProcess; this.notifier = utils.getNotifier(); this.logger = utils.getLogger(); } public void setWorkspaceId(String workspaceId) { this.workspaceId = workspaceId; } public void setReportName(String reportName) { this.reportName = reportName; } /** * Describes the common workflow of all CI plugins. * Executes ci build * * @return BuildResult */ public BuildResult execute() { try { Master master = start(); if (master == null) { logger.error("Set Build status [FAILED]."); notifier.notifyError("Set Build status [FAILED]."); return BuildResult.FAILED; } return waitForFinishAndDoPostProcess(master); } catch (IOException e) { logger.error("Caught exception. Set Build status [FAILED]. Reason is: " + e.getMessage(), e); notifier.notifyError("Caught exception. Set Build status [FAILED]. Reason is: " + e.getMessage()); return BuildResult.FAILED; } catch (InterruptedException | InterruptRuntimeException e) { logger.error("Caught exception. Set Build status [ABORTED]. Reason is: " + e.getMessage(), e); notifier.notifyError("Caught exception. Set Build status [ABORTED]."); return BuildResult.ABORTED; } } protected BuildResult waitForFinishAndDoPostProcess(Master master) throws IOException { try { waitForFinish(master); return doPostProcess(master); } catch (InterruptedException | InterruptRuntimeException ex) { logger.warn("Caught InterruptedException, execute postProcess. " + ex.getMessage()); boolean hasReports = interrupt(master); if (hasReports) { doPostProcess(master); } // because build has been aborted return BuildResult.ABORTED; } } /** * Start Test with 'testId' in BlazeMeter * and the Post properties and notes to Master * * @return Master of started Test */ public Master start() throws IOException, InterruptedException { notifier.notifyInfo("CiBuild is started."); currentTest = TestDetector.detectTest(utils, testId); if (ciPostProcess != null) { ciPostProcess.setTest(currentTest.getId(), currentTest.getTestType()); } if (currentTest == null) { logger.error("Failed to detect test type. Test with id=" + testId + " not found."); notifier.notifyError("Failed to detect test type. Test with id = " + testId + " not found."); return null; } updateTestFiles(); notifier.notifyInfo(String.format("Start test id : %s, name : %s", currentTest.getId(), currentTest.getName())); return startTest(currentTest); } protected void updateTestFiles() throws IOException, InterruptedException { if (currentTest instanceof SingleTest && isSupportTestFiles(currentTest)) { SingleTest test = (SingleTest) currentTest; updateMainTestFile(test); updateAdditionalTestFiles(test); validateTestFiles(test); } else { if ((mainTestFile != null) || (additionalTestFiles != null && !additionalTestFiles.isEmpty())) { notifier.notifyWarning("Current test does not support uploading script files"); } } } protected void validateTestFiles(SingleTest test) throws IOException, InterruptedException { List fileNames = new ArrayList<>(); if (mainTestFile != null) { fileNames.add(mainTestFile.getName()); } if (additionalTestFiles != null && !additionalTestFiles.isEmpty()) { for (File file : additionalTestFiles) { fileNames.add(file.getName()); } } if (!fileNames.isEmpty()) { test.validateFiles(fileNames); waitForValidations(test, fileNames); } } protected void waitForValidations(SingleTest test, List fileNames) throws IOException, InterruptedException { boolean isValidationFinished = false; while (!isValidationFinished) { JSONArray validations = test.validations(); isValidationFinished = checkFilesValidation(fileNames, validations); if (!isValidationFinished) { Thread.sleep(1000); } } logger.info(String.format("Validation for files %s finished successfully", Arrays.toString(fileNames.toArray(new String[0])))); notifier.notifyInfo(String.format("Validation for files %s finished successfully", Arrays.toString(fileNames.toArray(new String[0])))); } /** * @return true - if success, false - is not finished and throws RuntimeException if have validation errors */ protected boolean checkFilesValidation(List fileNames, JSONArray validations) { boolean isFinished = true; for (int i = 0; i < validations.size(); i++) { JSONObject object = validations.getJSONObject(i); String fileName = object.getString("fileName"); if (fileNames.contains(fileName)) { boolean checkFile = checkFileValidation(fileName, object); if (!checkFile) { logger.info(String.format("Validation for file=%s is not ready, will be repeat", fileName)); isFinished = false; } } else { logger.debug("Skipping " + object + ", because this file was not update"); } } return isFinished; } protected boolean checkFileValidation(String fileName, JSONObject object) { int status = object.getInt("status"); if (status >= 100) { JSONArray errors = object.getJSONArray("errors"); if (!errors.isEmpty()) { notifier.notifyError(String.format("Validation error: file=%s; errors=%s", fileName, errors)); logger.error(String.format("Validation error: file=%s; errors=%s", fileName, errors)); throw new ValidationException(String.format("Validation error: file=%s; errors=%s", fileName, errors)); } logger.info("File " + fileName + " successfully validated"); return true; } else { logger.info("Validation for file=" + fileName + " is not ready. status=" + status); return false; } } protected boolean isSupportTestFiles(AbstractTest test) { return "jmeter".equals(test.getTestType()) || "taurus".equals(test.getTestType()) || "functionalApi".equals(test.getTestType()); } protected void updateMainTestFile(SingleTest test) throws IOException { if (mainTestFile != null) { String filepath = FilenameUtils.normalize(mainTestFile.getAbsolutePath()); logger.info("Update main test file: " + filepath); notifier.notifyInfo("Update main test file: " + filepath); test.uploadFile(mainTestFile); validateTestFileName(mainTestFile.getName()); test.updateTestFilename(mainTestFile.getName()); } } protected void validateTestFileName(String filename) { if (!filename.endsWith(".jmx") && !filename.endsWith(".yml") && !filename.endsWith(".yaml")) { notifier.notifyWarning("Unknown script type. Please, select 'Test type' in BlazeMeter web application"); } } protected void updateAdditionalTestFiles(SingleTest test) throws IOException { if (additionalTestFiles != null && !additionalTestFiles.isEmpty()) { for (File file : additionalTestFiles) { String filepath = FilenameUtils.normalize(file.getAbsolutePath()); logger.info("Upload additional test file: " + filepath); notifier.notifyInfo("Upload additional test file: " + filepath); test.uploadFile(file); } } } protected Master startTest(AbstractTest test) throws IOException, InterruptedException { Master master; if (!StringUtils.isBlank(properties) && test instanceof SingleTest) { notifier.notifyInfo("Sent properties: " + properties); master = test.startWithProperties(properties); } else { master = test.start(); } Calendar startTime = Calendar.getInstance(); startTime.setTimeInMillis(System.currentTimeMillis()); notifier.notifyInfo("Test has been started successfully at " + startTime.getTime().toString() + ". Master id=" + master.getId()); try { setReportName(master); if (test.getTestType().equals(FUNCTIONAL_GUI_TEST)) { String serverReport = master.getServerReport(workspaceId, test.getId()); notifier.notifyInfo("Test report will be available at " + serverReport); waitForFinish(master); } if(test.getTestType().equals(TEST_SUITE)) { String serverReport = master.getServerReportForTestSuite(workspaceId, test.getId()); notifier.notifyInfo("Test report will be available at " + serverReport); waitForFinish(master); } master.setTestType(test.getTestType()); generatePublicReport(master); skipInitState(master); if (!StringUtils.isBlank(properties) && test instanceof MultiTest) { notifier.notifyInfo("Sent properties: " + properties); master.postProperties(properties); } postNotes(master); } catch (InterruptedException | InterruptRuntimeException ex) { logger.warn("Interrupt master", ex); boolean hasReports = interrupt(master); if (hasReports) { doPostProcess(master); } throw new InterruptedException("Interrupt master"); } return master; } protected void generatePublicReport(Master master) throws InterruptedException { try { publicReport = master.getPublicReport(); notifier.notifyInfo("Public test report will be available at " + publicReport); } catch (InterruptedException | InterruptRuntimeException ex) { logger.warn("Interrupt while get public report", ex); throw ex; } catch (Exception ex) { logger.warn("Cannot get public token", ex); notifier.notifyWarning("Cannot get public token"); } } protected void postNotes(Master master) throws InterruptedException { try { if (!StringUtils.isBlank(notes)) { notifier.notifyInfo("Sent notes: " + notes); master.postNotes(notes); } } catch (InterruptedException | InterruptRuntimeException ex) { logger.warn("Interrupt while post notes", ex); throw ex; } catch (Exception ex) { logger.warn("Cannot post notes", ex); notifier.notifyWarning("Cannot post notes"); } } protected void setReportName(Master master) throws InterruptedException { try { if (!StringUtils.isBlank(reportName)) { notifier.notifyInfo("Setting report name : " + reportName); master.setReportName(reportName); } } catch (InterruptedException | InterruptRuntimeException ex) { logger.warn("Interrupt while setting report name", ex); throw ex; } catch (Exception ex) { logger.warn("Cannot set report name", ex); notifier.notifyWarning("Cannot set report name"); } } /** * Skip INIT state. * It should be done before post notes and post session properties */ protected void skipInitState(Master master) throws InterruptedException, IOException { int n = 1; long bzmCheckTimeout = BlazeMeterUtils.getCheckTimeout(); while (n < 6) { try { Thread.sleep(bzmCheckTimeout); int statusCode = master.getStatus(); if (statusCode > 0) { break; } } catch (InterruptedException | InterruptRuntimeException ex) { logger.warn("Caught InterruptedException while skip InitState"); throw ex; } catch (Exception e) { logger.warn("Failed to skip INIT state"); return; } finally { n++; } } } /** * Interrupt Build. * * @return true - if build has reports. */ public boolean interrupt(Master master) throws IOException { notifier.notifyWarning("Build has been interrupted"); boolean hasReports = false; int statusCode = master.getStatus(); if (statusCode < 100 && statusCode != 0) { master.terminate(); } if (statusCode >= 100 || statusCode == -1 || statusCode == 0) { master.stop(); hasReports = true; } return hasReports; } /** * Waits until test will be over on server * Master object corresponds to master session which is created after test was started. * * @throws InterruptedException IOException */ public void waitForFinish(Master master) throws InterruptedException, IOException { long start = System.currentTimeMillis(); long lastPrint = start; long bzmCheckTimeout = BlazeMeterUtils.getCheckTimeout(); long bzmMinute = Long.parseLong(System.getProperty("bzm.minute", "60000")); while (true) { Thread.sleep(bzmCheckTimeout); if (master.getStatus() == 140) { return; } long now = System.currentTimeMillis(); if (now - lastPrint > bzmMinute) { notifier.notifyInfo("Check if the test is still running. Time passed since start: " + ((now - start) / 1000 / 60) + " minutes."); lastPrint = now; } checkAborted(); } } // TODO: is it really need? protected void checkAborted() throws InterruptedException { if (Thread.interrupted()) { logger.warn("Job was stopped by user"); notifier.notifyError("Job was stopped by user"); throw new InterruptedException("Job was stopped by user"); } } /** * Run Post process action on Master */ public BuildResult doPostProcess(Master master) { return ciPostProcess.execute(master); } public BlazeMeterUtils getUtils() { return utils; } public String getTestId() { return testId; } public String getProperties() { return properties; } public String getNotes() { return notes; } public CiPostProcess getCiPostProcess() { return ciPostProcess; } public String getPublicReport() { return publicReport; } public void setPublicReport(String publicReport) { this.publicReport = publicReport; } public AbstractTest getCurrentTest() { return currentTest; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy