blob: 97e53dc2c36cc8744d2de8fc9062b7524d09826c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The SF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.apache.felix.hc.core.impl.executor;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.MockitoAnnotations.initMocks;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import org.apache.felix.hc.api.HealthCheck;
import org.apache.felix.hc.api.Result;
import org.apache.felix.hc.api.ResultLog;
import org.apache.felix.hc.api.execution.HealthCheckExecutionResult;
import org.apache.felix.hc.api.execution.HealthCheckMetadata;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
public class HealthCheckResultCacheTest {
private static final int HC_TIMEOUT_NOT_SET = -1;
private static final int DUR_1_MIN = 60 * 1000;
private static final int DUR_2_MIN = 2 * DUR_1_MIN;
private static final int DUR_3_MIN = 3 * DUR_1_MIN;
private static final int DUR_4_MIN = 4 * DUR_1_MIN;
private HealthCheckResultCache healthCheckResultCache = new HealthCheckResultCache();
@Mock
ServiceReference serviceRef;
@Before
public void setup() {
initMocks(this);
}
private HealthCheckMetadata setupHealthCheckMetadata(long id, long ttl) {
reset(serviceRef);
doReturn(id).when(serviceRef).getProperty(Constants.SERVICE_ID);
doReturn(ttl).when(serviceRef).getProperty(HealthCheck.RESULT_CACHE_TTL_IN_MS);
doReturn("HC id=" + id).when(serviceRef).getProperty(HealthCheck.NAME);
return new HealthCheckMetadata(serviceRef);
}
@Test
public void testHealthCheckResultCache() {
HealthCheckMetadata hc1 = setupHealthCheckMetadata(1, HC_TIMEOUT_NOT_SET);
ExecutionResult executionResult1 = spy(new ExecutionResult(hc1, new Result(Result.Status.OK, "result for hc1"), 1));
doReturn(new Date(new Date().getTime() - DUR_1_MIN)).when(executionResult1).getFinishedAt();
healthCheckResultCache.updateWith(executionResult1);
HealthCheckMetadata hc2 = setupHealthCheckMetadata(2, HC_TIMEOUT_NOT_SET);
ExecutionResult executionResult2 = spy(new ExecutionResult(hc2, new Result(Result.Status.OK, "result for hc2"), 1));
doReturn(new Date(new Date().getTime() - DUR_3_MIN)).when(executionResult2).getFinishedAt();
healthCheckResultCache.updateWith(executionResult2);
HealthCheckMetadata hc3 = setupHealthCheckMetadata(3, DUR_4_MIN);
ExecutionResult executionResult3 = spy(new ExecutionResult(hc3, new Result(Result.Status.OK, "result for hc3"), 1));
doReturn(new Date(new Date().getTime() - DUR_3_MIN)).when(executionResult3).getFinishedAt();
healthCheckResultCache.updateWith(executionResult3);
HealthCheckMetadata hc4 = setupHealthCheckMetadata(4, HC_TIMEOUT_NOT_SET);
// no result for this yet
List<HealthCheckMetadata> hcList = new ArrayList<HealthCheckMetadata>(Arrays.asList(hc1, hc2, hc3, hc4));
List<HealthCheckExecutionResult> results = new ArrayList<HealthCheckExecutionResult>();
healthCheckResultCache.useValidCacheResults(hcList, results, DUR_2_MIN);
assertTrue(hcList.contains(hc2)); // result too old, left in hcList for later execution
assertTrue(hcList.contains(hc4)); // no result was added to cache via updateWith()
assertTrue(results.contains(executionResult1)); // true <= result one min old, global timeout 2min
assertFalse(results.contains(executionResult2)); // false <= result three min old, global timeout 2min
assertTrue(results.contains(executionResult3)); // true <= result one three old, HC timeout 4min
// values not found in cache are left in hcList
assertEquals(2, hcList.size());
assertEquals(2, results.size());
}
@Test
public void testHealthCheckResultCacheTtl() {
// -- test cache miss due to HC TTL
HealthCheckMetadata hcWithTtl = setupHealthCheckMetadata(1, DUR_1_MIN);
ExecutionResult executionResult = spy(new ExecutionResult(hcWithTtl, new Result(Result.Status.OK, "result for hc"), 1));
doReturn(new Date(new Date().getTime() - DUR_2_MIN)).when(executionResult).getFinishedAt();
healthCheckResultCache.updateWith(executionResult);
HealthCheckExecutionResult result = healthCheckResultCache.getValidCacheResult(hcWithTtl, DUR_3_MIN);
assertNull(result); // even though global timeout would be ok (2min<3min, the hc timeout of 1min invalidates the result)
// -- test cache hit due to HC TTL
hcWithTtl = setupHealthCheckMetadata(2, DUR_3_MIN);
executionResult = spy(new ExecutionResult(hcWithTtl, new Result(Result.Status.OK, "result for hc"), 1));
doReturn(new Date(new Date().getTime() - DUR_2_MIN)).when(executionResult).getFinishedAt();
healthCheckResultCache.updateWith(executionResult);
result = healthCheckResultCache.getValidCacheResult(hcWithTtl, DUR_1_MIN);
assertEquals(executionResult, result); // even though global timeout would invalidate this result (1min<2min, the hc timeout of 3min
// allows the result)
// -- test Long.MAX_VALUE
hcWithTtl = setupHealthCheckMetadata(3, Long.MAX_VALUE);
executionResult = spy(new ExecutionResult(hcWithTtl, new Result(Result.Status.OK, "result for hc"), 1));
doReturn(new Date(new Date().getTime() - DUR_4_MIN)).when(executionResult).getFinishedAt();
healthCheckResultCache.updateWith(executionResult);
result = healthCheckResultCache.getValidCacheResult(hcWithTtl, DUR_1_MIN);
assertEquals(executionResult, result);
}
private HealthCheckMetadata setupHealthCheckMetadataWithStickyResults(long id, long nonOkStickyForSec) {
reset(serviceRef);
doReturn(id).when(serviceRef).getProperty(Constants.SERVICE_ID);
doReturn(nonOkStickyForSec).when(serviceRef).getProperty(HealthCheck.KEEP_NON_OK_RESULTS_STICKY_FOR_SEC);
doReturn("HC id=" + id).when(serviceRef).getProperty(HealthCheck.NAME);
return new HealthCheckMetadata(serviceRef);
}
@Test
public void testCreateExecutionResultWithStickyResults() {
HealthCheckMetadata hcWithStickyResultsSet = setupHealthCheckMetadataWithStickyResults(1, 120 /* 2 minutes */);
ExecutionResult currentResult = spy(new ExecutionResult(hcWithStickyResultsSet, new Result(Result.Status.OK, "result for hc"), 1));
HealthCheckExecutionResult overallResultWithStickyResults = healthCheckResultCache
.createExecutionResultWithStickyResults(currentResult);
assertTrue("Exact same result is expected if no history exists", currentResult == overallResultWithStickyResults);
// add 4 minutes old WARN to cache
ExecutionResult oldWarnResult = spy(
new ExecutionResult(hcWithStickyResultsSet, new Result(Result.Status.WARN, "result for hc"), 1));
doReturn(new Date(System.currentTimeMillis() - DUR_4_MIN)).when(oldWarnResult).getFinishedAt();
healthCheckResultCache.updateWith(oldWarnResult);
// check that it is not used
currentResult = new ExecutionResult(hcWithStickyResultsSet, new Result(Result.Status.OK, "result for hc"), 1);
overallResultWithStickyResults = healthCheckResultCache.createExecutionResultWithStickyResults(currentResult);
assertTrue("Exact same result is expected if WARN HC Result is too old", currentResult == overallResultWithStickyResults);
// change WARN to 1 minute age
doReturn(new Date(System.currentTimeMillis() - DUR_1_MIN)).when(oldWarnResult).getFinishedAt();
overallResultWithStickyResults = healthCheckResultCache.createExecutionResultWithStickyResults(currentResult);
assertTrue("Expect newly created result as sticky result should be taken into account",
currentResult != overallResultWithStickyResults);
assertEquals("Expect status to be taken over from old, sticky WARN", Result.Status.WARN,
overallResultWithStickyResults.getHealthCheckResult().getStatus());
assertEquals("Expect 4 entries, two each for current and WARN", 4, getLogMsgCount(overallResultWithStickyResults));
// add 1 minutes old CRITICAL to cache
ExecutionResult oldCriticalResult = spy(
new ExecutionResult(hcWithStickyResultsSet, new Result(Result.Status.CRITICAL, "result for hc"), 1));
doReturn(new Date(System.currentTimeMillis() - DUR_1_MIN)).when(oldCriticalResult).getFinishedAt();
healthCheckResultCache.updateWith(oldCriticalResult);
overallResultWithStickyResults = healthCheckResultCache.createExecutionResultWithStickyResults(currentResult);
assertTrue("Expect newly created result as sticky result should be taken into account",
currentResult != overallResultWithStickyResults);
assertEquals("Expect status to be taken over from old, sticky CRITICAL", Result.Status.CRITICAL,
overallResultWithStickyResults.getHealthCheckResult().getStatus());
assertEquals("Expect six entries, two each for current, WARN and CRITICAL result", 6,
getLogMsgCount(overallResultWithStickyResults));
}
private int getLogMsgCount(HealthCheckExecutionResult result) {
int count = 0;
for (ResultLog.Entry entry : result.getHealthCheckResult()) {
count++;
}
return count;
}
}