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) { String healthCheckHttpEndpoint = (String) PropertiesUtil.getPropertyValue(parametersList, SupportedParameters.HEALTH_CHECK_HTTP_ENDPOINT, getDefaultHealthCheckHttpEndpoint(healthCheckType)); + Integer healthCheckInterval = (Integer) PropertiesUtil.getPropertyValue(parametersList, SupportedParameters.HEALTH_CHECK_INTERVAL, + null); String readinessHealthCheckType = (String) PropertiesUtil.getPropertyValue(parametersList, SupportedParameters.READINESS_HEALTH_CHECK_TYPE, null); String readinessHealthCheckHttpEndpoint = (String) PropertiesUtil.getPropertyValue(parametersList, @@ -69,6 +71,7 @@ public Staging parse(List> parametersList) { .invocationTimeout(healthCheckInvocationTimeout) .healthCheckType(healthCheckType) .healthCheckHttpEndpoint(healthCheckHttpEndpoint) + .healthCheckInterval(healthCheckInterval) .readinessHealthCheckType(readinessHealthCheckType) .readinessHealthCheckHttpEndpoint(readinessHealthCheckHttpEndpoint) .readinessHealthCheckInvocationTimeout(readinessHealthCheckInvocationTimeout) 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..959dcb394e 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,36 @@ void testValidateWithAllParametersMissing() { assertNull(staging.getDockerInfo()); } + @Test + void testParseHealthCheckIntervalWhenSet() { + parametersList.add(mapOf("health-check-interval", 15)); + + Staging staging = parser.parse(parametersList); + + assertNotNull(staging); + assertEquals(Integer.valueOf(15), staging.getHealthCheckInterval()); + } + + @Test + void testParseHealthCheckIntervalDefaultsToNullWhenAbsent() { + Staging staging = parser.parse(parametersList); + + assertNotNull(staging); + assertNull(staging.getHealthCheckInterval()); + } + + @Test + void testParseHealthCheckIntervalIsIndependentOfReadinessHealthCheckInterval() { + parametersList.add(mapOf("health-check-interval", 10)); + parametersList.add(mapOf("readiness-health-check-interval", 30)); + + Staging staging = parser.parse(parametersList); + + assertNotNull(staging); + assertEquals(Integer.valueOf(10), staging.getHealthCheckInterval()); + assertEquals(Integer.valueOf(30), staging.getReadinessHealthCheckInterval()); + } + 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/Messages.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/Messages.java index 1a3fb90642..6d47a6ada6 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/Messages.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/Messages.java @@ -819,6 +819,8 @@ public class Messages { public static final String IGNORING_NOT_FOUND_OPTIONAL_SERVICE = "Service {0} not found but is optional"; public static final String IGNORING_NOT_FOUND_INACTIVE_SERVICE = "Service {0} not found but is inactive"; + public static final String MTA_USES_HEALTH_CHECK_INTERVAL_PARAMETER = "MTA with ID \"{0}\" associated with operation ID \"{1}\" uses health check interval parameter: interval=\"{2}\", buildpacks=\"{3}\", moduleType=\"{4}\""; + // Not log messages public static final String SERVICE_TYPE = "{0}/{1}"; public static final String PARSE_NULL_STRING_ERROR = "Cannot parse null string"; 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..06da8e767c 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 @@ -4,6 +4,7 @@ import java.util.List; import org.cloudfoundry.multiapps.controller.core.model.SupportedParameters; +import org.cloudfoundry.multiapps.controller.process.Messages; import org.cloudfoundry.multiapps.controller.process.steps.ProcessContext; import org.cloudfoundry.multiapps.controller.process.variables.Variables; import org.cloudfoundry.multiapps.mta.model.Module; @@ -33,6 +34,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 +43,10 @@ public void reportUsageOfAdditionalParameters(Module module) { readinessHealthCheckInvocationTimeout, readinessHealthCheckInterval, buildpacks.toString(), module.getType()); } + if (healthCheckInterval != null) { + reportUsageOfLivenessHealthCheckIntervalParameter(mtaId, correlationId, healthCheckInterval, buildpacks.toString(), + module.getType()); + } } // this method is being observed by Dynatrace, be careful if you change it @@ -51,4 +58,11 @@ private void reportUsageOfReadinessHealthCheckParameters(String mtaId, String co mtaId, correlationId, readinessHealthCheckType, readinessHealthCheckHttpEndpoint, readinessHealthCheckInvocationTimeout, readinessHealthCheckInterval, buildpacks, moduleType)); } + + // this method is being observed by Dynatrace, be careful if you change it + private void reportUsageOfLivenessHealthCheckIntervalParameter(String mtaId, String correlationId, Integer healthCheckInterval, + String buildpacks, String moduleType) { + LOGGER.info(MessageFormat.format(Messages.MTA_USES_HEALTH_CHECK_INTERVAL_PARAMETER, mtaId, correlationId, healthCheckInterval, + buildpacks, moduleType)); + } } 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..6f2ee8c0ad --- /dev/null +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/util/AdditionalModuleParametersReporterTest.java @@ -0,0 +1,132 @@ +package org.cloudfoundry.multiapps.controller.process.util; + +import java.text.MessageFormat; +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.Messages; +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.Mockito; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class AdditionalModuleParametersReporterTest { + + private static final String MTA_ID = "test-mta"; + private static final String CORRELATION_ID = "test-correlation-id"; + private static final String MODULE_TYPE = "java-tomcat"; + private static final String MODULE_NAME = "test-module"; + private static final Integer HEALTH_CHECK_INTERVAL_VALUE = 15; + + private ProcessContext context; + private AdditionalModuleParametersReporter reporter; + + @BeforeEach + void setUp() { + context = createContext(); + DeploymentDescriptor descriptor = DeploymentDescriptor.createV3() + .setId(MTA_ID); + context.setVariable(Variables.DEPLOYMENT_DESCRIPTOR, descriptor); + context.setVariable(Variables.CORRELATION_ID, CORRELATION_ID); + reporter = new AdditionalModuleParametersReporter(context); + } + + @Test + void testReportUsageWithHealthCheckIntervalDoesNotThrow() { + Module module = createModule(Map.of(SupportedParameters.HEALTH_CHECK_INTERVAL, HEALTH_CHECK_INTERVAL_VALUE)); + + assertDoesNotThrow(() -> reporter.reportUsageOfAdditionalParameters(module)); + } + + @Test + void testReportUsageWithoutHealthCheckIntervalDoesNotThrow() { + Module module = createModule(Map.of()); + + assertDoesNotThrow(() -> reporter.reportUsageOfAdditionalParameters(module)); + } + + @Test + void testReportUsageWithBothHealthCheckIntervalAndReadinessParametersDoesNotThrow() { + Map parameters = new HashMap<>(); + parameters.put(SupportedParameters.HEALTH_CHECK_INTERVAL, HEALTH_CHECK_INTERVAL_VALUE); + parameters.put(SupportedParameters.READINESS_HEALTH_CHECK_TYPE, "http"); + parameters.put(SupportedParameters.READINESS_HEALTH_CHECK_HTTP_ENDPOINT, "/ready"); + parameters.put(SupportedParameters.READINESS_HEALTH_CHECK_INVOCATION_TIMEOUT, 5); + parameters.put(SupportedParameters.READINESS_HEALTH_CHECK_INTERVAL, 30); + Module module = createModule(parameters); + + assertDoesNotThrow(() -> reporter.reportUsageOfAdditionalParameters(module)); + } + + @Test + void testHealthCheckIntervalMessageTemplateIsWellFormed() { + assertNotNull(Messages.MTA_USES_HEALTH_CHECK_INTERVAL_PARAMETER); + String formatted = MessageFormat.format(Messages.MTA_USES_HEALTH_CHECK_INTERVAL_PARAMETER, MTA_ID, CORRELATION_ID, + HEALTH_CHECK_INTERVAL_VALUE, "[]", MODULE_TYPE); + + assertTrue(formatted.contains(MTA_ID), () -> "expected MTA id in message, got: " + formatted); + assertTrue(formatted.contains(CORRELATION_ID), () -> "expected correlation id in message, got: " + formatted); + assertTrue(formatted.contains(HEALTH_CHECK_INTERVAL_VALUE.toString()), + () -> "expected health-check-interval value in message, got: " + formatted); + assertTrue(formatted.contains(MODULE_TYPE), () -> "expected module type in message, got: " + formatted); + } + + @Test + void testHealthCheckIntervalMessageMentionsIntervalKeyword() { + String formatted = MessageFormat.format(Messages.MTA_USES_HEALTH_CHECK_INTERVAL_PARAMETER, MTA_ID, CORRELATION_ID, + HEALTH_CHECK_INTERVAL_VALUE, "[]", MODULE_TYPE); + + assertTrue(formatted.toLowerCase() + .contains("interval"), + () -> "expected the message to identify it as a health-check-interval log line, got: " + formatted); + } + + @Test + void testReportUsageReadsHealthCheckIntervalFromModuleParametersAsInteger() { + Module module = createModule(Map.of(SupportedParameters.HEALTH_CHECK_INTERVAL, HEALTH_CHECK_INTERVAL_VALUE)); + + Object actual = module.getParameters() + .get(SupportedParameters.HEALTH_CHECK_INTERVAL); + + assertEquals(HEALTH_CHECK_INTERVAL_VALUE, actual); + assertDoesNotThrow(() -> reporter.reportUsageOfAdditionalParameters(module)); + } + + @Test + void testReportUsageWithHealthCheckIntervalAndBuildpacksDoesNotThrow() { + Map parameters = new HashMap<>(); + parameters.put(SupportedParameters.HEALTH_CHECK_INTERVAL, HEALTH_CHECK_INTERVAL_VALUE); + parameters.put(SupportedParameters.BUILDPACKS, List.of("java_buildpack")); + Module module = createModule(parameters); + + assertDoesNotThrow(() -> reporter.reportUsageOfAdditionalParameters(module)); + } + + private Module createModule(Map parameters) { + Map mutableParameters = new HashMap<>(parameters); + return Module.createV3() + .setName(MODULE_NAME) + .setType(MODULE_TYPE) + .setParameters(mutableParameters); + } + + private ProcessContext createContext() { + DelegateExecution execution = MockDelegateExecution.createSpyInstance(); + StepLogger stepLogger = Mockito.mock(StepLogger.class); + CloudControllerClientProvider clientProvider = Mockito.mock(CloudControllerClientProvider.class); + return new ProcessContext(execution, stepLogger, clientProvider); + } + +}