You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by sn...@apache.org on 2015/03/17 19:33:04 UTC
[2/3] cassandra git commit: DatabaseDescriptor throws NPE when
rpc_interface is used
DatabaseDescriptor throws NPE when rpc_interface is used
patch by Ariel Weisberg; reviewed by Carl Yeksigian for CASSANDRA-8839
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/89cdfd8e
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/89cdfd8e
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/89cdfd8e
Branch: refs/heads/trunk
Commit: 89cdfd8e075d8883d776d7f881735f1c25e3cb54
Parents: c0f159b
Author: Ariel Weisberg <ar...@weisberg.ws>
Authored: Tue Mar 17 19:27:54 2015 +0100
Committer: Robert Stupp <sn...@snazy.de>
Committed: Tue Mar 17 19:27:54 2015 +0100
----------------------------------------------------------------------
CHANGES.txt | 1 +
conf/cassandra.yaml | 12 ++
.../org/apache/cassandra/config/Config.java | 6 +-
.../cassandra/config/DatabaseDescriptor.java | 205 +++++++++++--------
.../config/YamlConfigurationLoader.java | 16 +-
.../config/DatabaseDescriptorTest.java | 139 ++++++++++++-
6 files changed, 277 insertions(+), 102 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/89cdfd8e/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 7b8e0ad..30bf698 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
2.1.4
+ * DatabaseDescriptor throws NPE when rpc_interface is used (CASSANDRA-8839)
* Don't check if an sstable is live for offline compactions (CASSANDRA-8841)
* Don't set clientMode in SSTableLoader (CASSANDRA-8238)
* Fix SSTableRewriter with disabled early open (CASSANDRA-8535)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/89cdfd8e/conf/cassandra.yaml
----------------------------------------------------------------------
diff --git a/conf/cassandra.yaml b/conf/cassandra.yaml
index cea12b3..2b43ba7 100644
--- a/conf/cassandra.yaml
+++ b/conf/cassandra.yaml
@@ -370,8 +370,14 @@ ssl_storage_port: 7001
# address associated with the hostname (it might not be).
#
# Setting listen_address to 0.0.0.0 is always wrong.
+#
+# If you choose to specify the interface by name and the interface has an ipv4 and an ipv6 address
+# you can specify which should be chosen using listen_interface_prefer_ipv6. If false the first ipv4
+# address will be used. If true the first ipv6 address will be used. Defaults to false preferring
+# ipv4. If there is only one address it will be selected regardless of ipv4/ipv6.
listen_address: localhost
# listen_interface: eth0
+# listen_interface_prefer_ipv6: false
# Address to broadcast to other Cassandra nodes
# Leaving this blank will set it to the same value as listen_address
@@ -422,8 +428,14 @@ start_rpc: true
# set broadcast_rpc_address to a value other than 0.0.0.0.
#
# For security reasons, you should not expose this port to the internet. Firewall it if needed.
+#
+# If you choose to specify the interface by name and the interface has an ipv4 and an ipv6 address
+# you can specify which should be chosen using rpc_interface_prefer_ipv6. If false the first ipv4
+# address will be used. If true the first ipv6 address will be used. Defaults to false preferring
+# ipv4. If there is only one address it will be selected regardless of ipv4/ipv6.
rpc_address: localhost
# rpc_interface: eth1
+# rpc_interface_prefer_ipv6: false
# port for Thrift to listen for clients on
rpc_port: 9160
http://git-wip-us.apache.org/repos/asf/cassandra/blob/89cdfd8e/src/java/org/apache/cassandra/config/Config.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/config/Config.java b/src/java/org/apache/cassandra/config/Config.java
index ccd4467..fbbd1dd 100644
--- a/src/java/org/apache/cassandra/config/Config.java
+++ b/src/java/org/apache/cassandra/config/Config.java
@@ -34,7 +34,7 @@ import org.apache.cassandra.utils.FBUtilities;
/**
* A class that contains configuration properties for the cassandra node it runs within.
- *
+ *
* Properties declared as volatile can be mutated via JMX.
*/
public class Config
@@ -101,12 +101,14 @@ public class Config
public Integer ssl_storage_port = 7001;
public String listen_address;
public String listen_interface;
+ public Boolean listen_interface_prefer_ipv6 = false;
public String broadcast_address;
public String internode_authenticator;
public Boolean start_rpc = true;
public String rpc_address;
public String rpc_interface;
+ public Boolean rpc_interface_prefer_ipv6 = false;
public String broadcast_rpc_address;
public Integer rpc_port = 9160;
public Integer rpc_listen_backlog = 50;
@@ -155,7 +157,7 @@ public class Config
public Double commitlog_sync_batch_window_in_ms;
public Integer commitlog_sync_period_in_ms;
public int commitlog_segment_size_in_mb = 32;
-
+
@Deprecated
public int commitlog_periodic_queue_size = -1;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/89cdfd8e/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
index 4426f20..65cec9c 100644
--- a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
+++ b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
@@ -20,6 +20,8 @@ package org.apache.cassandra.config;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
@@ -38,6 +40,7 @@ import java.util.UUID;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSet;
import com.google.common.primitives.Longs;
+
import org.apache.cassandra.thrift.ThriftServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -158,7 +161,7 @@ public class DatabaseDescriptor
return loader.loadConfig();
}
- private static InetAddress getNetworkInterfaceAddress(String intf, String configName) throws ConfigurationException
+ private static InetAddress getNetworkInterfaceAddress(String intf, String configName, boolean preferIPv6) throws ConfigurationException
{
try
{
@@ -168,9 +171,18 @@ public class DatabaseDescriptor
Enumeration<InetAddress> addrs = ni.getInetAddresses();
if (!addrs.hasMoreElements())
throw new ConfigurationException("Configured " + configName + " \"" + intf + "\" was found, but had no addresses");
- InetAddress retval = listenAddress = addrs.nextElement();
- if (addrs.hasMoreElements())
- throw new ConfigurationException("Configured " + configName + " \"" + intf + "\" can't have more than one address");
+
+ /*
+ * Try to return the first address of the preferred type, otherwise return the first address
+ */
+ InetAddress retval = null;
+ while (addrs.hasMoreElements())
+ {
+ InetAddress temp = addrs.nextElement();
+ if (preferIPv6 && temp.getClass() == Inet6Address.class) return temp;
+ if (!preferIPv6 && temp.getClass() == Inet4Address.class) return temp;
+ if (retval == null) retval = temp;
+ }
return retval;
}
catch (SocketException e)
@@ -179,6 +191,103 @@ public class DatabaseDescriptor
}
}
+ @VisibleForTesting
+ static void applyAddressConfig(Config config) throws ConfigurationException
+ {
+ listenAddress = null;
+ rpcAddress = null;
+ broadcastAddress = null;
+ broadcastRpcAddress = null;
+
+ /* Local IP, hostname or interface to bind services to */
+ if (config.listen_address != null && config.listen_interface != null)
+ {
+ throw new ConfigurationException("Set listen_address OR listen_interface, not both");
+ }
+ else if (config.listen_address != null)
+ {
+ try
+ {
+ listenAddress = InetAddress.getByName(config.listen_address);
+ }
+ catch (UnknownHostException e)
+ {
+ throw new ConfigurationException("Unknown listen_address '" + config.listen_address + "'");
+ }
+
+ if (listenAddress.isAnyLocalAddress())
+ throw new ConfigurationException("listen_address cannot be a wildcard address (" + config.listen_address + ")!");
+ }
+ else if (config.listen_interface != null)
+ {
+ listenAddress = getNetworkInterfaceAddress(config.listen_interface, "listen_interface", config.listen_interface_prefer_ipv6);
+ }
+
+ /* Gossip Address to broadcast */
+ if (config.broadcast_address != null)
+ {
+ try
+ {
+ broadcastAddress = InetAddress.getByName(config.broadcast_address);
+ }
+ catch (UnknownHostException e)
+ {
+ throw new ConfigurationException("Unknown broadcast_address '" + config.broadcast_address + "'");
+ }
+
+ if (broadcastAddress.isAnyLocalAddress())
+ throw new ConfigurationException("broadcast_address cannot be a wildcard address (" + config.broadcast_address + ")!");
+ }
+
+ /* Local IP, hostname or interface to bind RPC server to */
+ if (config.rpc_address != null && config.rpc_interface != null)
+ {
+ throw new ConfigurationException("Set rpc_address OR rpc_interface, not both");
+ }
+ else if (config.rpc_address != null)
+ {
+ try
+ {
+ rpcAddress = InetAddress.getByName(config.rpc_address);
+ }
+ catch (UnknownHostException e)
+ {
+ throw new ConfigurationException("Unknown host in rpc_address " + config.rpc_address);
+ }
+ }
+ else if (config.rpc_interface != null)
+ {
+ rpcAddress = getNetworkInterfaceAddress(config.rpc_interface, "rpc_interface", config.rpc_interface_prefer_ipv6);
+ }
+ else
+ {
+ rpcAddress = FBUtilities.getLocalAddress();
+ }
+
+ /* RPC address to broadcast */
+ if (config.broadcast_rpc_address != null)
+ {
+ try
+ {
+ broadcastRpcAddress = InetAddress.getByName(config.broadcast_rpc_address);
+ }
+ catch (UnknownHostException e)
+ {
+ throw new ConfigurationException("Unknown broadcast_rpc_address '" + config.broadcast_rpc_address + "'");
+ }
+
+ if (broadcastRpcAddress.isAnyLocalAddress())
+ throw new ConfigurationException("broadcast_rpc_address cannot be a wildcard address (" + config.broadcast_rpc_address + ")!");
+ }
+ else
+ {
+ if (rpcAddress.isAnyLocalAddress())
+ throw new ConfigurationException("If rpc_address is set to a wildcard address (" + config.rpc_address + "), then " +
+ "you must set broadcast_rpc_address to a value other than " + config.rpc_address);
+ broadcastRpcAddress = rpcAddress;
+ }
+ }
+
private static void applyConfig(Config config) throws ConfigurationException
{
conf = config;
@@ -326,93 +435,7 @@ public class DatabaseDescriptor
else
logger.info("Global memtable off-heap threshold is enabled at {}MB", conf.memtable_offheap_space_in_mb);
- /* Local IP, hostname or interface to bind services to */
- if (conf.listen_address != null && conf.listen_interface != null)
- {
- throw new ConfigurationException("Set listen_address OR listen_interface, not both");
- }
- else if (conf.listen_address != null)
- {
- try
- {
- listenAddress = InetAddress.getByName(conf.listen_address);
- }
- catch (UnknownHostException e)
- {
- throw new ConfigurationException("Unknown listen_address '" + conf.listen_address + "'");
- }
-
- if (listenAddress.isAnyLocalAddress())
- throw new ConfigurationException("listen_address cannot be a wildcard address (" + conf.listen_address + ")!");
- }
- else if (conf.listen_interface != null)
- {
- listenAddress = getNetworkInterfaceAddress(conf.listen_interface, "listen_interface");
- }
-
- /* Gossip Address to broadcast */
- if (conf.broadcast_address != null)
- {
- try
- {
- broadcastAddress = InetAddress.getByName(conf.broadcast_address);
- }
- catch (UnknownHostException e)
- {
- throw new ConfigurationException("Unknown broadcast_address '" + conf.broadcast_address + "'");
- }
-
- if (broadcastAddress.isAnyLocalAddress())
- throw new ConfigurationException("broadcast_address cannot be a wildcard address (" + conf.broadcast_address + ")!");
- }
-
- /* Local IP, hostname or interface to bind RPC server to */
- if (conf.rpc_address != null && conf.rpc_interface != null)
- {
- throw new ConfigurationException("Set rpc_address OR rpc_interface, not both");
- }
- else if (conf.rpc_address != null)
- {
- try
- {
- rpcAddress = InetAddress.getByName(conf.rpc_address);
- }
- catch (UnknownHostException e)
- {
- throw new ConfigurationException("Unknown host in rpc_address " + conf.rpc_address);
- }
- }
- else if (conf.rpc_interface != null)
- {
- listenAddress = getNetworkInterfaceAddress(conf.rpc_interface, "rpc_interface");
- }
- else
- {
- rpcAddress = FBUtilities.getLocalAddress();
- }
-
- /* RPC address to broadcast */
- if (conf.broadcast_rpc_address != null)
- {
- try
- {
- broadcastRpcAddress = InetAddress.getByName(conf.broadcast_rpc_address);
- }
- catch (UnknownHostException e)
- {
- throw new ConfigurationException("Unknown broadcast_rpc_address '" + conf.broadcast_rpc_address + "'");
- }
-
- if (broadcastRpcAddress.isAnyLocalAddress())
- throw new ConfigurationException("broadcast_rpc_address cannot be a wildcard address (" + conf.broadcast_rpc_address + ")!");
- }
- else
- {
- if (rpcAddress.isAnyLocalAddress())
- throw new ConfigurationException("If rpc_address is set to a wildcard address (" + conf.rpc_address + "), then " +
- "you must set broadcast_rpc_address to a value other than " + conf.rpc_address);
- broadcastRpcAddress = rpcAddress;
- }
+ applyAddressConfig(config);
if (conf.thrift_framed_transport_size_in_mb <= 0)
throw new ConfigurationException("thrift_framed_transport_size_in_mb must be positive");
http://git-wip-us.apache.org/repos/asf/cassandra/blob/89cdfd8e/src/java/org/apache/cassandra/config/YamlConfigurationLoader.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/config/YamlConfigurationLoader.java b/src/java/org/apache/cassandra/config/YamlConfigurationLoader.java
index e222046..0061926 100644
--- a/src/java/org/apache/cassandra/config/YamlConfigurationLoader.java
+++ b/src/java/org/apache/cassandra/config/YamlConfigurationLoader.java
@@ -50,7 +50,7 @@ public class YamlConfigurationLoader implements ConfigurationLoader
/**
* Inspect the classpath to find storage configuration file
*/
- private URL getStorageConfigURL() throws ConfigurationException
+ static URL getStorageConfigURL() throws ConfigurationException
{
String configUrl = System.getProperty("cassandra.config");
if (configUrl == null)
@@ -100,7 +100,7 @@ public class YamlConfigurationLoader implements ConfigurationLoader
// getStorageConfigURL should have ruled this out
throw new AssertionError(e);
}
-
+
logConfig(configBytes);
org.yaml.snakeyaml.constructor.Constructor constructor = new org.yaml.snakeyaml.constructor.Constructor(Config.class);
@@ -134,16 +134,16 @@ public class YamlConfigurationLoader implements ConfigurationLoader
}
logger.info("Node configuration:[" + Joiner.on("; ").join(configMap.entrySet()) + "]");
}
-
- private static class MissingPropertiesChecker extends PropertyUtils
+
+ private static class MissingPropertiesChecker extends PropertyUtils
{
private final Set<String> missingProperties = new HashSet<>();
-
+
public MissingPropertiesChecker()
{
setSkipMissingProperties(true);
}
-
+
@Override
public Property getProperty(Class<? extends Object> type, String name) throws IntrospectionException
{
@@ -154,10 +154,10 @@ public class YamlConfigurationLoader implements ConfigurationLoader
}
return result;
}
-
+
public void check() throws ConfigurationException
{
- if (!missingProperties.isEmpty())
+ if (!missingProperties.isEmpty())
{
throw new ConfigurationException("Invalid yaml. Please remove properties " + missingProperties + " from your cassandra.yaml");
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/89cdfd8e/test/unit/org/apache/cassandra/config/DatabaseDescriptorTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/config/DatabaseDescriptorTest.java b/test/unit/org/apache/cassandra/config/DatabaseDescriptorTest.java
index f6d4ad4..46522cc 100644
--- a/test/unit/org/apache/cassandra/config/DatabaseDescriptorTest.java
+++ b/test/unit/org/apache/cassandra/config/DatabaseDescriptorTest.java
@@ -18,9 +18,17 @@
*/
package org.apache.cassandra.config;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.util.Enumeration;
+
+import junit.framework.Assert;
+
+import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
-
import org.apache.cassandra.OrderedJUnit4ClassRunner;
import org.apache.cassandra.SchemaLoader;
import org.apache.cassandra.db.Keyspace;
@@ -126,4 +134,133 @@ public class DatabaseDescriptorTest
return testConfig;
}
}
+
+ static NetworkInterface suitableInterface = null;
+ static boolean hasIPv4andIPv6 = false;
+
+ /*
+ * Server only accepts interfaces by name if they have a single address
+ * OS X seems to always have an ipv4 and ipv6 address on all interfaces which means some tests fail
+ * if not checked for and skipped
+ */
+ @BeforeClass
+ public static void selectSuitableInterface() throws Exception {
+ Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
+ while(interfaces.hasMoreElements()) {
+ NetworkInterface intf = interfaces.nextElement();
+
+ System.out.println("Evaluating " + intf.getName());
+
+ if (intf.isLoopback()) {
+ suitableInterface = intf;
+
+ boolean hasIPv4 = false;
+ boolean hasIPv6 = false;
+ Enumeration<InetAddress> addresses = suitableInterface.getInetAddresses();
+ while (addresses.hasMoreElements()) {
+ if (addresses.nextElement().getClass() == Inet6Address.class)
+ hasIPv6 = true;
+ else
+ hasIPv4 = true;
+ }
+ hasIPv4andIPv6 = hasIPv4 && hasIPv6;
+ return;
+ }
+ }
+ }
+
+ @Test
+ public void testRpcInterface() throws Exception
+ {
+ Config testConfig = DatabaseDescriptor.loadConfig();
+ testConfig.rpc_interface = suitableInterface.getName();
+ testConfig.rpc_address = null;
+ DatabaseDescriptor.applyAddressConfig(testConfig);
+
+ /*
+ * Confirm ability to select between IPv4 and IPv6
+ */
+ if (hasIPv4andIPv6)
+ {
+ testConfig = DatabaseDescriptor.loadConfig();
+ testConfig.rpc_interface = suitableInterface.getName();
+ testConfig.rpc_address = null;
+ testConfig.rpc_interface_prefer_ipv6 = true;
+ DatabaseDescriptor.applyAddressConfig(testConfig);
+
+ assertEquals(DatabaseDescriptor.getRpcAddress().getClass(), Inet6Address.class);
+
+ testConfig = DatabaseDescriptor.loadConfig();
+ testConfig.rpc_interface = suitableInterface.getName();
+ testConfig.rpc_address = null;
+ testConfig.rpc_interface_prefer_ipv6 = false;
+ DatabaseDescriptor.applyAddressConfig(testConfig);
+
+ assertEquals(DatabaseDescriptor.getRpcAddress().getClass(), Inet4Address.class);
+ }
+ else
+ {
+ /*
+ * Confirm first address of interface is selected
+ */
+ assertEquals(DatabaseDescriptor.getRpcAddress(), suitableInterface.getInetAddresses().nextElement());
+ }
+ }
+
+ @Test
+ public void testListenInterface() throws Exception
+ {
+ Config testConfig = DatabaseDescriptor.loadConfig();
+ testConfig.listen_interface = suitableInterface.getName();
+ testConfig.listen_address = null;
+ DatabaseDescriptor.applyAddressConfig(testConfig);
+
+ /*
+ * Confirm ability to select between IPv4 and IPv6
+ */
+ if (hasIPv4andIPv6)
+ {
+ testConfig = DatabaseDescriptor.loadConfig();
+ testConfig.listen_interface = suitableInterface.getName();
+ testConfig.listen_address = null;
+ testConfig.listen_interface_prefer_ipv6 = true;
+ DatabaseDescriptor.applyAddressConfig(testConfig);
+
+ assertEquals(DatabaseDescriptor.getListenAddress().getClass(), Inet6Address.class);
+
+ testConfig = DatabaseDescriptor.loadConfig();
+ testConfig.listen_interface = suitableInterface.getName();
+ testConfig.listen_address = null;
+ testConfig.listen_interface_prefer_ipv6 = false;
+ DatabaseDescriptor.applyAddressConfig(testConfig);
+
+ assertEquals(DatabaseDescriptor.getListenAddress().getClass(), Inet4Address.class);
+ }
+ else
+ {
+ /*
+ * Confirm first address of interface is selected
+ */
+ assertEquals(DatabaseDescriptor.getRpcAddress(), suitableInterface.getInetAddresses().nextElement());
+ }
+ }
+
+ @Test
+ public void testListenAddress() throws Exception
+ {
+ Config testConfig = DatabaseDescriptor.loadConfig();
+ testConfig.listen_address = suitableInterface.getInterfaceAddresses().get(0).getAddress().getHostAddress();
+ testConfig.listen_interface = null;
+ DatabaseDescriptor.applyAddressConfig(testConfig);
+ }
+
+ @Test
+ public void testRpcAddress() throws Exception
+ {
+ Config testConfig = DatabaseDescriptor.loadConfig();
+ testConfig.rpc_address = suitableInterface.getInterfaceAddresses().get(0).getAddress().getHostAddress();
+ testConfig.rpc_interface = null;
+ DatabaseDescriptor.applyAddressConfig(testConfig);
+
+ }
}