Skip to content

Commit f492cf9

Browse files
Fix hooks timeout
1 parent b60a0a7 commit f492cf9

File tree

18 files changed

+268
-128
lines changed

18 files changed

+268
-128
lines changed

multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/model/HookPhase.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public enum HookPhase {
3939
BLUE_GREEN_APPLICATION_BEFORE_START_LIVE("blue-green.application.before-start.live"),
4040
NONE("");
4141

42-
private static Map<String, HookPhase> namesToValues = Arrays.stream(HookPhase.values())
42+
private static final Map<String, HookPhase> NAMES_TO_VALUES = Arrays.stream(HookPhase.values())
4343
.collect(Collectors.toMap(hookPhase -> hookPhase.value,
4444
Function.identity()));
4545
private final String value;
@@ -49,7 +49,7 @@ public enum HookPhase {
4949
}
5050

5151
public static HookPhase fromString(String hookPhaseName) {
52-
HookPhase hookPhase = namesToValues.get(hookPhaseName);
52+
HookPhase hookPhase = NAMES_TO_VALUES.get(hookPhaseName);
5353
if (hookPhase == null) {
5454
throw new IllegalStateException(MessageFormat.format("Unsupported hook phase \"{0}\"", hookPhaseName));
5555
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package org.cloudfoundry.multiapps.controller.process.listeners;
2+
3+
import org.cloudfoundry.multiapps.controller.core.util.ApplicationConfiguration;
4+
import org.cloudfoundry.multiapps.controller.persistence.services.HistoricOperationEventService;
5+
import org.cloudfoundry.multiapps.controller.persistence.services.ProcessLoggerPersister;
6+
import org.cloudfoundry.multiapps.controller.persistence.services.ProcessLoggerProvider;
7+
import org.cloudfoundry.multiapps.controller.persistence.services.ProgressMessageService;
8+
import org.cloudfoundry.multiapps.controller.process.flowable.FlowableFacade;
9+
import org.cloudfoundry.multiapps.controller.process.util.StepLogger;
10+
import org.cloudfoundry.multiapps.controller.process.variables.Variables;
11+
import org.flowable.engine.delegate.DelegateExecution;
12+
13+
import jakarta.inject.Named;
14+
15+
@Named("hooksEndProcessListener")
16+
public class HooksEndProcessListener extends AbstractProcessExecutionListener {
17+
18+
protected HooksEndProcessListener(ProgressMessageService progressMessageService, StepLogger.Factory stepLoggerFactory,
19+
ProcessLoggerProvider processLoggerProvider, ProcessLoggerPersister processLoggerPersister,
20+
HistoricOperationEventService historicOperationEventService, FlowableFacade flowableFacade,
21+
ApplicationConfiguration configuration) {
22+
super(progressMessageService,
23+
stepLoggerFactory,
24+
processLoggerProvider,
25+
processLoggerPersister,
26+
historicOperationEventService,
27+
flowableFacade,
28+
configuration);
29+
}
30+
31+
@Override
32+
protected void notifyInternal(DelegateExecution execution) throws Exception {
33+
setVariableInParentProcess(execution, Variables.MUST_RESET_TIMEOUT.getName(), true);
34+
}
35+
}

multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/RestartAppStep.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@
44
import java.time.Duration;
55
import java.util.List;
66

7-
import jakarta.inject.Inject;
8-
import jakarta.inject.Named;
9-
107
import org.cloudfoundry.multiapps.controller.core.cf.CloudControllerClientFactory;
118
import org.cloudfoundry.multiapps.controller.core.model.HookPhase;
129
import org.cloudfoundry.multiapps.controller.core.security.token.TokenService;
@@ -22,6 +19,9 @@
2219
import com.sap.cloudfoundry.client.facade.domain.CloudApplication;
2320
import com.sap.cloudfoundry.client.facade.domain.CloudApplication.State;
2421

22+
import jakarta.inject.Inject;
23+
import jakarta.inject.Named;
24+
2525
@Named("restartAppStep")
2626
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
2727
public class RestartAppStep extends TimeoutAsyncFlowableStepWithHooks implements BeforeStepHookPhaseProvider {
@@ -96,6 +96,10 @@ protected List<AsyncExecution> getAsyncStepExecutions(ProcessContext context) {
9696

9797
@Override
9898
public Duration getTimeout(ProcessContext context) {
99+
Duration timeout = super.getTimeout(context);
100+
if (timeout != null) {
101+
return timeout;
102+
}
99103
return calculateTimeout(context, TimeoutType.START);
100104
}
101105
}

multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/SyncFlowableStepWithHooks.java

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
import java.util.List;
44

5-
import jakarta.inject.Inject;
6-
75
import org.cloudfoundry.multiapps.controller.core.cf.metadata.processor.MtaMetadataParser;
86
import org.cloudfoundry.multiapps.controller.process.util.HooksCalculator;
97
import org.cloudfoundry.multiapps.controller.process.util.HooksExecutor;
@@ -15,6 +13,9 @@
1513
import org.cloudfoundry.multiapps.controller.process.variables.Variables;
1614
import org.cloudfoundry.multiapps.mta.model.Hook;
1715
import org.cloudfoundry.multiapps.mta.model.Module;
16+
import org.flowable.engine.delegate.DelegateExecution;
17+
18+
import jakarta.inject.Inject;
1819

1920
public abstract class SyncFlowableStepWithHooks extends SyncFlowableStep {
2021

@@ -31,13 +32,15 @@ protected StepPhase executeStep(ProcessContext context) {
3132
StepPhase currentStepPhase = context.getVariable(Variables.STEP_PHASE);
3233
Module moduleToDeploy = moduleDeterminer.determineModuleToDeploy();
3334
HooksCalculator hooksCalculator = getHooksCalculator(context);
34-
HooksExecutor hooksExecutor = getHooksExecutor(hooksCalculator, moduleToDeploy);
35-
List<Hook> executedHooks = hooksExecutor.executeBeforeStepHooks(currentStepPhase);
36-
if (!executedHooks.isEmpty()) {
35+
HooksExecutor hooksExecutor = getHooksExecutor(hooksCalculator, moduleToDeploy, context.getExecution());
36+
List<Hook> hooksForExecution = hooksExecutor.executeBeforeStepHooks(currentStepPhase);
37+
context.setVariable(Variables.HOOKS_FOR_EXECUTION, hooksForExecution);
38+
if (!hooksForExecution.isEmpty()) {
3739
return currentStepPhase;
3840
}
3941
currentStepPhase = executeStepInternal(context);
40-
hooksExecutor.executeAfterStepHooks(currentStepPhase);
42+
hooksForExecution = hooksExecutor.executeAfterStepHooks(currentStepPhase);
43+
context.setVariable(Variables.HOOKS_FOR_EXECUTION, hooksForExecution);
4144
return currentStepPhase;
4245
}
4346

@@ -56,8 +59,8 @@ protected HooksCalculator getHooksCalculator(ProcessContext context) {
5659
.build();
5760
}
5861

59-
protected HooksExecutor getHooksExecutor(HooksCalculator hooksCalculator, Module moduleToDeploy) {
60-
return new HooksExecutor(hooksCalculator, moduleToDeploy);
62+
protected HooksExecutor getHooksExecutor(HooksCalculator hooksCalculator, Module moduleToDeploy, DelegateExecution delegateExecution) {
63+
return new HooksExecutor(hooksCalculator, moduleToDeploy, delegateExecution);
6164
}
6265

6366
protected abstract StepPhase executeStepInternal(ProcessContext context);

multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/TimeoutAsyncFlowableStep.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,12 @@ private long getStepStartTime(ProcessContext context) {
3939
context.getExecution()
4040
.setVariable(getStepStartTimeVariable(), stepStartTime);
4141
}
42-
42+
if (context.getVariable(Variables.MUST_RESET_TIMEOUT)) {
43+
stepStartTime = System.currentTimeMillis();
44+
context.getExecution()
45+
.setVariable(getStepStartTimeVariable(), stepStartTime);
46+
context.setVariable(Variables.MUST_RESET_TIMEOUT, false);
47+
}
4348
return stepStartTime;
4449
}
4550

multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/TimeoutAsyncFlowableStepWithHooks.java

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
package org.cloudfoundry.multiapps.controller.process.steps;
22

3+
import java.time.Duration;
34
import java.util.List;
45

5-
import jakarta.inject.Inject;
6-
76
import org.cloudfoundry.multiapps.controller.core.cf.metadata.processor.MtaMetadataParser;
87
import org.cloudfoundry.multiapps.controller.process.util.HooksCalculator;
98
import org.cloudfoundry.multiapps.controller.process.util.HooksExecutor;
@@ -12,9 +11,13 @@
1211
import org.cloudfoundry.multiapps.controller.process.util.ImmutableHooksCalculator;
1312
import org.cloudfoundry.multiapps.controller.process.util.ImmutableModuleDeterminer;
1413
import org.cloudfoundry.multiapps.controller.process.util.ModuleDeterminer;
14+
import org.cloudfoundry.multiapps.controller.process.util.TimeoutType;
1515
import org.cloudfoundry.multiapps.controller.process.variables.Variables;
1616
import org.cloudfoundry.multiapps.mta.model.Hook;
1717
import org.cloudfoundry.multiapps.mta.model.Module;
18+
import org.flowable.engine.delegate.DelegateExecution;
19+
20+
import jakarta.inject.Inject;
1821

1922
public abstract class TimeoutAsyncFlowableStepWithHooks extends TimeoutAsyncFlowableStep {
2023

@@ -27,18 +30,27 @@ public abstract class TimeoutAsyncFlowableStepWithHooks extends TimeoutAsyncFlow
2730

2831
@Override
2932
public StepPhase executeAsyncStep(ProcessContext context) {
30-
ModuleDeterminer moduleDeterminer = getModuleDeterminer(context);
3133
StepPhase currentStepPhase = context.getVariable(Variables.STEP_PHASE);
32-
Module moduleToDeploy = moduleDeterminer.determineModuleToDeploy();
33-
HooksCalculator hooksCalculator = getHooksCalculator(context);
34-
HooksExecutor hooksExecutor = getHooksExecutor(hooksCalculator, moduleToDeploy);
35-
List<Hook> executedHooks = hooksExecutor.executeBeforeStepHooks(currentStepPhase);
34+
List<Hook> executedHooks = executeHooksForExecution(context, currentStepPhase);
35+
context.setVariable(Variables.HOOKS_FOR_EXECUTION, executedHooks);
3636
if (!executedHooks.isEmpty()) {
3737
return currentStepPhase;
3838
}
3939
return executePollingStep(context);
4040
}
4141

42+
private List<Hook> executeHooksForExecution(ProcessContext context, StepPhase currentStepPhase) {
43+
HooksExecutor hooksExecutor = getHooksExecutor(context);
44+
return hooksExecutor.executeBeforeStepHooks(currentStepPhase);
45+
}
46+
47+
private HooksExecutor getHooksExecutor(ProcessContext context) {
48+
ModuleDeterminer moduleDeterminer = getModuleDeterminer(context);
49+
Module moduleToDeploy = moduleDeterminer.determineModuleToDeploy();
50+
HooksCalculator hooksCalculator = getHooksCalculator(context);
51+
return getHooksDeterminer(hooksCalculator, moduleToDeploy, context.getExecution());
52+
}
53+
4254
protected ModuleDeterminer getModuleDeterminer(ProcessContext context) {
4355
return ImmutableModuleDeterminer.builder()
4456
.context(context)
@@ -54,9 +66,20 @@ protected HooksCalculator getHooksCalculator(ProcessContext context) {
5466
.build();
5567
}
5668

57-
protected HooksExecutor getHooksExecutor(HooksCalculator hooksCalculator, Module moduleToDeploy) {
58-
return new HooksExecutor(hooksCalculator, moduleToDeploy);
69+
protected HooksExecutor getHooksDeterminer(HooksCalculator hooksCalculator, Module moduleToDeploy,
70+
DelegateExecution delegateExecution) {
71+
return new HooksExecutor(hooksCalculator, moduleToDeploy, delegateExecution);
5972
}
6073

6174
protected abstract StepPhase executePollingStep(ProcessContext context);
75+
76+
@Override
77+
public Duration getTimeout(ProcessContext context) {
78+
StepPhase currentStepPhase = context.getVariable(Variables.STEP_PHASE);
79+
if (!getHooksExecutor(context).determineBeforeStepHooks(currentStepPhase)
80+
.isEmpty()) {
81+
return calculateTimeout(context, TimeoutType.TASK);
82+
}
83+
return null;
84+
}
6285
}

multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/HooksCalculator.java

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,19 @@
66
import org.cloudfoundry.multiapps.controller.core.model.HookPhase;
77
import org.cloudfoundry.multiapps.controller.process.steps.ProcessContext;
88
import org.cloudfoundry.multiapps.controller.process.steps.StepPhase;
9-
import org.cloudfoundry.multiapps.controller.process.variables.Variables;
109
import org.cloudfoundry.multiapps.mta.model.Hook;
1110
import org.cloudfoundry.multiapps.mta.model.Module;
1211
import org.immutables.value.Value;
1312

1413
@Value.Immutable
1514
public abstract class HooksCalculator {
1615

17-
public List<Hook> calculateHooksForExecution(Module moduleToDeploy, StepPhase currentStepPhase) {
16+
public HooksWithPhases calculateHooksForExecution(Module moduleToDeploy, StepPhase currentStepPhase) {
1817
List<HookPhase> currentHookPhasesForExecution = determineHookPhaseForCurrentStepPhase(currentStepPhase);
19-
return getHooksForCurrentPhase(moduleToDeploy, currentHookPhasesForExecution);
18+
return ImmutableHooksWithPhases.builder()
19+
.hooks(getHooksForCurrentPhase(moduleToDeploy, currentHookPhasesForExecution))
20+
.hookPhases(currentHookPhasesForExecution)
21+
.build();
2022
}
2123

2224
private List<HookPhase> determineHookPhaseForCurrentStepPhase(StepPhase currentStepPhase) {
@@ -45,10 +47,6 @@ private ModuleHooksAggregator getModuleHooksAggregator(Module moduleToDeploy) {
4547
return new ModuleHooksAggregator(getContext().getExecution(), moduleToDeploy);
4648
}
4749

48-
public void setHooksForExecution(List<Hook> hooksForExecution) {
49-
getContext().setVariable(Variables.HOOKS_FOR_EXECUTION, hooksForExecution);
50-
}
51-
5250
public abstract ProcessContext getContext();
5351

5452
public abstract List<HookPhase> getHookPhasesBeforeStep();
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,36 @@
11
package org.cloudfoundry.multiapps.controller.process.util;
22

33
import java.util.Collections;
4+
import java.util.HashMap;
45
import java.util.List;
6+
import java.util.Map;
57

8+
import org.apache.commons.collections4.ListUtils;
9+
import org.cloudfoundry.multiapps.controller.core.model.HookPhase;
610
import org.cloudfoundry.multiapps.controller.process.steps.StepPhase;
11+
import org.cloudfoundry.multiapps.controller.process.steps.StepsUtil;
712
import org.cloudfoundry.multiapps.mta.model.Hook;
813
import org.cloudfoundry.multiapps.mta.model.Module;
14+
import org.flowable.engine.delegate.DelegateExecution;
915

1016
public class HooksExecutor {
1117

1218
private final HooksCalculator hooksCalculator;
1319
private final Module moduleToDeploy;
20+
private final DelegateExecution delegateExecution;
1421

15-
public HooksExecutor(HooksCalculator hooksCalculator, Module moduleToDeploy) {
22+
public HooksExecutor(HooksCalculator hooksCalculator, Module moduleToDeploy, DelegateExecution delegateExecution) {
1623
this.hooksCalculator = hooksCalculator;
1724
this.moduleToDeploy = moduleToDeploy;
25+
this.delegateExecution = delegateExecution;
26+
}
27+
28+
public List<Hook> determineBeforeStepHooks(StepPhase currentStepPhase) {
29+
if (!hooksCalculator.isInPreExecuteStepPhase(currentStepPhase) || moduleToDeploy == null) {
30+
return Collections.emptyList();
31+
}
32+
HooksWithPhases hooksWithPhases = hooksCalculator.calculateHooksForExecution(moduleToDeploy, currentStepPhase);
33+
return hooksWithPhases.getHooks();
1834
}
1935

2036
public List<Hook> executeBeforeStepHooks(StepPhase currentStepPhase) {
@@ -24,20 +40,48 @@ public List<Hook> executeBeforeStepHooks(StepPhase currentStepPhase) {
2440
return executeHooks(currentStepPhase);
2541
}
2642

27-
public List<Hook> executeAfterStepHooks(StepPhase currentStepPhase) {
28-
if (!hooksCalculator.isInPostExecuteStepPhase(currentStepPhase)) {
43+
private List<Hook> executeHooks(StepPhase currentStepPhase) {
44+
if (moduleToDeploy == null) {
2945
return Collections.emptyList();
3046
}
31-
return executeHooks(currentStepPhase);
47+
HooksWithPhases hooksWithPhases = hooksCalculator.calculateHooksForExecution(moduleToDeploy, currentStepPhase);
48+
Map<String, List<String>> alreadyExecutedHooksForModule = StepsUtil.getExecutedHooksForModule(delegateExecution,
49+
moduleToDeploy.getName());
50+
updateExecutedHooksForModule(alreadyExecutedHooksForModule, hooksWithPhases.getHookPhases(), hooksWithPhases.getHooks());
51+
return hooksWithPhases.getHooks();
3252
}
3353

34-
private List<Hook> executeHooks(StepPhase stepPhase) {
35-
if (moduleToDeploy == null) {
54+
private void updateExecutedHooksForModule(Map<String, List<String>> alreadyExecutedHooks, List<HookPhase> currentHookPhasesForExecution,
55+
List<Hook> hooksForExecution) {
56+
Map<String, List<String>> result = new HashMap<>(alreadyExecutedHooks);
57+
updateExecutedHooks(result, currentHookPhasesForExecution, hooksForExecution);
58+
StepsUtil.setExecutedHooksForModule(delegateExecution, moduleToDeploy.getName(), result);
59+
}
60+
61+
private void updateExecutedHooks(Map<String, List<String>> alreadyExecutedHooks, List<HookPhase> currentHookPhasesForExecution,
62+
List<Hook> hooksForExecution) {
63+
for (Hook hook : hooksForExecution) {
64+
updateHook(alreadyExecutedHooks, currentHookPhasesForExecution, hook);
65+
}
66+
}
67+
68+
private void updateHook(Map<String, List<String>> alreadyExecutedHooks, List<HookPhase> currentHookPhasesForExecution, Hook hook) {
69+
List<String> hookPhasesBasedOnCurrentHookPhase = getHookPhasesBasedOnCurrentHookPhase(currentHookPhasesForExecution,
70+
hook.getPhases());
71+
alreadyExecutedHooks.merge(hook.getName(), hookPhasesBasedOnCurrentHookPhase, ListUtils::union);
72+
}
73+
74+
private List<String> getHookPhasesBasedOnCurrentHookPhase(List<HookPhase> currentHookPhasesForExecution, List<String> hookPhases) {
75+
return hookPhases.stream()
76+
.filter(phase -> currentHookPhasesForExecution.contains(HookPhase.fromString(phase)))
77+
.toList();
78+
}
79+
80+
public List<Hook> executeAfterStepHooks(StepPhase currentStepPhase) {
81+
if (!hooksCalculator.isInPostExecuteStepPhase(currentStepPhase)) {
3682
return Collections.emptyList();
3783
}
38-
List<Hook> hooksForExecution = hooksCalculator.calculateHooksForExecution(moduleToDeploy, stepPhase);
39-
hooksCalculator.setHooksForExecution(hooksForExecution);
40-
return hooksForExecution;
84+
return executeHooks(currentStepPhase);
4185
}
4286

4387
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package org.cloudfoundry.multiapps.controller.process.util;
2+
3+
import java.util.List;
4+
5+
import org.cloudfoundry.multiapps.controller.core.model.HookPhase;
6+
import org.cloudfoundry.multiapps.mta.model.Hook;
7+
import org.immutables.value.Value;
8+
9+
@Value.Immutable
10+
public interface HooksWithPhases {
11+
12+
List<HookPhase> getHookPhases();
13+
14+
List<Hook> getHooks();
15+
}

0 commit comments

Comments
 (0)