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 +69,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..a3c6ad9a14 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 @@ -21,6 +21,7 @@ import static org.cloudfoundry.multiapps.controller.core.model.SupportedParameters.BUILDPACK; import static org.cloudfoundry.multiapps.controller.core.model.SupportedParameters.BUILDPACKS; import static org.cloudfoundry.multiapps.controller.core.model.SupportedParameters.DOCKER; +import static org.cloudfoundry.multiapps.controller.core.model.SupportedParameters.HEALTH_CHECK_INTERVAL; import static org.cloudfoundry.multiapps.controller.core.model.SupportedParameters.LIFECYCLE; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -138,6 +139,24 @@ void testValidateWithAllParametersMissing() { assertNull(staging.getDockerInfo()); } + @Test + void testHealthCheckIntervalIsParsedWhenProvided() { + parametersList.add(mapOf(HEALTH_CHECK_INTERVAL, 15)); + + Staging staging = parser.parse(parametersList); + + assertNotNull(staging); + assertEquals(15, 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-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..13e1230fc1 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 @@ -16,9 +16,15 @@ public class AdditionalModuleParametersReporter { private static final Logger LOGGER = LoggerFactory.getLogger(AdditionalModuleParametersReporter.class); private final ProcessContext context; + private final Logger logger; public AdditionalModuleParametersReporter(ProcessContext context) { + this(context, LOGGER); + } + + AdditionalModuleParametersReporter(ProcessContext context, Logger logger) { this.context = context; + this.logger = logger; } public void reportUsageOfAdditionalParameters(Module module) { @@ -33,6 +39,8 @@ public void reportUsageOfAdditionalParameters(Module module) { .get(SupportedParameters.READINESS_HEALTH_CHECK_INVOCATION_TIMEOUT); Integer readinessHealthCheckInterval = (Integer) module.getParameters() .get(SupportedParameters.READINESS_HEALTH_CHECK_INTERVAL); + Integer healthCheckInterval = (Integer) module.getParameters() + .get(SupportedParameters.HEALTH_CHECK_INTERVAL); List buildpacks = PropertiesUtil.getPluralOrSingular(List.of(module.getParameters()), SupportedParameters.BUILDPACKS, SupportedParameters.BUILDPACK); if (readinessHealthCheckType != null) { @@ -40,6 +48,9 @@ public void reportUsageOfAdditionalParameters(Module module) { readinessHealthCheckInvocationTimeout, readinessHealthCheckInterval, buildpacks.toString(), module.getType()); } + if (healthCheckInterval != null) { + reportUsageOfHealthCheckInterval(mtaId, correlationId, healthCheckInterval, buildpacks.toString(), module.getType()); + } } // this method is being observed by Dynatrace, be careful if you change it @@ -51,4 +62,10 @@ private void reportUsageOfReadinessHealthCheckParameters(String mtaId, String co mtaId, correlationId, readinessHealthCheckType, readinessHealthCheckHttpEndpoint, readinessHealthCheckInvocationTimeout, readinessHealthCheckInterval, buildpacks, moduleType)); } + + private void reportUsageOfHealthCheckInterval(String mtaId, String correlationId, Integer healthCheckInterval, String buildpacks, + String moduleType) { + logger.info(MessageFormat.format("MTA with ID \"{0}\" associated with operation ID \"{1}\" uses liveness health check parameters: interval=\"{2}\", buildpacks=\"{3}\", moduleType=\"{4}\"", + mtaId, correlationId, 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..789436bbe3 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,15 @@ 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").healthCheckType("port").healthCheckInterval(10).build(), + ImmutableStaging.builder().addBuildpack("buildpack-1").healthCheckType("port").healthCheckInterval(20).build(), + true, LifecycleType.BUILDPACK), + Arguments.of( + ImmutableStaging.builder().addBuildpack("buildpack-1").healthCheckType("port").healthCheckInterval(10).build(), + ImmutableStaging.builder().addBuildpack("buildpack-1").healthCheckType("port").healthCheckInterval(10).build(), + false, LifecycleType.BUILDPACK)); //@formatter:on } @@ -232,6 +240,7 @@ private void prepareClient(CloudApplication application, Set routes, String command = staging.getCommand() == null ? DEFAULT_COMMAND : staging.getCommand(); var hcType = staging.getHealthCheckType(); var hcTimeout = staging.getHealthCheckTimeout(); + var hcInterval = staging.getHealthCheckInterval(); var hcEndpoint = staging.getHealthCheckHttpEndpoint(); when(client.getApplicationProcess(application.getGuid())).thenReturn(ImmutableCloudProcess.builder() .command(command) @@ -243,6 +252,7 @@ private void prepareClient(CloudApplication application, Set routes, : HealthCheckType.valueOf( hcType.toUpperCase())) .healthCheckTimeout(hcTimeout) + .healthCheckInterval(hcInterval) .healthCheckHttpEndpoint(hcEndpoint) .instances(1) .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..629d849a5c --- /dev/null +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/util/AdditionalModuleParametersReporterTest.java @@ -0,0 +1,81 @@ +package org.cloudfoundry.multiapps.controller.process.util; + +import java.util.HashMap; +import java.util.Map; + +import org.cloudfoundry.multiapps.controller.core.cf.CloudControllerClientProvider; +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.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; +import org.slf4j.Logger; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +class AdditionalModuleParametersReporterTest { + + private static final String MTA_ID = "test-mta"; + private static final String CORRELATION_ID = "test-correlation-id"; + + @Test + void testHealthCheckIntervalLoggedWhenSet() { + Logger mockLogger = Mockito.mock(Logger.class); + Module module = createModule(Map.of("health-check-interval", 30)); + + new AdditionalModuleParametersReporter(createContext(), mockLogger).reportUsageOfAdditionalParameters(module); + + ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(String.class); + verify(mockLogger, atLeastOnce()).info(messageCaptor.capture()); + long livenessLogs = messageCaptor.getAllValues() + .stream() + .filter(message -> message.contains("liveness health check")) + .count(); + assertEquals(1, livenessLogs); + String message = messageCaptor.getAllValues() + .stream() + .filter(formatted -> formatted.contains("liveness health check")) + .findFirst() + .orElseThrow(); + assertTrue(message.contains("interval=\"30\""), "Log message should contain interval=\"30\", was: " + message); + assertTrue(message.contains(MTA_ID), "Log message should contain MTA id, was: " + message); + assertTrue(message.contains(CORRELATION_ID), "Log message should contain correlation id, was: " + message); + } + + @Test + void testHealthCheckIntervalNotLoggedWhenAbsent() { + Logger mockLogger = Mockito.mock(Logger.class); + Module module = createModule(new HashMap<>()); + + new AdditionalModuleParametersReporter(createContext(), mockLogger).reportUsageOfAdditionalParameters(module); + + verify(mockLogger, never()).info(anyString()); + } + + private static Module createModule(Map parameters) { + return Module.createV3() + .setName("test-module") + .setType("application") + .setParameters(parameters); + } + + private static ProcessContext createContext() { + DelegateExecution delegateExecution = MockDelegateExecution.createSpyInstance(); + StepLogger stepLogger = Mockito.mock(StepLogger.class); + CloudControllerClientProvider cloudControllerClientProvider = Mockito.mock(CloudControllerClientProvider.class); + ProcessContext context = new ProcessContext(delegateExecution, stepLogger, cloudControllerClientProvider); + DeploymentDescriptor deploymentDescriptor = DeploymentDescriptor.createV3() + .setId(MTA_ID); + context.setVariable(Variables.DEPLOYMENT_DESCRIPTOR, deploymentDescriptor); + context.setVariable(Variables.CORRELATION_ID, CORRELATION_ID); + return context; + } +}