/*
* Copyright 2014 the original author or authors.
*
* Licensed 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.springframework.data.redis.test.util;
import java.util.HashMap;
import java.util.Map;
import org.junit.AssumptionViolatedException;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import redis.clients.jedis.Jedis;
/**
* @author Christoph Strobl
*/
public class RedisSentinelRule implements TestRule {
public enum SentinelsAvailable {
ALL_ACTIVE, ONE_ACTIVE, NONE_ACTIVE
}
private static final RedisSentinelConfiguration DEFAULT_SENTINEL_CONFIG = new RedisSentinelConfiguration()
.master("mymaster").sentinel("127.0.0.1", 26379).sentinel("127.0.0.1", 26380).sentinel("127.0.0.1", 26381);
private RedisSentinelConfiguration sentinelConfig;
private SentinelsAvailable requiredSentinels;
private Map<Object, Boolean> cache = new HashMap<Object, Boolean>();
protected RedisSentinelRule(RedisSentinelConfiguration config) {
this.sentinelConfig = config;
}
/**
* Create new {@link RedisSentinelRule} for given {@link RedisSentinelConfiguration}.
*
* @param config
* @return
*/
public static RedisSentinelRule forConfig(RedisSentinelConfiguration config) {
return new RedisSentinelRule(config != null ? config : DEFAULT_SENTINEL_CONFIG);
}
/**
* Create new {@link RedisSentinelRule} using default configuration.
*
* @return
*/
public static RedisSentinelRule withDefaultConfig() {
return new RedisSentinelRule(DEFAULT_SENTINEL_CONFIG);
}
public RedisSentinelRule sentinelsDisabled() {
this.requiredSentinels = SentinelsAvailable.NONE_ACTIVE;
return this;
}
/**
* Verifies all {@literal Sentinel} nodes are available.
*
* @return
*/
public RedisSentinelRule allActive() {
this.requiredSentinels = SentinelsAvailable.ALL_ACTIVE;
return this;
}
/**
* Verifies at least one {@literal Sentinel} node is available.
*
* @return
*/
public RedisSentinelRule oneActive() {
this.requiredSentinels = SentinelsAvailable.ONE_ACTIVE;
return this;
}
/**
* Will only check {@link RedisSentinelConfiguration} configuration in case {@link RequiresRedisSentinel} is detected
* on test method.
*
* @return
*/
public RedisSentinelRule dynamicModeSelection() {
this.requiredSentinels = null;
return this;
}
@Override
public Statement apply(final Statement base, final Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
if (description.isTest()) {
RequiresRedisSentinel sentinels = description.getAnnotation(RequiresRedisSentinel.class);
if (RedisSentinelRule.this.requiredSentinels != null || sentinels != null) {
verify(sentinels != null ? sentinels.value() : RedisSentinelRule.this.requiredSentinels);
}
} else {
verify(RedisSentinelRule.this.requiredSentinels);
}
base.evaluate();
}
};
}
private void verify(SentinelsAvailable verificationMode) {
int failed = 0;
for (RedisNode node : sentinelConfig.getSentinels()) {
if (cache.isEmpty() || !cache.containsKey(node.asString())) {
cache.put(node.asString(), isAvailable(node));
}
if (!cache.get(node.asString())) {
failed++;
}
}
if (failed > 0) {
if (SentinelsAvailable.ALL_ACTIVE.equals(verificationMode)) {
throw new AssumptionViolatedException(String.format(
"Expected all Redis Sentinels to respone but %s of %s did not responde", failed, sentinelConfig
.getSentinels().size()));
}
if (SentinelsAvailable.ONE_ACTIVE.equals(verificationMode) && sentinelConfig.getSentinels().size() - 1 < failed) {
throw new AssumptionViolatedException(
"Expected at least one sentinel to respond but it seems all are offline - Game Over!");
}
}
if (SentinelsAvailable.NONE_ACTIVE.equals(verificationMode) && failed != sentinelConfig.getSentinels().size()) {
throw new AssumptionViolatedException(String.format(
"Expected to have no sentinels online but found that %s are still alive.", (sentinelConfig.getSentinels()
.size() - failed)));
}
}
private boolean isAvailable(RedisNode node) {
Jedis jedis = null;
try {
jedis = new Jedis(node.getHost(), node.getPort());
jedis.ping();
return true;
} catch (Exception e) {
return false;
} finally {
if (jedis != null) {
try {
jedis.disconnect();
if (jedis.getClient().getSocket().isConnected()) {
jedis.getClient().getSocket().close();
Thread.sleep(5);
}
jedis.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}