-
Notifications
You must be signed in to change notification settings - Fork 3
SED-4157-executions history #577
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This might be true now, but can change in the future, you should first check the execution end time and only if not set set use the current time. |
||
| List<ExecutionResultSnapshot> pastExecutionsSnapshots = executionAccessor.getLastEndedExecutionsByCanonicalPlanName(execution.getCanonicalPlanName(), 10, searchBeforeTimestamp) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. agreed, get the value from the configuration (step.properties) with a default value of 10. |
||
| .stream() | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. getLastEndedExecutionsByCanonicalPlanName should return a stream directly |
||
| .map(e -> new ExecutionResultSnapshot() | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Think of the information you'll need to also display a tooltip, at least the execution start time would be required too |
||
| .setId(e.getId().toString()) | ||
| .setResult(e.getResult()) | ||
| .setStatus(e.getStatus()) | ||
| ) | ||
| .collect(Collectors.toList()); | ||
| execution.setHistoryResults(pastExecutionsSnapshots); | ||
| executionAccessor.save(execution); | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -86,9 +86,27 @@ protected PlanRunnerResult execute() { | |
| addImportResultToExecution(importResult); | ||
| saveFailureReportWithResult(ReportNodeStatus.VETOED); | ||
| } else { | ||
| String canonicalPlanName; | ||
| try { | ||
| Plan plan = getPlanFromExecutionParametersOrImport(); | ||
| ExecutionParameters executionParameters = executionContext.getExecutionParameters(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So as discussed please revert to calling the method getPlanFromExecutionParametersOrImport, and imply update the execution canonical name from within that method. We already persist the import result their. |
||
| Plan plan = executionParameters.getPlan(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could simply check where we use the executionParameters.setPlan to see that this case is used for interactive execution (see startInteractiveSession in InteractiveServices), if you want more details or debug the case. This case is edgy and it would actually be cleaner and more correct to not create any historical statuses for them (or even ingest any time-series for them). For now I would then not set the canonical name and handle null value in the plugin |
||
| 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(); | ||
|
Comment on lines
+104
to
+105
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When a plan is passed directly in This will require adding // plan already exists in memory
RepositoryObjectReference repositoryObject = executionParameters.getRepositoryObject();
if (repositoryObject != null) {
canonicalPlanName = getCanonicalPlanName(repositoryObject.getRepositoryParameters());
} else {
canonicalPlanName = plan.getId().toString();
} |
||
| } | ||
|
|
||
| addPlanToContextAndUpdateExecution(plan); | ||
| addCanonicalPlanNameToExecution(canonicalPlanName); | ||
|
|
||
| logger.info(messageWithId("Starting execution.")); | ||
| updateStatus(ExecutionStatus.ESTIMATING); | ||
|
|
@@ -176,6 +194,7 @@ private List<ExecutionVeto> 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(); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -44,6 +44,7 @@ public class Execution extends AbstractOrganizableObject implements EnricheableO | |
| private ReportNodeStatus result; | ||
| private List<Error> lifecycleErrors; | ||
| private String planId; | ||
| private String canonicalPlanName; | ||
| private ImportResult importResult; | ||
| private List<ReportExport> 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<ExecutionResultSnapshot> 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<ExecutionResultSnapshot> getHistoryResults() { | ||
| return historyResults; | ||
| } | ||
|
|
||
| public Execution setHistoryResults(List<ExecutionResultSnapshot> historyResults) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use at standard setter, must be void |
||
| this.historyResults = historyResults; | ||
| return this; | ||
| } | ||
|
|
||
| @Override | ||
| public String toString() { | ||
| return "Execution [startTime=" + startTime + ", endTime=" + endTime + ", description=" + description | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -51,4 +51,5 @@ public interface ExecutionAccessor extends Accessor<Execution>, ExecutionProvide | |
|
|
||
| List<Execution> getLastEndedExecutionsBySchedulerTaskID(String schedulerTaskID, int limit, Long from, Long to); | ||
|
|
||
| List<Execution> getLastEndedExecutionsByCanonicalPlanName(String canonicalPlanName, int limit, Long from); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should return the stream directly |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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<Execution> getLastEndedExecutionsBySchedulerTaskID(String schedulerT | |
| order, 0, limit, 0) | ||
| .collect(Collectors.toList()); | ||
| } | ||
|
|
||
| @Override | ||
| public List<Execution> getLastEndedExecutionsByCanonicalPlanName(String canonicalPlanName, int limit, Long from) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The provided timestamp is used to filter execution ending before it, please rename accordingly |
||
| SearchOrder order = new SearchOrder("endTime", -1); | ||
|
|
||
| List<Filter> 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()); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use standard setters (void) for POJO, this is not a builder. |
||
| 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; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -39,6 +39,18 @@ private static Map<String, String> getCanonicalRepositoryParameters(Set<String> | |
| .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)) : null; | ||
| } | ||
|
|
||
| public static String getCanonicalPlanName(Map<String, String> repositoryParameters) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This cannot be a static method since you need access to the canonical keys set |
||
| if (repositoryParameters == null || repositoryParameters.isEmpty()) { | ||
| return ""; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should return null to be consistent and let the caller know that no canonical name exist |
||
| } | ||
|
|
||
| return repositoryParameters.entrySet().stream() | ||
| .sorted(Map.Entry.comparingByKey()) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are not filtering by canonicalRepositoryParameters, this is not a canonical representation. |
||
| .map(e -> e.getKey() + "=" + e.getValue()) | ||
| .collect(java.util.stream.Collectors.joining("&")); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| } | ||
|
|
||
|
|
||
| @Override | ||
| public Set<String> getCanonicalRepositoryParameters() { | ||
| return canonicalRepositoryParameters; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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())); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cannot be a static call, you have the repository instance right above. |
||
| return importResult; | ||
| } | ||
|
|
||
| public Repository getRepository(String respositoryId) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a typo in the comment. It should be
TODO.