/*
* Copyright 2016 Hortonworks.
*
* 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 com.hortonworks.registries.schemaregistry.avro;
import com.google.common.io.Resources;
import com.hortonworks.registries.schemaregistry.SchemaIdVersion;
import com.hortonworks.registries.schemaregistry.SchemaMetadata;
import com.hortonworks.registries.schemaregistry.SchemaMetadataInfo;
import com.hortonworks.registries.schemaregistry.SchemaVersion;
import com.hortonworks.registries.schemaregistry.SchemaVersionKey;
import com.hortonworks.registries.schemaregistry.client.SchemaRegistryClient;
import com.hortonworks.registries.schemaregistry.webservice.LocalSchemaRegistryServer;
import org.apache.commons.io.IOUtils;
import org.apache.curator.test.TestingServer;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileWriter;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
*
*/
public class LocalRegistryServerHATest {
private static final Logger LOG = LoggerFactory.getLogger(LocalRegistryServerHATest.class);
private TestingServer testingServer;
private List<LocalSchemaRegistryServer> registryServers;
@Before
public void startZooKeeper() throws Exception {
testingServer = new TestingServer(true);
URI configPath = Resources.getResource("schema-registry-test-ha.yaml").toURI();
String fileContent = IOUtils.toString(configPath, "UTF-8");
File registryConfigFile = File.createTempFile("ha-", ".yaml");
registryConfigFile.deleteOnExit();
try (FileWriter writer = new FileWriter(registryConfigFile)) {
IOUtils.write(fileContent.replace("__zk_connect_url__", testingServer.getConnectString()), writer);
}
List<LocalSchemaRegistryServer> schemaRegistryServers = new ArrayList<>();
for (int i = 1; i < 4; i++) {
LocalSchemaRegistryServer server = new LocalSchemaRegistryServer(registryConfigFile.getAbsolutePath());
schemaRegistryServers.add(server);
server.start();
}
registryServers = Collections.unmodifiableList(schemaRegistryServers);
}
@After
public void stopZooKeeper() throws Exception {
for (LocalSchemaRegistryServer registryServer : registryServers) {
try {
registryServer.stop();
} catch (Exception e) {
// ignore any exceptions as one of the servers have been already stopped and that may throw an exception.
}
}
testingServer.stop();
}
private LocalSchemaRegistryServer leaderSchemaRegistryServer() {
LocalSchemaRegistryServer leader = null;
while (leader == null) {
for (LocalSchemaRegistryServer registryServer : registryServers) {
if (registryServer.hasLeadership()) {
leader = registryServer;
}
}
}
//leader may have been changed before it is returned when the current leader leaves HA quorum.
return leader;
}
private LocalSchemaRegistryServer followerSchemaRegistryServer() {
leaderSchemaRegistryServer();
for (LocalSchemaRegistryServer registryServer : registryServers) {
if (!registryServer.hasLeadership()) {
return registryServer;
}
}
throw new IllegalStateException("None of the servers are followers!!");
}
@Test
public void testHASanity() throws Exception {
LocalSchemaRegistryServer followerServer = followerSchemaRegistryServer();
SchemaRegistryClient schemaRegistryClient = createSchemaRegistryClient(followerServer.getLocalPort());
// registering schema metadata on follower, this should have been redirected to leader.
String schemaName = "foo";
SchemaMetadata schemaMetadata = new SchemaMetadata.Builder(schemaName).type("avro").build();
Long schemaId = schemaRegistryClient.registerSchemaMetadata(schemaMetadata);
Assert.assertNotNull(schemaId);
// registering a new schema on follower, this should have been redirected to leader.
String schema1 = IOUtils.toString(LocalRegistryServerTest.class.getResourceAsStream("/schema-1.avsc"), "UTF-8");
SchemaIdVersion v1 = schemaRegistryClient.addSchemaVersion(schemaName,
new SchemaVersion(schema1, "Initial version of the schema"));
// retrieve schema on leader as the schema data is stored in memory in leader. this data does not exist on
// followers as the storage is inmemory.
LocalSchemaRegistryServer leaderServer = leaderSchemaRegistryServer();
int leaderPort = leaderServer.getLocalPort();
SchemaRegistryClient leaderClient = createSchemaRegistryClient(leaderPort);
SchemaMetadataInfo schemaMetadataInfo = leaderClient.getSchemaMetadataInfo(schemaName);
Assert.assertEquals(schemaMetadata, schemaMetadataInfo.getSchemaMetadata());
// stop the leader server
leaderServer.stop();
// get the new leader server and run operations.
LocalSchemaRegistryServer newLeaderServer = leaderSchemaRegistryServer();
Assert.assertNotEquals(leaderPort, newLeaderServer.getLocalPort());
leaderClient = createSchemaRegistryClient(newLeaderServer.getLocalPort());
String receivedSchema = leaderClient.getSchemaVersionInfo(new SchemaVersionKey(schemaName, v1.getVersion())).getSchemaText();
Assert.assertEquals(schema1, receivedSchema);
}
private SchemaRegistryClient createSchemaRegistryClient(int localPort) {
final String rootUrl = String.format("http://localhost:%d/api/v1", localPort);
final Map<String, String> SCHEMA_REGISTRY_CLIENT_CONF = Collections.singletonMap(SchemaRegistryClient.Configuration.SCHEMA_REGISTRY_URL.name(), rootUrl);
return new SchemaRegistryClient(SCHEMA_REGISTRY_CLIENT_CONF);
}
}