blob: a6bbdb629863a1bfb735fbc3777caea9fdad5a55 [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 ASF 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.cassandra.db.commitlog;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import com.google.common.collect.ImmutableMap;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.config.ParameterizedClass;
import org.apache.cassandra.config.TransparentDataEncryptionOptions;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.io.compress.LZ4Compressor;
import org.apache.cassandra.io.util.FileDataInput;
import org.apache.cassandra.io.util.FileSegmentInputStream;
import org.apache.cassandra.net.MessagingService;
import org.apache.cassandra.security.EncryptionContext;
import org.apache.cassandra.security.EncryptionContextGenerator;
public class CommitLogDescriptorTest
{
private static final byte[] iv = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
ParameterizedClass compression;
TransparentDataEncryptionOptions enabledTdeOptions;
// Context with enabledTdeOptions enabled
EncryptionContext enabledEncryption;
// Context with enabledTdeOptions disabled, with the assumption that enabledTdeOptions was never previously enabled
EncryptionContext neverEnabledEncryption;
// Context with enabledTdeOptions disabled, with the assumption that enabledTdeOptions was previously enabled, but now disabled
// due to operator changing the yaml.
EncryptionContext previouslyEnabledEncryption;
@Before
public void setup()
{
Map<String,String> params = new HashMap<>();
compression = new ParameterizedClass(LZ4Compressor.class.getName(), params);
enabledTdeOptions = EncryptionContextGenerator.createEncryptionOptions();
enabledEncryption = new EncryptionContext(enabledTdeOptions, iv, false);
neverEnabledEncryption = EncryptionContextGenerator.createDisabledContext();
TransparentDataEncryptionOptions disaabledTdeOptions = new TransparentDataEncryptionOptions(false, enabledTdeOptions.cipher, enabledTdeOptions.key_alias, enabledTdeOptions.key_provider);
previouslyEnabledEncryption = new EncryptionContext(disaabledTdeOptions);
DatabaseDescriptor.daemonInitialization();
}
@Test
public void testVersions()
{
Assert.assertTrue(CommitLogDescriptor.isValid("CommitLog-1340512736956320000.log"));
Assert.assertTrue(CommitLogDescriptor.isValid("CommitLog-2-1340512736956320000.log"));
Assert.assertFalse(CommitLogDescriptor.isValid("CommitLog--1340512736956320000.log"));
Assert.assertFalse(CommitLogDescriptor.isValid("CommitLog--2-1340512736956320000.log"));
Assert.assertFalse(CommitLogDescriptor.isValid("CommitLog-2-1340512736956320000-123.log"));
Assert.assertEquals(1340512736956320000L, CommitLogDescriptor.fromFileName("CommitLog-2-1340512736956320000.log").id);
Assert.assertEquals(MessagingService.current_version, new CommitLogDescriptor(1340512736956320000L, null, neverEnabledEncryption).getMessagingVersion());
String newCLName = "CommitLog-" + CommitLogDescriptor.current_version + "-1340512736956320000.log";
Assert.assertEquals(MessagingService.current_version, CommitLogDescriptor.fromFileName(newCLName).getMessagingVersion());
}
// migrated from CommitLogTest
private void testDescriptorPersistence(CommitLogDescriptor desc) throws IOException
{
ByteBuffer buf = ByteBuffer.allocate(1024);
CommitLogDescriptor.writeHeader(buf, desc);
long length = buf.position();
// Put some extra data in the stream.
buf.putDouble(0.1);
buf.flip();
FileDataInput input = new FileSegmentInputStream(buf, "input", 0);
CommitLogDescriptor read = CommitLogDescriptor.readHeader(input, neverEnabledEncryption);
Assert.assertEquals("Descriptor length", length, input.getFilePointer());
Assert.assertEquals("Descriptors", desc, read);
}
// migrated from CommitLogTest
@Test
public void testDescriptorPersistence() throws IOException
{
testDescriptorPersistence(new CommitLogDescriptor(11, null, neverEnabledEncryption));
testDescriptorPersistence(new CommitLogDescriptor(CommitLogDescriptor.VERSION_21, 13, null, neverEnabledEncryption));
testDescriptorPersistence(new CommitLogDescriptor(CommitLogDescriptor.VERSION_22, 15, null, neverEnabledEncryption));
testDescriptorPersistence(new CommitLogDescriptor(CommitLogDescriptor.VERSION_22, 17, new ParameterizedClass("LZ4Compressor", null), neverEnabledEncryption));
testDescriptorPersistence(new CommitLogDescriptor(CommitLogDescriptor.VERSION_22, 19,
new ParameterizedClass("StubbyCompressor", ImmutableMap.of("parameter1", "value1", "flag2", "55", "argument3", "null")
), neverEnabledEncryption));
}
// migrated from CommitLogTest
@Test
public void testDescriptorInvalidParametersSize() throws IOException
{
Map<String, String> params = new HashMap<>();
for (int i=0; i<65535; ++i)
params.put("key"+i, Integer.toString(i, 16));
try {
CommitLogDescriptor desc = new CommitLogDescriptor(CommitLogDescriptor.VERSION_22,
21,
new ParameterizedClass("LZ4Compressor", params),
neverEnabledEncryption);
ByteBuffer buf = ByteBuffer.allocate(1024000);
CommitLogDescriptor.writeHeader(buf, desc);
Assert.fail("Parameter object too long should fail on writing descriptor.");
} catch (ConfigurationException e)
{
// correct path
}
}
@Test
public void constructParametersString_NoCompressionOrEncryption()
{
String json = CommitLogDescriptor.constructParametersString(null, null, Collections.emptyMap());
Assert.assertFalse(json.contains(CommitLogDescriptor.COMPRESSION_CLASS_KEY));
Assert.assertFalse(json.contains(EncryptionContext.ENCRYPTION_CIPHER));
json = CommitLogDescriptor.constructParametersString(null, neverEnabledEncryption, Collections.emptyMap());
Assert.assertFalse(json.contains(CommitLogDescriptor.COMPRESSION_CLASS_KEY));
Assert.assertFalse(json.contains(EncryptionContext.ENCRYPTION_CIPHER));
}
@Test
public void constructParametersString_WithCompressionAndEncryption()
{
String json = CommitLogDescriptor.constructParametersString(compression, enabledEncryption, Collections.emptyMap());
Assert.assertTrue(json.contains(CommitLogDescriptor.COMPRESSION_CLASS_KEY));
Assert.assertTrue(json.contains(EncryptionContext.ENCRYPTION_CIPHER));
}
@Test
public void writeAndReadHeader_NoCompressionOrEncryption() throws IOException
{
CommitLogDescriptor descriptor = new CommitLogDescriptor(CommitLogDescriptor.current_version, 1, null, neverEnabledEncryption);
ByteBuffer buffer = ByteBuffer.allocate(16 * 1024);
CommitLogDescriptor.writeHeader(buffer, descriptor);
buffer.flip();
FileSegmentInputStream dataInput = new FileSegmentInputStream(buffer, null, 0);
CommitLogDescriptor result = CommitLogDescriptor.readHeader(dataInput, neverEnabledEncryption);
Assert.assertNotNull(result);
Assert.assertNull(result.compression);
Assert.assertFalse(result.getEncryptionContext().isEnabled());
}
@Test
public void writeAndReadHeader_OnlyCompression() throws IOException
{
CommitLogDescriptor descriptor = new CommitLogDescriptor(CommitLogDescriptor.current_version, 1, compression, neverEnabledEncryption);
ByteBuffer buffer = ByteBuffer.allocate(16 * 1024);
CommitLogDescriptor.writeHeader(buffer, descriptor);
buffer.flip();
FileSegmentInputStream dataInput = new FileSegmentInputStream(buffer, null, 0);
CommitLogDescriptor result = CommitLogDescriptor.readHeader(dataInput, neverEnabledEncryption);
Assert.assertNotNull(result);
Assert.assertEquals(compression, result.compression);
Assert.assertFalse(result.getEncryptionContext().isEnabled());
}
@Test
public void writeAndReadHeader_WithEncryptionHeader_EncryptionEnabledInYaml() throws IOException
{
CommitLogDescriptor descriptor = new CommitLogDescriptor(CommitLogDescriptor.current_version, 1, null, enabledEncryption);
ByteBuffer buffer = ByteBuffer.allocate(16 * 1024);
CommitLogDescriptor.writeHeader(buffer, descriptor);
buffer.flip();
FileSegmentInputStream dataInput = new FileSegmentInputStream(buffer, null, 0);
CommitLogDescriptor result = CommitLogDescriptor.readHeader(dataInput, enabledEncryption);
Assert.assertNotNull(result);
Assert.assertNull(result.compression);
Assert.assertTrue(result.getEncryptionContext().isEnabled());
Assert.assertArrayEquals(iv, result.getEncryptionContext().getIV());
}
/**
* Check that even though enabledTdeOptions is disabled in the yaml, we can still read the commit log header as encrypted.
*/
@Test
public void writeAndReadHeader_WithEncryptionHeader_EncryptionDisabledInYaml() throws IOException
{
CommitLogDescriptor descriptor = new CommitLogDescriptor(CommitLogDescriptor.current_version, 1, null, enabledEncryption);
ByteBuffer buffer = ByteBuffer.allocate(16 * 1024);
CommitLogDescriptor.writeHeader(buffer, descriptor);
buffer.flip();
FileSegmentInputStream dataInput = new FileSegmentInputStream(buffer, null, 0);
CommitLogDescriptor result = CommitLogDescriptor.readHeader(dataInput, previouslyEnabledEncryption);
Assert.assertNotNull(result);
Assert.assertNull(result.compression);
Assert.assertTrue(result.getEncryptionContext().isEnabled());
Assert.assertArrayEquals(iv, result.getEncryptionContext().getIV());
}
/**
* Shouldn't happen in the real world (should only have either compression or enabledTdeOptions), but the header
* functionality should be correct
*/
@Test
public void writeAndReadHeader_WithCompressionAndEncryption() throws IOException
{
CommitLogDescriptor descriptor = new CommitLogDescriptor(CommitLogDescriptor.current_version, 1, compression, enabledEncryption);
ByteBuffer buffer = ByteBuffer.allocate(16 * 1024);
CommitLogDescriptor.writeHeader(buffer, descriptor);
buffer.flip();
FileSegmentInputStream dataInput = new FileSegmentInputStream(buffer, null, 0);
CommitLogDescriptor result = CommitLogDescriptor.readHeader(dataInput, enabledEncryption);
Assert.assertNotNull(result);
Assert.assertEquals(compression, result.compression);
Assert.assertTrue(result.getEncryptionContext().isEnabled());
Assert.assertEquals(enabledEncryption, result.getEncryptionContext());
Assert.assertArrayEquals(iv, result.getEncryptionContext().getIV());
}
@Test
public void equals_NoCompressionOrEncryption()
{
CommitLogDescriptor desc1 = new CommitLogDescriptor(CommitLogDescriptor.current_version, 1, null, null);
Assert.assertEquals(desc1, desc1);
CommitLogDescriptor desc2 = new CommitLogDescriptor(CommitLogDescriptor.current_version, 1, null, null);
Assert.assertEquals(desc1, desc2);
desc1 = new CommitLogDescriptor(CommitLogDescriptor.current_version, 1, null, neverEnabledEncryption);
Assert.assertEquals(desc1, desc1);
desc2 = new CommitLogDescriptor(CommitLogDescriptor.current_version, 1, null, neverEnabledEncryption);
Assert.assertEquals(desc1, desc2);
desc1 = new CommitLogDescriptor(CommitLogDescriptor.current_version, 1, null, previouslyEnabledEncryption);
Assert.assertEquals(desc1, desc1);
desc2 = new CommitLogDescriptor(CommitLogDescriptor.current_version, 1, null, previouslyEnabledEncryption);
Assert.assertEquals(desc1, desc2);
}
@Test
public void equals_OnlyCompression()
{
CommitLogDescriptor desc1 = new CommitLogDescriptor(CommitLogDescriptor.current_version, 1, compression, null);
Assert.assertEquals(desc1, desc1);
CommitLogDescriptor desc2 = new CommitLogDescriptor(CommitLogDescriptor.current_version, 1, compression, null);
Assert.assertEquals(desc1, desc2);
desc1 = new CommitLogDescriptor(CommitLogDescriptor.current_version, 1, compression, neverEnabledEncryption);
Assert.assertEquals(desc1, desc1);
desc2 = new CommitLogDescriptor(CommitLogDescriptor.current_version, 1, compression, neverEnabledEncryption);
Assert.assertEquals(desc1, desc2);
desc1 = new CommitLogDescriptor(CommitLogDescriptor.current_version, 1, compression, previouslyEnabledEncryption);
Assert.assertEquals(desc1, desc1);
desc2 = new CommitLogDescriptor(CommitLogDescriptor.current_version, 1, compression, previouslyEnabledEncryption);
Assert.assertEquals(desc1, desc2);
}
@Test
public void equals_OnlyEncryption()
{
CommitLogDescriptor desc1 = new CommitLogDescriptor(CommitLogDescriptor.current_version, 1, null, enabledEncryption);
Assert.assertEquals(desc1, desc1);
CommitLogDescriptor desc2 = new CommitLogDescriptor(CommitLogDescriptor.current_version, 1, null, enabledEncryption);
Assert.assertEquals(desc1, desc2);
desc1 = new CommitLogDescriptor(CommitLogDescriptor.current_version, 1, null, neverEnabledEncryption);
Assert.assertEquals(desc1, desc1);
desc2 = new CommitLogDescriptor(CommitLogDescriptor.current_version, 1, null, neverEnabledEncryption);
Assert.assertEquals(desc1, desc2);
desc1 = new CommitLogDescriptor(CommitLogDescriptor.current_version, 1, null, previouslyEnabledEncryption);
Assert.assertEquals(desc1, desc1);
desc2 = new CommitLogDescriptor(CommitLogDescriptor.current_version, 1, null, previouslyEnabledEncryption);
Assert.assertEquals(desc1, desc2);
}
/**
* Shouldn't have both enabled in real life, but ensure they are correct, nonetheless
*/
@Test
public void equals_BothCompressionAndEncryption()
{
CommitLogDescriptor desc1 = new CommitLogDescriptor(CommitLogDescriptor.current_version, 1, compression, enabledEncryption);
Assert.assertEquals(desc1, desc1);
CommitLogDescriptor desc2 = new CommitLogDescriptor(CommitLogDescriptor.current_version, 1, compression, enabledEncryption);
Assert.assertEquals(desc1, desc2);
}
}