diff --git a/step-controller/step-controller-backend/src/test/resources/step.properties b/step-controller/step-controller-backend/src/test/resources/step.properties index f821e516c1..cd21b24d06 100644 --- a/step-controller/step-controller-backend/src/test/resources/step.properties +++ b/step-controller/step-controller-backend/src/test/resources/step.properties @@ -53,7 +53,7 @@ plugins.selenium.libs.3.x=../../../../step-distribution/step-distribution-parent plugins.selenium.libs.2.x=../../../../step-distribution/step-distribution-parent/step-distribution-controller/template-controller/ext/selenium/selenium-2.53.1 plugins.jmeter.home=../../../../step-distribution/step-distribution-parent/step-distribution-controller/template-controller/ext/jmeter plugins.javascript.libs=../../../../step-distribution/step-distribution-parent/step-distribution-controller/template-controller/ext/javascript -plugins.groovy.libs=../../../../step-distribution/step-distribution-parent/step-distribution-controller/template-controller/ext/groovy +plugins.groovy.libs=../../../step-distribution/step-distribution-controller/template-controller/ext/groovy authentication=true # Defines the path to the embedded function packages # plugins.FunctionPackagePlugin.embeddedpackages.folder=../../../../step-distribution/step-distribution-parent/step-distribution-controller/template-controller/plugins/keywords diff --git a/step-core/src/main/java/step/core/repositories/ImportResult.java b/step-core/src/main/java/step/core/repositories/ImportResult.java index 7df94fd1f4..a5f9dda80e 100644 --- a/step-core/src/main/java/step/core/repositories/ImportResult.java +++ b/step-core/src/main/java/step/core/repositories/ImportResult.java @@ -28,6 +28,7 @@ public class ImportResult implements Serializable { protected boolean successful = false;; protected String planId; + protected String canonicalPlanName; List errors; @@ -47,6 +48,14 @@ public void setPlanId(String planId) { this.planId = planId; } + public String getCanonicalPlanName() { + return canonicalPlanName; + } + + public void setCanonicalPlanName(String canonicalPlanName) { + this.canonicalPlanName = canonicalPlanName; + } + public List getErrors() { return errors; } diff --git a/step-plans/step-plans-base-artefacts/src/main/java/step/reporting/ExecutionHistoryReportPlugin.java b/step-plans/step-plans-base-artefacts/src/main/java/step/reporting/ExecutionHistoryReportPlugin.java new file mode 100644 index 0000000000..b2adb0d9a9 --- /dev/null +++ b/step-plans/step-plans-base-artefacts/src/main/java/step/reporting/ExecutionHistoryReportPlugin.java @@ -0,0 +1,39 @@ +package step.reporting; + +import step.core.execution.ExecutionContext; +import step.core.execution.model.Execution; +import step.core.execution.model.ExecutionAccessor; +import step.core.execution.model.ExecutionResultSnapshot; +import step.core.plugins.Plugin; +import step.engine.plugins.AbstractExecutionEnginePlugin; + +import java.util.List; +import java.util.stream.Collectors; + +@Plugin +public class ExecutionHistoryReportPlugin extends AbstractExecutionEnginePlugin { + + @Override + public void executionStart(ExecutionContext context) { + // TOOD collect the executions here too + } + + @Override + public void afterExecutionEnd(ExecutionContext context) { + ExecutionAccessor executionAccessor = context.getExecutionAccessor(); + Execution execution = context.getExecutionManager().getExecution(); + // endTime is not set here + long searchBeforeTimestamp = System.currentTimeMillis() - 1; + List pastExecutionsSnapshots = executionAccessor.getLastEndedExecutionsByCanonicalPlanName(execution.getCanonicalPlanName(), 10, searchBeforeTimestamp) + .stream() + .map(e -> new ExecutionResultSnapshot() + .setId(e.getId().toString()) + .setResult(e.getResult()) + .setStatus(e.getStatus()) + ) + .collect(Collectors.toList()); + execution.setHistoryResults(pastExecutionsSnapshots); + executionAccessor.save(execution); + } + +} diff --git a/step-plans/step-plans-core/src/main/java/step/core/execution/ExecutionEngineRunner.java b/step-plans/step-plans-core/src/main/java/step/core/execution/ExecutionEngineRunner.java index da368e5a25..db58cef5fd 100644 --- a/step-plans/step-plans-core/src/main/java/step/core/execution/ExecutionEngineRunner.java +++ b/step-plans/step-plans-core/src/main/java/step/core/execution/ExecutionEngineRunner.java @@ -86,9 +86,27 @@ protected PlanRunnerResult execute() { addImportResultToExecution(importResult); saveFailureReportWithResult(ReportNodeStatus.VETOED); } else { + String canonicalPlanName; try { - Plan plan = getPlanFromExecutionParametersOrImport(); + ExecutionParameters executionParameters = executionContext.getExecutionParameters(); + Plan plan = executionParameters.getPlan(); + if (plan == null) { + ImportResult importResult = importPlan(executionContext); + canonicalPlanName = importResult.getCanonicalPlanName(); + addImportResultToExecution(importResult); + if (importResult.isSuccessful()) { + PlanAccessor planAccessor = executionContext.getPlanAccessor(); + plan = planAccessor.get(new ObjectId(importResult.getPlanId())); + } else { + throw new PlanImportException(); + } + } else { + // plan already exists in memory + canonicalPlanName = plan.getId().toString(); + } + addPlanToContextAndUpdateExecution(plan); + addCanonicalPlanNameToExecution(canonicalPlanName); logger.info(messageWithId("Starting execution.")); updateStatus(ExecutionStatus.ESTIMATING); @@ -176,6 +194,7 @@ private List getExecutionVetoes() { .collect(Collectors.toList()); } + // TODO from here we need to extract the importResult, somehow private Plan getPlanFromExecutionParametersOrImport() throws PlanImportException { ExecutionParameters executionParameters = executionContext.getExecutionParameters(); Plan executionParametersPlan = executionParameters.getPlan(); @@ -209,6 +228,10 @@ private void addPlanToContextAndUpdateExecution(Plan plan) { }); } + private void addCanonicalPlanNameToExecution(String canonicalPlanName) { + updateExecution(execution -> execution.setCanonicalPlanName(canonicalPlanName)); + } + private String messageWithId(String message) { return message + " Execution ID: " + executionContext.getExecutionId(); } diff --git a/step-plans/step-plans-core/src/main/java/step/core/execution/model/Execution.java b/step-plans/step-plans-core/src/main/java/step/core/execution/model/Execution.java index 81ef0004d7..097b3ef602 100644 --- a/step-plans/step-plans-core/src/main/java/step/core/execution/model/Execution.java +++ b/step-plans/step-plans-core/src/main/java/step/core/execution/model/Execution.java @@ -44,6 +44,7 @@ public class Execution extends AbstractOrganizableObject implements EnricheableO private ReportNodeStatus result; private List lifecycleErrors; private String planId; + private String canonicalPlanName; private ImportResult importResult; private List reportExports; private String executionTaskID; @@ -54,6 +55,7 @@ public class Execution extends AbstractOrganizableObject implements EnricheableO private ExecutiontTaskParameters executiontTaskParameters; private String resolvedPlanRootNodeId; private String agentsInvolved; + private List historyResults; public Execution() { super(); @@ -148,6 +150,14 @@ public void setPlanId(String planId) { this.planId = planId; } + public String getCanonicalPlanName() { + return canonicalPlanName; + } + + public void setCanonicalPlanName(String canonicalPlanName) { + this.canonicalPlanName = canonicalPlanName; + } + /** * @return the result of the import phase from the external repository (ALM, Jira, etc) */ @@ -245,6 +255,15 @@ public void setAgentsInvolved(String agentsInvolved) { this.agentsInvolved = agentsInvolved; } + public List getHistoryResults() { + return historyResults; + } + + public Execution setHistoryResults(List historyResults) { + this.historyResults = historyResults; + return this; + } + @Override public String toString() { return "Execution [startTime=" + startTime + ", endTime=" + endTime + ", description=" + description diff --git a/step-plans/step-plans-core/src/main/java/step/core/execution/model/ExecutionAccessor.java b/step-plans/step-plans-core/src/main/java/step/core/execution/model/ExecutionAccessor.java index 11c590f014..6690061a1c 100644 --- a/step-plans/step-plans-core/src/main/java/step/core/execution/model/ExecutionAccessor.java +++ b/step-plans/step-plans-core/src/main/java/step/core/execution/model/ExecutionAccessor.java @@ -51,4 +51,5 @@ public interface ExecutionAccessor extends Accessor, ExecutionProvide List getLastEndedExecutionsBySchedulerTaskID(String schedulerTaskID, int limit, Long from, Long to); + List getLastEndedExecutionsByCanonicalPlanName(String canonicalPlanName, int limit, Long from); } diff --git a/step-plans/step-plans-core/src/main/java/step/core/execution/model/ExecutionAccessorImpl.java b/step-plans/step-plans-core/src/main/java/step/core/execution/model/ExecutionAccessorImpl.java index f4733ca29e..89aff5f9fb 100644 --- a/step-plans/step-plans-core/src/main/java/step/core/execution/model/ExecutionAccessorImpl.java +++ b/step-plans/step-plans-core/src/main/java/step/core/execution/model/ExecutionAccessorImpl.java @@ -46,6 +46,8 @@ public void createIndexesIfNeeded(Long ttl) { new IndexField("endTime",Order.DESC, null)))); collectionDriver.createOrUpdateCompoundIndex(new LinkedHashSet<>(List.of(new IndexField("planId",Order.ASC, null), new IndexField("endTime",Order.DESC, null)))); + collectionDriver.createOrUpdateCompoundIndex(new LinkedHashSet<>(List.of(new IndexField("canonicalPlanName",Order.ASC, null), + new IndexField("endTime",Order.DESC, null)))); } @Override @@ -174,4 +176,22 @@ public List getLastEndedExecutionsBySchedulerTaskID(String schedulerT order, 0, limit, 0) .collect(Collectors.toList()); } + + @Override + public List getLastEndedExecutionsByCanonicalPlanName(String canonicalPlanName, int limit, Long from) { + SearchOrder order = new SearchOrder("endTime", -1); + + List filters = new ArrayList<>(List.of( + Filters.equals("canonicalPlanName", canonicalPlanName), + Filters.equals("status", ExecutionStatus.ENDED.name()) + )); + + if (from != null) { + filters.add(Filters.lte("endTime", from)); + } + + return collectionDriver + .find(Filters.and(filters), order, 0, limit, 0) + .collect(Collectors.toList()); + } } diff --git a/step-plans/step-plans-core/src/main/java/step/core/execution/model/ExecutionResultSnapshot.java b/step-plans/step-plans-core/src/main/java/step/core/execution/model/ExecutionResultSnapshot.java new file mode 100644 index 0000000000..119019dda1 --- /dev/null +++ b/step-plans/step-plans-core/src/main/java/step/core/execution/model/ExecutionResultSnapshot.java @@ -0,0 +1,37 @@ +package step.core.execution.model; + +import step.core.artefacts.reports.ReportNodeStatus; + +public class ExecutionResultSnapshot { + + private String id; + private ExecutionStatus status; + private ReportNodeStatus result; + + public String getId() { + return id; + } + + public ExecutionResultSnapshot setId(String id) { + this.id = id; + return this; + } + + public ExecutionStatus getStatus() { + return status; + } + + public ExecutionResultSnapshot setStatus(ExecutionStatus status) { + this.status = status; + return this; + } + + public ReportNodeStatus getResult() { + return result; + } + + public ExecutionResultSnapshot setResult(ReportNodeStatus result) { + this.result = result; + return this; + } +} diff --git a/step-plans/step-plans-core/src/main/java/step/core/repositories/AbstractRepository.java b/step-plans/step-plans-core/src/main/java/step/core/repositories/AbstractRepository.java index f48a4ce163..390c8efd20 100644 --- a/step-plans/step-plans-core/src/main/java/step/core/repositories/AbstractRepository.java +++ b/step-plans/step-plans-core/src/main/java/step/core/repositories/AbstractRepository.java @@ -39,6 +39,18 @@ private static Map getCanonicalRepositoryParameters(Set .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)) : null; } + public static String getCanonicalPlanName(Map repositoryParameters) { + if (repositoryParameters == null || repositoryParameters.isEmpty()) { + return ""; + } + + return repositoryParameters.entrySet().stream() + .sorted(Map.Entry.comparingByKey()) + .map(e -> e.getKey() + "=" + e.getValue()) + .collect(java.util.stream.Collectors.joining("&")); + } + + @Override public Set getCanonicalRepositoryParameters() { return canonicalRepositoryParameters; diff --git a/step-plans/step-plans-core/src/main/java/step/core/repositories/Repository.java b/step-plans/step-plans-core/src/main/java/step/core/repositories/Repository.java index ae4598515e..0f0f1f9053 100644 --- a/step-plans/step-plans-core/src/main/java/step/core/repositories/Repository.java +++ b/step-plans/step-plans-core/src/main/java/step/core/repositories/Repository.java @@ -54,4 +54,5 @@ default void postExecution(ExecutionContext context, RepositoryObjectReference r boolean compareCanonicalRepositoryParameters(Map repositoryParameters1, Map repositoryParameters2); Set getCanonicalRepositoryParameters(); + } diff --git a/step-plans/step-plans-core/src/main/java/step/core/repositories/RepositoryObjectManager.java b/step-plans/step-plans-core/src/main/java/step/core/repositories/RepositoryObjectManager.java index 3e8fe9807d..9afe5f4f09 100644 --- a/step-plans/step-plans-core/src/main/java/step/core/repositories/RepositoryObjectManager.java +++ b/step-plans/step-plans-core/src/main/java/step/core/repositories/RepositoryObjectManager.java @@ -31,6 +31,8 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; +import static step.core.repositories.AbstractRepository.getCanonicalPlanName; + public class RepositoryObjectManager { private static final Logger logger = LoggerFactory.getLogger(RepositoryObjectManager.class); @@ -48,7 +50,9 @@ public void registerRepository(String id, Repository repository) { public ImportResult importPlan(ExecutionContext context, RepositoryObjectReference artefact) throws Exception { String respositoryId = artefact.getRepositoryID(); Repository repository = getRepository(respositoryId); - return repository.importArtefact(context, artefact.getRepositoryParameters()); + ImportResult importResult = repository.importArtefact(context, artefact.getRepositoryParameters()); + importResult.setCanonicalPlanName(getCanonicalPlanName(artefact.getRepositoryParameters())); + return importResult; } public Repository getRepository(String respositoryId) {