blob: be2a0f34a8cb0f5df4ace70af938c85329bf3393 [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.bookkeeper.meta;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.collect.Lists;
import java.io.IOException;
import java.util.Base64;
import java.util.Optional;
import java.util.Random;
import org.apache.bookkeeper.client.LedgerMetadataBuilder;
import org.apache.bookkeeper.client.api.DigestType;
import org.apache.bookkeeper.client.api.LedgerMetadata;
import org.apache.bookkeeper.net.BookieSocketAddress;
import org.junit.Assert;
import org.junit.Test;
/**
* Test Ledger Metadata serialization and deserialization.
*/
public class TestLedgerMetadataSerDe {
// as used in 4.0.x & 4.1.x
private static final String version1 =
"Qm9va2llTWV0YWRhdGFGb3JtYXRWZXJzaW9uCTEKMgozCjAKMAkxOTIuMC4yLjE6MTIzNAkxOTIu"
+ "MC4yLjI6MTIzNAkxOTIuMC4yLjM6MTIzNAotMTAyCUNMT1NFRA==";
// as used in 4.2.x & 4.3.x (text protobuf based metadata, password and digest introduced)
private static final String version2 =
"Qm9va2llTWV0YWRhdGFGb3JtYXRWZXJzaW9uCTIKcXVvcnVtU2l6ZTogMgplbnNlbWJsZVNpemU6I"
+ "DMKbGVuZ3RoOiAwCmxhc3RFbnRyeUlkOiAtMQpzdGF0ZTogSU5fUkVDT1ZFUlkKc2VnbWVudCB7"
+ "CiAgZW5zZW1ibGVNZW1iZXI6ICIxOTIuMC4yLjE6MTIzNCIKICBlbnNlbWJsZU1lbWJlcjogIjE"
+ "5Mi4wLjIuMjoxMjM0IgogIGVuc2VtYmxlTWVtYmVyOiAiMTkyLjAuMi4zOjEyMzQiCiAgZmlyc3"
+ "RFbnRyeUlkOiAwCn0KZGlnZXN0VHlwZTogQ1JDMzIKcGFzc3dvcmQ6ICJwYXNzd2QiCmFja1F1b"
+ "3J1bVNpemU6IDIK";
// version 2 + ctime, as used in 4.4.x to 4.8.x (ctime is optional from 4.6.x onwards)
private static final String version2ctime =
"Qm9va2llTWV0YWRhdGFGb3JtYXRWZXJzaW9uCTIKcXVvcnVtU2l6ZTogMgplbnNlbWJsZVNpemU6I"
+ "DMKbGVuZ3RoOiAwCmxhc3RFbnRyeUlkOiAtMQpzdGF0ZTogSU5fUkVDT1ZFUlkKc2VnbWVudCB7"
+ "CiAgZW5zZW1ibGVNZW1iZXI6ICIxOTIuMC4yLjE6MTIzNCIKICBlbnNlbWJsZU1lbWJlcjogIjE"
+ "5Mi4wLjIuMjoxMjM0IgogIGVuc2VtYmxlTWVtYmVyOiAiMTkyLjAuMi4zOjEyMzQiCiAgZmlyc3"
+ "RFbnRyeUlkOiAwCn0KZGlnZXN0VHlwZTogQ1JDMzIKcGFzc3dvcmQ6ICJwYXNzd2QiCmFja1F1b"
+ "3J1bVNpemU6IDIKY3RpbWU6IDE1NDQwMDIzODMwNzUK";
// version 3, since 4.9.x, protobuf binary format
private static final String version3 =
"Qm9va2llTWV0YWRhdGFGb3JtYXRWZXJzaW9uCTMKYAgCEAMYACD///////////8BKAEyMgoOMTkyL"
+ "jAuMi4xOjMxODEKDjE5Mi4wLjIuMjozMTgxCg4xOTIuMC4yLjM6MzE4MRAAOANCBmZvb2JhckgB"
+ "UP///////////wFgAA==";
private static void testDecodeEncode(String encoded) throws Exception {
LedgerMetadataSerDe serDe = new LedgerMetadataSerDe();
LedgerMetadata md = serDe.parseConfig(Base64.getDecoder().decode(encoded), 59L, Optional.empty());
String reserialized = Base64.getEncoder().encodeToString(serDe.serialize(md));
Assert.assertEquals(encoded, reserialized);
}
@Test
public void testVersion1SerDe() throws Exception {
testDecodeEncode(version1);
}
@Test
public void testVersion2SerDe() throws Exception {
testDecodeEncode(version2);
}
@Test
public void testVersion2CtimeSerDe() throws Exception {
testDecodeEncode(version2ctime);
}
@Test
public void testVersion3SerDe() throws Exception {
testDecodeEncode(version3);
}
@Test(expected = IOException.class)
public void testJunkSerDe() throws Exception {
LedgerMetadataSerDe serDe = new LedgerMetadataSerDe();
String junk = "";
serDe.parseConfig(junk.getBytes(UTF_8), 59L, Optional.empty());
}
@Test(expected = IOException.class)
public void testJunk2SerDe() throws Exception {
byte[] randomBytes = new byte[1000];
new Random().nextBytes(randomBytes);
LedgerMetadataSerDe serDe = new LedgerMetadataSerDe();
serDe.parseConfig(randomBytes, 59L, Optional.empty());
}
@Test(expected = IOException.class)
public void testJunkVersionSerDe() throws Exception {
byte[] junkVersion = "BookieMetadataFormatVersion\tfoobar\nblahblah".getBytes(UTF_8);
LedgerMetadataSerDe serDe = new LedgerMetadataSerDe();
serDe.parseConfig(junkVersion, 59L, Optional.empty());
}
@Test(expected = IOException.class)
public void testVeryLongVersionSerDe() throws Exception {
byte[] veryLongVersion = "BookieMetadataFormatVersion\t123456789123456789\nblahblah".getBytes(UTF_8);
LedgerMetadataSerDe serDe = new LedgerMetadataSerDe();
serDe.parseConfig(veryLongVersion, 59L, Optional.empty());
}
@Test
public void testPeggedToV3SerDe() throws Exception {
LedgerMetadataSerDe serDe = new LedgerMetadataSerDe();
LedgerMetadata metadata = LedgerMetadataBuilder.create().withId(13L)
.withEnsembleSize(3).withWriteQuorumSize(2).withAckQuorumSize(1)
.withPassword("foobar".getBytes(UTF_8)).withDigestType(DigestType.CRC32C)
.newEnsembleEntry(0L, Lists.newArrayList(new BookieSocketAddress("192.0.2.1", 3181).toBookieId(),
new BookieSocketAddress("192.0.2.2", 3181).toBookieId(),
new BookieSocketAddress("192.0.2.3", 3181).toBookieId()))
.build();
byte[] encoded = serDe.serialize(metadata);
LedgerMetadata decoded = serDe.parseConfig(encoded, 59L, Optional.empty());
Assert.assertEquals(LedgerMetadataSerDe.METADATA_FORMAT_VERSION_3, decoded.getMetadataFormatVersion());
}
@Test
public void testStoreSystemtimeAsLedgerCtimeEnabledWithNewerVersion()
throws Exception {
LedgerMetadata lm = LedgerMetadataBuilder.create().withId(13L)
.withEnsembleSize(3).withWriteQuorumSize(2).withAckQuorumSize(1)
.withPassword("foobar".getBytes(UTF_8)).withDigestType(DigestType.CRC32C)
.newEnsembleEntry(0L, Lists.newArrayList(new BookieSocketAddress("192.0.2.1", 1234).toBookieId(),
new BookieSocketAddress("192.0.2.2", 1234).toBookieId(),
new BookieSocketAddress("192.0.2.3", 1234).toBookieId()))
.withCreationTime(123456L)
.storingCreationTime(true)
.build();
LedgerMetadataSerDe serDe = new LedgerMetadataSerDe();
byte[] serialized = serDe.serialize(lm);
LedgerMetadata deserialized = serDe.parseConfig(serialized, 59L, Optional.of(654321L));
Assert.assertEquals(deserialized.getCtime(), 123456L);
// give it another round
LedgerMetadata deserialized2 = serDe.parseConfig(serDe.serialize(deserialized), 59L, Optional.of(98765L));
Assert.assertEquals(deserialized2.getCtime(), 123456L);
}
@Test
public void testStoreSystemtimeAsLedgerCtimeDisabledWithNewerVersion()
throws Exception {
LedgerMetadata lm = LedgerMetadataBuilder.create().withId(13L)
.withEnsembleSize(3).withWriteQuorumSize(2).withAckQuorumSize(1)
.withPassword("foobar".getBytes(UTF_8)).withDigestType(DigestType.CRC32C)
.newEnsembleEntry(0L, Lists.newArrayList(new BookieSocketAddress("192.0.2.1", 1234).toBookieId(),
new BookieSocketAddress("192.0.2.2", 1234).toBookieId(),
new BookieSocketAddress("192.0.2.3", 1234).toBookieId()))
.build();
LedgerMetadataSerDe serDe = new LedgerMetadataSerDe();
byte[] serialized = serDe.serialize(lm);
LedgerMetadata deserialized = serDe.parseConfig(serialized, 59L, Optional.of(654321L));
Assert.assertEquals(654321L, deserialized.getCtime());
// give it another round
LedgerMetadata deserialized2 = serDe.parseConfig(serDe.serialize(deserialized), 59L, Optional.of(98765L));
Assert.assertEquals(98765L, deserialized2.getCtime());
}
}