diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudProcess.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudProcess.java index 4bd4b83775..931f528b75 100644 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudProcess.java +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudProcess.java @@ -23,11 +23,13 @@ public CloudProcess derive() { Integer healthCheckTimeout = null; String healthCheckHttpEndpoint = null; Integer healthCheckInvocationTimeout = null; + Integer healthCheckInterval = null; if (healthCheck.getData() != null) { Data healthCheckData = healthCheck.getData(); healthCheckTimeout = healthCheckData.getTimeout(); healthCheckInvocationTimeout = healthCheckData.getInvocationTimeout(); healthCheckHttpEndpoint = healthCheckData.getEndpoint(); + healthCheckInterval = healthCheckData.getInterval(); } Integer readinessHealthCheckInvocationTimeout = null; String readinessHealthCheckHttpEndpoint = null; @@ -49,6 +51,7 @@ public CloudProcess derive() { .healthCheckHttpEndpoint(healthCheckHttpEndpoint) .healthCheckTimeout(healthCheckTimeout) .healthCheckInvocationTimeout(healthCheckInvocationTimeout) + .healthCheckInterval(healthCheckInterval) .readinessHealthCheckType(readinessHealthCheckType.getType() .getValue()) .readinessHealthCheckHttpEndpoint(readinessHealthCheckHttpEndpoint) diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/CloudProcess.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/CloudProcess.java index 86df6003a7..ef141ae3fe 100644 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/CloudProcess.java +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/CloudProcess.java @@ -29,6 +29,9 @@ public abstract class CloudProcess extends CloudEntity implements Derivable> parametersList) { Integer healthCheckInvocationTimeout = (Integer) PropertiesUtil.getPropertyValue(parametersList, SupportedParameters.HEALTH_CHECK_INVOCATION_TIMEOUT, null); + Integer healthCheckInterval = (Integer) PropertiesUtil.getPropertyValue(parametersList, + SupportedParameters.HEALTH_CHECK_INTERVAL, + null); String healthCheckType = (String) PropertiesUtil.getPropertyValue(parametersList, SupportedParameters.HEALTH_CHECK_TYPE, null); String healthCheckHttpEndpoint = (String) PropertiesUtil.getPropertyValue(parametersList, SupportedParameters.HEALTH_CHECK_HTTP_ENDPOINT, @@ -67,6 +70,7 @@ public Staging parse(List> parametersList) { .stackName(stackName) .healthCheckTimeout(healthCheckTimeout) .invocationTimeout(healthCheckInvocationTimeout) + .healthCheckInterval(healthCheckInterval) .healthCheckType(healthCheckType) .healthCheckHttpEndpoint(healthCheckHttpEndpoint) .readinessHealthCheckType(readinessHealthCheckType) diff --git a/multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/parser/StagingParametersParserTest.java b/multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/parser/StagingParametersParserTest.java index 98677fc307..06766dd350 100644 --- a/multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/parser/StagingParametersParserTest.java +++ b/multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/parser/StagingParametersParserTest.java @@ -138,6 +138,24 @@ void testValidateWithAllParametersMissing() { assertNull(staging.getDockerInfo()); } + @Test + void testHealthCheckIntervalIsParsedWhenProvided() { + parametersList.add(mapOf("health-check-interval", 45)); + + Staging staging = parser.parse(parametersList); + + assertNotNull(staging); + assertEquals(Integer.valueOf(45), staging.getHealthCheckInterval()); + } + + @Test + void testHealthCheckIntervalIsNullWhenAbsent() { + Staging staging = parser.parse(parametersList); + + assertNotNull(staging); + assertNull(staging.getHealthCheckInterval()); + } + private static Map mapOf(String key, Object value) { return Collections.singletonMap(key, value); } diff --git a/multiapps-controller-core/src/test/resources/org/cloudfoundry/multiapps/controller/core/cf/v2/apps-with-health-check-type-http-with-endpoint.json b/multiapps-controller-core/src/test/resources/org/cloudfoundry/multiapps/controller/core/cf/v2/apps-with-health-check-type-http-with-endpoint.json index b1d69773e8..76bbc81bce 100644 --- a/multiapps-controller-core/src/test/resources/org/cloudfoundry/multiapps/controller/core/cf/v2/apps-with-health-check-type-http-with-endpoint.json +++ b/multiapps-controller-core/src/test/resources/org/cloudfoundry/multiapps/controller/core/cf/v2/apps-with-health-check-type-http-with-endpoint.json @@ -21,6 +21,7 @@ "buildpacks": [], "healthCheckType": "http", "healthCheckHttpEndpoint": "/health", + "healthCheckInterval": 45, "appFeatures": {} }, "routes": [ diff --git a/multiapps-controller-core/src/test/resources/org/cloudfoundry/multiapps/controller/core/cf/v2/apps-with-health-check-type-port.json b/multiapps-controller-core/src/test/resources/org/cloudfoundry/multiapps/controller/core/cf/v2/apps-with-health-check-type-port.json index 50c38dc520..e571fb94da 100644 --- a/multiapps-controller-core/src/test/resources/org/cloudfoundry/multiapps/controller/core/cf/v2/apps-with-health-check-type-port.json +++ b/multiapps-controller-core/src/test/resources/org/cloudfoundry/multiapps/controller/core/cf/v2/apps-with-health-check-type-port.json @@ -20,6 +20,7 @@ "staging": { "buildpacks": [], "healthCheckType": "port", + "healthCheckInterval": 30, "appFeatures": {} }, "routes": [ diff --git a/multiapps-controller-core/src/test/resources/org/cloudfoundry/multiapps/controller/core/cf/v2/mtad-health-check-type-http-with-endpoint.yaml b/multiapps-controller-core/src/test/resources/org/cloudfoundry/multiapps/controller/core/cf/v2/mtad-health-check-type-http-with-endpoint.yaml index e7b3b4f0ff..dfa5c61f08 100644 --- a/multiapps-controller-core/src/test/resources/org/cloudfoundry/multiapps/controller/core/cf/v2/mtad-health-check-type-http-with-endpoint.yaml +++ b/multiapps-controller-core/src/test/resources/org/cloudfoundry/multiapps/controller/core/cf/v2/mtad-health-check-type-http-with-endpoint.yaml @@ -8,3 +8,4 @@ modules: parameters: health-check-type: http health-check-http-endpoint: /health + health-check-interval: 45 diff --git a/multiapps-controller-core/src/test/resources/org/cloudfoundry/multiapps/controller/core/cf/v2/mtad-health-check-type-port.yaml b/multiapps-controller-core/src/test/resources/org/cloudfoundry/multiapps/controller/core/cf/v2/mtad-health-check-type-port.yaml index 0b34885c33..44ec4cb14d 100644 --- a/multiapps-controller-core/src/test/resources/org/cloudfoundry/multiapps/controller/core/cf/v2/mtad-health-check-type-port.yaml +++ b/multiapps-controller-core/src/test/resources/org/cloudfoundry/multiapps/controller/core/cf/v2/mtad-health-check-type-port.yaml @@ -7,3 +7,4 @@ modules: type: foo parameters: health-check-type: port + health-check-interval: 30 diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/AdditionalModuleParametersReporter.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/AdditionalModuleParametersReporter.java index 1d2cff63be..e09dfd0c68 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/AdditionalModuleParametersReporter.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/AdditionalModuleParametersReporter.java @@ -25,6 +25,13 @@ public void reportUsageOfAdditionalParameters(Module module) { String mtaId = context.getRequiredVariable(Variables.DEPLOYMENT_DESCRIPTOR) .getId(); String correlationId = context.getRequiredVariable(Variables.CORRELATION_ID); + List buildpacks = PropertiesUtil.getPluralOrSingular(List.of(module.getParameters()), SupportedParameters.BUILDPACKS, + SupportedParameters.BUILDPACK); + reportReadinessHealthCheckIfPresent(module, mtaId, correlationId, buildpacks); + reportHealthCheckIfPresent(module, mtaId, correlationId, buildpacks); + } + + private void reportReadinessHealthCheckIfPresent(Module module, String mtaId, String correlationId, List buildpacks) { String readinessHealthCheckType = (String) module.getParameters() .get(SupportedParameters.READINESS_HEALTH_CHECK_TYPE); String readinessHealthCheckHttpEndpoint = (String) module.getParameters() @@ -33,8 +40,6 @@ public void reportUsageOfAdditionalParameters(Module module) { .get(SupportedParameters.READINESS_HEALTH_CHECK_INVOCATION_TIMEOUT); Integer readinessHealthCheckInterval = (Integer) module.getParameters() .get(SupportedParameters.READINESS_HEALTH_CHECK_INTERVAL); - List buildpacks = PropertiesUtil.getPluralOrSingular(List.of(module.getParameters()), SupportedParameters.BUILDPACKS, - SupportedParameters.BUILDPACK); if (readinessHealthCheckType != null) { reportUsageOfReadinessHealthCheckParameters(mtaId, correlationId, readinessHealthCheckType, readinessHealthCheckHttpEndpoint, readinessHealthCheckInvocationTimeout, readinessHealthCheckInterval, @@ -51,4 +56,31 @@ private void reportUsageOfReadinessHealthCheckParameters(String mtaId, String co mtaId, correlationId, readinessHealthCheckType, readinessHealthCheckHttpEndpoint, readinessHealthCheckInvocationTimeout, readinessHealthCheckInterval, buildpacks, moduleType)); } + + private void reportHealthCheckIfPresent(Module module, String mtaId, String correlationId, List buildpacks) { + String healthCheckType = (String) module.getParameters() + .get(SupportedParameters.HEALTH_CHECK_TYPE); + String healthCheckHttpEndpoint = (String) module.getParameters() + .get(SupportedParameters.HEALTH_CHECK_HTTP_ENDPOINT); + Integer healthCheckTimeout = (Integer) module.getParameters() + .get(SupportedParameters.HEALTH_CHECK_TIMEOUT); + Integer healthCheckInvocationTimeout = (Integer) module.getParameters() + .get(SupportedParameters.HEALTH_CHECK_INVOCATION_TIMEOUT); + Integer healthCheckInterval = (Integer) module.getParameters() + .get(SupportedParameters.HEALTH_CHECK_INTERVAL); + if (healthCheckType != null || healthCheckHttpEndpoint != null || healthCheckTimeout != null || healthCheckInvocationTimeout != null + || healthCheckInterval != null) { + reportUsageOfHealthCheckParameters(mtaId, correlationId, healthCheckType, healthCheckHttpEndpoint, healthCheckTimeout, + healthCheckInvocationTimeout, healthCheckInterval, buildpacks.toString(), module.getType()); + } + } + + private void reportUsageOfHealthCheckParameters(String mtaId, String correlationId, String healthCheckType, + String healthCheckHttpEndpoint, Integer healthCheckTimeout, + Integer healthCheckInvocationTimeout, Integer healthCheckInterval, String buildpacks, + String moduleType) { + LOGGER.info(MessageFormat.format("MTA with ID \"{0}\" associated with operation ID \"{1}\" uses health check parameters: type=\"{2}\", httpEndpoint=\"{3}\", timeout=\"{4}\", invocationTimeout=\"{5}\", interval=\"{6}\", buildpacks=\"{7}\", moduleType=\"{8}\"", + mtaId, correlationId, healthCheckType, healthCheckHttpEndpoint, healthCheckTimeout, + healthCheckInvocationTimeout, healthCheckInterval, buildpacks, moduleType)); + } } diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/CreateOrUpdateStepWithExistingAppTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/CreateOrUpdateStepWithExistingAppTest.java index 78f20b5f65..9ec37642c8 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/CreateOrUpdateStepWithExistingAppTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/CreateOrUpdateStepWithExistingAppTest.java @@ -111,7 +111,13 @@ static Stream testHandleStagingApplicationAttributes() { true, LifecycleType.CNB), Arguments.of(ImmutableStaging.builder().addBuildpack("buildpack-333").build(), ImmutableStaging.builder().addBuildpacks("buildpack-4", "buildpack-8").lifecycleType(LifecycleType.CNB).build(), - true, LifecycleType.CNB)); + true, LifecycleType.CNB), + Arguments.of( + ImmutableStaging.builder().addBuildpack("buildpack-1").command("command1").stackName("stack1") + .healthCheckType("port").build(), + ImmutableStaging.builder().addBuildpack("buildpack-1").command("command1").stackName("stack1") + .healthCheckType("port").healthCheckInterval(15).build(), + true, LifecycleType.BUILDPACK)); //@formatter:on } @@ -233,6 +239,7 @@ private void prepareClient(CloudApplication application, Set routes, var hcType = staging.getHealthCheckType(); var hcTimeout = staging.getHealthCheckTimeout(); var hcEndpoint = staging.getHealthCheckHttpEndpoint(); + var hcInterval = staging.getHealthCheckInterval(); when(client.getApplicationProcess(application.getGuid())).thenReturn(ImmutableCloudProcess.builder() .command(command) .diskInMb(disk == null ? 1024 : disk) @@ -244,6 +251,7 @@ private void prepareClient(CloudApplication application, Set routes, hcType.toUpperCase())) .healthCheckTimeout(hcTimeout) .healthCheckHttpEndpoint(hcEndpoint) + .healthCheckInterval(hcInterval) .instances(1) .build()); } diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollStartAppStatusExecutionTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollStartAppStatusExecutionTest.java index 0a5d2cebb3..f231cb8c42 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollStartAppStatusExecutionTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollStartAppStatusExecutionTest.java @@ -119,6 +119,7 @@ private CloudApplicationExtended buildApplication(int instancesCount, boolean is .instances(instancesCount) .staging(ImmutableStaging.builder() .readinessHealthCheckType(isReadinessEnabled ? "test" : null) + .healthCheckInterval(30) .build()) .build(); } diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PrepareToStopDependentModuleStepTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PrepareToStopDependentModuleStepTest.java index 7b89dbd125..5845276736 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PrepareToStopDependentModuleStepTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PrepareToStopDependentModuleStepTest.java @@ -82,6 +82,7 @@ private void setUpMocks(Module module) { private Staging createStaging() { return ImmutableStaging.builder() .readinessHealthCheckType("http") + .healthCheckInterval(30) .build(); } diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/RestartAppStepTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/RestartAppStepTest.java index 83fe91c9be..7bdc10e287 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/RestartAppStepTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/RestartAppStepTest.java @@ -93,6 +93,7 @@ private CloudApplicationExtended createApplication(String name, CloudApplication .state(state) .staging(ImmutableStaging.builder() .readinessHealthCheckType(isReadinessEnabled ? "test" : null) + .healthCheckInterval(30) .build()) .build(); } diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/util/AdditionalModuleParametersReporterTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/util/AdditionalModuleParametersReporterTest.java new file mode 100644 index 0000000000..bcf31afb7c --- /dev/null +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/util/AdditionalModuleParametersReporterTest.java @@ -0,0 +1,77 @@ +package org.cloudfoundry.multiapps.controller.process.util; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.cloudfoundry.multiapps.controller.core.cf.CloudControllerClientProvider; +import org.cloudfoundry.multiapps.controller.core.model.SupportedParameters; +import org.cloudfoundry.multiapps.controller.process.steps.ProcessContext; +import org.cloudfoundry.multiapps.controller.process.variables.Variables; +import org.cloudfoundry.multiapps.mta.model.DeploymentDescriptor; +import org.cloudfoundry.multiapps.mta.model.Module; +import org.flowable.engine.delegate.DelegateExecution; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +class AdditionalModuleParametersReporterTest { + + private static final String MODULE_TYPE = "java"; + + @Mock + private StepLogger stepLogger; + @Mock + private CloudControllerClientProvider clientProvider; + + private AdditionalModuleParametersReporter reporter; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + DelegateExecution execution = MockDelegateExecution.createSpyInstance(); + ProcessContext context = new ProcessContext(execution, stepLogger, clientProvider); + context.setVariable(Variables.DEPLOYMENT_DESCRIPTOR, DeploymentDescriptor.createV3() + .setId("test-mta") + .setVersion("1.0.0") + .setModules(List.of()) + .setResources(List.of())); + context.setVariable(Variables.CORRELATION_ID, "correlation-id-1"); + reporter = new AdditionalModuleParametersReporter(context); + } + + @Test + void testReportingHealthCheckIntervalOnlyDoesNotThrow() { + Module module = buildModuleWithParameters(Map.of(SupportedParameters.HEALTH_CHECK_INTERVAL, 30)); + assertDoesNotThrow(() -> reporter.reportUsageOfAdditionalParameters(module)); + } + + @Test + void testReportingFullHealthCheckFamilyDoesNotThrow() { + Map parameters = new HashMap<>(); + parameters.put(SupportedParameters.HEALTH_CHECK_TYPE, "http"); + parameters.put(SupportedParameters.HEALTH_CHECK_HTTP_ENDPOINT, "/health"); + parameters.put(SupportedParameters.HEALTH_CHECK_TIMEOUT, 10); + parameters.put(SupportedParameters.HEALTH_CHECK_INVOCATION_TIMEOUT, 5); + parameters.put(SupportedParameters.HEALTH_CHECK_INTERVAL, 45); + Module module = buildModuleWithParameters(parameters); + assertDoesNotThrow(() -> reporter.reportUsageOfAdditionalParameters(module)); + } + + @Test + void testReportingWithNoHealthCheckParametersDoesNotThrow() { + Module module = buildModuleWithParameters(Map.of()); + assertDoesNotThrow(() -> reporter.reportUsageOfAdditionalParameters(module)); + } + + private Module buildModuleWithParameters(Map parameters) { + Module module = Module.createV3() + .setName("test-module") + .setType(MODULE_TYPE); + module.setParameters(parameters); + return module; + } +} diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/util/ReadinessHealthCheckUtilTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/util/ReadinessHealthCheckUtilTest.java index 781efad760..c5cefb02e3 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/util/ReadinessHealthCheckUtilTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/util/ReadinessHealthCheckUtilTest.java @@ -10,12 +10,14 @@ import org.cloudfoundry.multiapps.controller.process.steps.ProcessContext; import org.cloudfoundry.multiapps.controller.process.variables.Variables; import org.flowable.engine.delegate.DelegateExecution; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.mockito.Mock; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; class ReadinessHealthCheckUtilTest { @@ -43,6 +45,19 @@ void testShouldWaitForAppToBecomeRoutable(String readinessHealthCheckType, boole createContext(readinessHealthCheckType))); } + @Test + void testHealthCheckIntervalAloneDoesNotTriggerReadinessGate() { + DelegateExecution execution = MockDelegateExecution.createSpyInstance(); + ProcessContext context = new ProcessContext(execution, stepLogger, clientProvider); + Staging staging = ImmutableStaging.builder() + .healthCheckInterval(30) + .build(); + context.setVariable(Variables.APP_TO_PROCESS, ImmutableCloudApplicationExtended.builder() + .staging(staging) + .build()); + assertFalse(ReadinessHealthCheckUtil.shouldWaitForAppToBecomeRoutable(context)); + } + private ProcessContext createContext(String readinessHealthCheckType) { DelegateExecution execution = MockDelegateExecution.createSpyInstance(); ProcessContext context = new ProcessContext(execution, stepLogger, clientProvider);