mirror of https://github.com/apache/cassandra
Fix copying of JAR of a trigger to temporary file
While testing the fix for trigger, we fixed the corresponding trigger example. While on it, we detected that ssl-factory example was not working either so it is fixed in this commit as well. patch by Stefan Miklosovic; reviewed by Brandon Williams for CASSANDRA-18264
This commit is contained in:
parent
cfe9641fbe
commit
9860c1e9d9
|
|
@ -84,3 +84,5 @@ venv/
|
||||||
# build-scripts will put cassandra-builds into the directory
|
# build-scripts will put cassandra-builds into the directory
|
||||||
cassandra-builds/
|
cassandra-builds/
|
||||||
cassandra-dtest/
|
cassandra-dtest/
|
||||||
|
|
||||||
|
conf/triggers/trigger-example.jar
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
4.1.1
|
4.1.1
|
||||||
|
* Fix copying of JAR of a trigger to temporary file (CASSANDRA-18264)
|
||||||
* Fix possible NoSuchFileException when removing a snapshot (CASSANDRA-18211)
|
* Fix possible NoSuchFileException when removing a snapshot (CASSANDRA-18211)
|
||||||
* PaxosPrepare may add instances to the Electorate that are not in gossip (CASSANDRA-18194)
|
* PaxosPrepare may add instances to the Electorate that are not in gossip (CASSANDRA-18194)
|
||||||
* Fix PAXOS2_COMMIT_AND_PREPARE_RSP serialisation AssertionError (CASSANDRA-18164)
|
* Fix PAXOS2_COMMIT_AND_PREPARE_RSP serialisation AssertionError (CASSANDRA-18164)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
Cassandra Custom SslContextFactory Example
|
||||||
|
==========================================
|
||||||
|
|
||||||
|
This example shows custom SslContextFactory implementation based on Kubernetes secrets
|
||||||
|
For the documentation please refer to the javadocs for `K8SecretsSslContextFactory.java.`
|
||||||
|
|
||||||
|
Installation
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Step 1: Build the Cassandra classes locally
|
||||||
|
|
||||||
|
----
|
||||||
|
$ cd <cassandra_src_dir>
|
||||||
|
$ ant build
|
||||||
|
----
|
||||||
|
|
||||||
|
Step 2: Run tests for the security examples
|
||||||
|
|
||||||
|
----
|
||||||
|
$ cd <cassandra_src_dir>/examples/ssl-factory
|
||||||
|
$ ant test
|
||||||
|
----
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
Cassandra Custom SslContextFactory Example:
|
|
||||||
==========================================
|
|
||||||
|
|
||||||
Example-1: Custom SslContextFactory implementation based on Kubernetes secrets
|
|
||||||
------------------------------------------------------------------------------
|
|
||||||
For the documentation please refer to the javadocs for the K8SecretsSslContextFactory.java.
|
|
||||||
|
|
||||||
|
|
||||||
Installation:
|
|
||||||
============
|
|
||||||
Step 1: Build the Cassandra classes locally
|
|
||||||
|
|
||||||
change directory to <cassandra_src_dir>
|
|
||||||
run "ant build"
|
|
||||||
|
|
||||||
Step 2: Run tests for the security examples
|
|
||||||
|
|
||||||
change directory to <cassandra_src_dir>/examples/ssl-factory
|
|
||||||
run "ant test"
|
|
||||||
|
|
@ -38,7 +38,7 @@ import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import org.apache.cassandra.config.EncryptionOptions;
|
import org.apache.cassandra.config.EncryptionOptions;
|
||||||
import org.apache.cassandra.io.util.PathUtils;
|
import org.apache.cassandra.io.util.File;
|
||||||
|
|
||||||
import static org.apache.cassandra.security.KubernetesSecretsSslContextFactory.ConfigKeys.KEYSTORE_PASSWORD_ENV_VAR;
|
import static org.apache.cassandra.security.KubernetesSecretsSslContextFactory.ConfigKeys.KEYSTORE_PASSWORD_ENV_VAR;
|
||||||
import static org.apache.cassandra.security.KubernetesSecretsSslContextFactory.ConfigKeys.KEYSTORE_UPDATED_TIMESTAMP_PATH;
|
import static org.apache.cassandra.security.KubernetesSecretsSslContextFactory.ConfigKeys.KEYSTORE_UPDATED_TIMESTAMP_PATH;
|
||||||
|
|
@ -64,7 +64,7 @@ public class KubernetesSecretsSslContextFactoryTest
|
||||||
private static void deleteFileIfExists(String file)
|
private static void deleteFileIfExists(String file)
|
||||||
{
|
{
|
||||||
Path filePath = Paths.get(file);
|
Path filePath = Paths.get(file);
|
||||||
boolean deleted = PathUtils.tryDelete(filePath);
|
boolean deleted = new File(filePath).toJavaIOFile().delete();
|
||||||
if (!deleted)
|
if (!deleted)
|
||||||
{
|
{
|
||||||
logger.warn("File {} could not be deleted.", filePath);
|
logger.warn("File {} could not be deleted.", filePath);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
Cassandra Trigger Example
|
||||||
|
==========================
|
||||||
|
|
||||||
|
The `AuditTrigger` class will create a basic audit of
|
||||||
|
activity on a table.
|
||||||
|
|
||||||
|
Installation
|
||||||
|
-------------
|
||||||
|
|
||||||
|
----
|
||||||
|
$ cd <cassandra_src_dir>/examples/triggers
|
||||||
|
$ ant install
|
||||||
|
----
|
||||||
|
|
||||||
|
It will build the trigger and copy it to `conf/triggers`. `AuditTrigger.properties`
|
||||||
|
in `conf` directory of this example will be automatically bundled into built jar file.
|
||||||
|
It is not needed to copy it to `conf/triggers` directory.
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
Create the keyspace and table configured in `AuditTrigger.properties`:
|
||||||
|
|
||||||
|
----
|
||||||
|
cqlsh> CREATE KEYSPACE test WITH REPLICATION =
|
||||||
|
{ 'class' : 'SimpleStrategy', 'replication_factor' : '1' };
|
||||||
|
cqlsh> CREATE TABLE test.audit (key timeuuid, keyspace_name text,
|
||||||
|
table_name text, primary_key text, PRIMARY KEY(key));
|
||||||
|
----
|
||||||
|
|
||||||
|
Create a table to add the trigger to:
|
||||||
|
|
||||||
|
Note: The example currently only handles non-composite partition keys
|
||||||
|
----
|
||||||
|
cqlsh> CREATE TABLE test.test (key text, value text, PRIMARY KEY(key));
|
||||||
|
----
|
||||||
|
|
||||||
|
Configure the trigger on the table:
|
||||||
|
|
||||||
|
----
|
||||||
|
cqlsh> CREATE TRIGGER test1 ON test.test USING 'org.apache.cassandra.triggers.AuditTrigger';
|
||||||
|
----
|
||||||
|
|
||||||
|
Start inserting data to the table that has the trigger. For each
|
||||||
|
partition added to the table an entry should appear in the 'audit' table:
|
||||||
|
|
||||||
|
----
|
||||||
|
cqlsh> INSERT INTO test.test (key, value) VALUES ('1', '1');
|
||||||
|
----
|
||||||
|
|
||||||
|
An entry will be automatically added to `test.audit` table:
|
||||||
|
|
||||||
|
----
|
||||||
|
cqlsh> SELECT * FROM test.audit ;
|
||||||
|
|
||||||
|
@ Row 1
|
||||||
|
---------------+--------------------------------------
|
||||||
|
key | 885141d0-ad7c-11ed-b917-9958320828b8
|
||||||
|
keyspace_name | test
|
||||||
|
primary_key | 1
|
||||||
|
table_name | test
|
||||||
|
----
|
||||||
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
Cassandra Trigger Example:
|
|
||||||
==========================
|
|
||||||
|
|
||||||
The AuditTrigger class will create a basic audit of
|
|
||||||
activity on a table.
|
|
||||||
|
|
||||||
Installation:
|
|
||||||
============
|
|
||||||
change directory to <cassandra_src_dir>/examples/triggers
|
|
||||||
run "ant jar"
|
|
||||||
Copy build/trigger-example.jar to <cassandra_conf>/triggers/
|
|
||||||
Copy conf/* to <cassandra_home>/conf/
|
|
||||||
|
|
||||||
Create the keyspace and table configured in AuditTrigger.properties:
|
|
||||||
CREATE KEYSPACE test WITH REPLICATION =
|
|
||||||
{ 'class' : 'SimpleStrategy', 'replication_factor' : '1' };
|
|
||||||
CREATE TABLE test.audit (key timeuuid, keyspace_name text,
|
|
||||||
table_name text, primary_key text, PRIMARY KEY(key));
|
|
||||||
|
|
||||||
Create a table to add the trigger to:
|
|
||||||
CREATE TABLE test.test (key text, value text, PRIMARY KEY(key));
|
|
||||||
Note: The example currently only handles non-composite partition keys
|
|
||||||
|
|
||||||
Configure the trigger on the table:
|
|
||||||
CREATE TRIGGER test1 ON test.test
|
|
||||||
USING 'org.apache.cassandra.triggers.AuditTrigger';
|
|
||||||
|
|
||||||
Start inserting data to the table that has the trigger. For each
|
|
||||||
partition added to the table an entry should appear in the 'audit' table:
|
|
||||||
INSERT INTO test.test (key, value) values ('1', '1');
|
|
||||||
SELECT * FROM test.audit;
|
|
||||||
|
|
||||||
key | keyspace_name | primary_key | table_name
|
|
||||||
--------------------------------------+---------------+-------------+------------
|
|
||||||
7dc75b60-770f-11e5-9019-033d8af33e6f | test | 1 | test
|
|
||||||
|
|
||||||
|
|
@ -57,7 +57,12 @@
|
||||||
</jar>
|
</jar>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
|
<target name="install" depends="jar">
|
||||||
|
<copy verbose="true" file="${build.dir}/${final.name}.jar" todir="${cassandra.dir}/conf/triggers" overwrite="true"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
<target name="clean">
|
<target name="clean">
|
||||||
<delete dir="${build.dir}" />
|
<delete dir="${build.dir}" />
|
||||||
|
<delete file="${cassandra.dir}/conf/triggers/${final.name}.jar"/>
|
||||||
</target>
|
</target>
|
||||||
</project>
|
</project>
|
||||||
|
|
|
||||||
|
|
@ -29,19 +29,27 @@ import org.apache.cassandra.db.partitions.Partition;
|
||||||
import org.apache.cassandra.db.partitions.PartitionUpdate;
|
import org.apache.cassandra.db.partitions.PartitionUpdate;
|
||||||
import org.apache.cassandra.io.util.FileUtils;
|
import org.apache.cassandra.io.util.FileUtils;
|
||||||
import org.apache.cassandra.utils.FBUtilities;
|
import org.apache.cassandra.utils.FBUtilities;
|
||||||
import org.apache.cassandra.utils.UUIDGen;
|
import org.apache.cassandra.utils.TimeUUID;
|
||||||
|
|
||||||
public class AuditTrigger implements ITrigger
|
public class AuditTrigger implements ITrigger
|
||||||
{
|
{
|
||||||
private Properties properties = loadProperties();
|
private static final String AUDIT_PROPERTIES_FILE_NAME = "AuditTrigger.properties";
|
||||||
|
|
||||||
|
private final Properties properties;
|
||||||
|
private final String auditKeyspace;
|
||||||
|
private final String auditTable;
|
||||||
|
|
||||||
|
public AuditTrigger()
|
||||||
|
{
|
||||||
|
properties = loadProperties();
|
||||||
|
auditKeyspace = properties.getProperty("keyspace");
|
||||||
|
auditTable = properties.getProperty("table");
|
||||||
|
}
|
||||||
|
|
||||||
public Collection<Mutation> augment(Partition update)
|
public Collection<Mutation> augment(Partition update)
|
||||||
{
|
{
|
||||||
String auditKeyspace = properties.getProperty("keyspace");
|
|
||||||
String auditTable = properties.getProperty("table");
|
|
||||||
|
|
||||||
TableMetadata metadata = Schema.instance.getTableMetadata(auditKeyspace, auditTable);
|
TableMetadata metadata = Schema.instance.getTableMetadata(auditKeyspace, auditTable);
|
||||||
PartitionUpdate.SimpleBuilder audit = PartitionUpdate.simpleBuilder(metadata, UUIDGen.getTimeUUID());
|
PartitionUpdate.SimpleBuilder audit = PartitionUpdate.simpleBuilder(metadata, TimeUUID.Generator.nextTimeUUID());
|
||||||
|
|
||||||
audit.row()
|
audit.row()
|
||||||
.add("keyspace_name", update.metadata().keyspace)
|
.add("keyspace_name", update.metadata().keyspace)
|
||||||
|
|
@ -54,7 +62,7 @@ public class AuditTrigger implements ITrigger
|
||||||
private static Properties loadProperties()
|
private static Properties loadProperties()
|
||||||
{
|
{
|
||||||
Properties properties = new Properties();
|
Properties properties = new Properties();
|
||||||
InputStream stream = AuditTrigger.class.getClassLoader().getResourceAsStream("AuditTrigger.properties");
|
InputStream stream = AuditTrigger.class.getClassLoader().getResourceAsStream(AUDIT_PROPERTIES_FILE_NAME);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
properties.load(stream);
|
properties.load(stream);
|
||||||
|
|
|
||||||
|
|
@ -227,6 +227,7 @@
|
||||||
-Dcassandra.logdir=$PROJECT_DIR$/data/logs
|
-Dcassandra.logdir=$PROJECT_DIR$/data/logs
|
||||||
-Dcassandra.reads.thresholds.coordinator.defensive_checks_enabled=true
|
-Dcassandra.reads.thresholds.coordinator.defensive_checks_enabled=true
|
||||||
-Dcassandra.storagedir=$PROJECT_DIR$/data
|
-Dcassandra.storagedir=$PROJECT_DIR$/data
|
||||||
|
-Dcassandra.triggers_dir=$PROJECT_DIR$/conf/triggers
|
||||||
-Djava.library.path=$PROJECT_DIR$/lib/sigar-bin
|
-Djava.library.path=$PROJECT_DIR$/lib/sigar-bin
|
||||||
-Dlogback.configurationFile=file://$PROJECT_DIR$/conf/logback.xml
|
-Dlogback.configurationFile=file://$PROJECT_DIR$/conf/logback.xml
|
||||||
-Xmx1G
|
-Xmx1G
|
||||||
|
|
|
||||||
|
|
@ -19,11 +19,10 @@ package org.apache.cassandra.triggers;
|
||||||
* under the License.
|
* under the License.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLClassLoader;
|
import java.net.URLClassLoader;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.function.BiPredicate;
|
import java.util.function.BiPredicate;
|
||||||
|
|
@ -39,15 +38,15 @@ import static java.nio.file.Files.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom class loader will load the classes from the class path, CCL will load
|
* Custom class loader will load the classes from the class path, CCL will load
|
||||||
* the classes from the the URL first, if it cannot find the required class it
|
* the classes from the URL first, if it cannot find the required class it
|
||||||
* will let the parent class loader do the its job.
|
* will let the parent class loader do its job.
|
||||||
*
|
*
|
||||||
* Note: If the CCL is GC'ed then the associated classes will be unloaded.
|
* Note: If the CCL is GC'ed then the associated classes will be unloaded.
|
||||||
*/
|
*/
|
||||||
public class CustomClassLoader extends URLClassLoader
|
public class CustomClassLoader extends URLClassLoader
|
||||||
{
|
{
|
||||||
private static final Logger logger = LoggerFactory.getLogger(CustomClassLoader.class);
|
private static final Logger logger = LoggerFactory.getLogger(CustomClassLoader.class);
|
||||||
private final Map<String, Class<?>> cache = new ConcurrentHashMap<String, Class<?>>();
|
private final Map<String, Class<?>> cache = new ConcurrentHashMap<>();
|
||||||
private final ClassLoader parent;
|
private final ClassLoader parent;
|
||||||
|
|
||||||
public CustomClassLoader(ClassLoader parent)
|
public CustomClassLoader(ClassLoader parent)
|
||||||
|
|
@ -83,7 +82,7 @@ public class CustomClassLoader extends URLClassLoader
|
||||||
logger.info("Loading new jar {}", inputJar.absolutePath());
|
logger.info("Loading new jar {}", inputJar.absolutePath());
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
copy(inputJar.toPath(), out.toPath());
|
copy(inputJar.toPath(), out.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||||
addURL(out.toPath().toUri().toURL());
|
addURL(out.toPath().toUri().toURL());
|
||||||
}
|
}
|
||||||
catch (IOException ex)
|
catch (IOException ex)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue