/* * Licensed to Elastic Search and Shay Banon under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. ElasticSearch 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.elasticsearch.test.integration.cluster; import org.elasticsearch.ElasticSearchException; import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; import org.elasticsearch.cluster.ClusterService; import org.elasticsearch.cluster.LocalNodeMasterListener; import org.elasticsearch.common.component.AbstractLifecycleComponent; import org.elasticsearch.common.component.LifecycleComponent; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.inject.Singleton; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.node.internal.InternalNode; import org.elasticsearch.plugins.AbstractPlugin; import org.elasticsearch.threadpool.ThreadPool; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.Collection; import java.util.List; import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; /** * */ public class LocalNodeMasterListenerTests extends AbstractZenNodesTests { @AfterMethod public void closeNodes() { closeAllNodes(); } @Test public void testListenerCallbacks() throws Exception { Settings settings = settingsBuilder() .put("discovery.zen.minimum_master_nodes", 1) .put("discovery.zen.ping_timeout", "200ms") .put("discovery.initial_state_timeout", "500ms") .put("plugin.types", TestPlugin.class.getName()) .build(); InternalNode node1 = (InternalNode) startNode("node1", settings); ClusterService clusterService1 = node1.injector().getInstance(ClusterService.class); MasterAwareService testService1 = node1.injector().getInstance(MasterAwareService.class); // the first node should be a master as the minimum required is 1 assertThat(clusterService1.state().nodes().masterNode(), notNullValue()); assertThat(clusterService1.state().nodes().localNodeMaster(), is(true)); assertThat(testService1.master(), is(true)); InternalNode node2 = (InternalNode) startNode("node2", settings); ClusterService clusterService2 = node2.injector().getInstance(ClusterService.class); MasterAwareService testService2 = node2.injector().getInstance(MasterAwareService.class); ClusterHealthResponse clusterHealth = node2.client().admin().cluster().prepareHealth().setWaitForNodes("2").execute().actionGet(); assertThat(clusterHealth.timedOut(), equalTo(false)); // the second node should not be the master as node1 is already the master. assertThat(clusterService2.state().nodes().localNodeMaster(), is(false)); assertThat(testService2.master(), is(false)); node1.close(); clusterHealth = node2.client().admin().cluster().prepareHealth().setWaitForNodes("1").execute().actionGet(); assertThat(clusterHealth.timedOut(), equalTo(false)); // now that node1 is closed, node2 should be elected as master assertThat(clusterService2.state().nodes().localNodeMaster(), is(true)); assertThat(testService2.master(), is(true)); Settings newSettings = settingsBuilder() .put("discovery.zen.minimum_master_nodes", 2) .build(); node2.client().admin().cluster().prepareUpdateSettings().setTransientSettings(newSettings).execute().actionGet(); Thread.sleep(200); // there should not be any master as the minimum number of required eligible masters is not met assertThat(clusterService2.state().nodes().masterNode(), is(nullValue())); assertThat(testService2.master(), is(false)); node1 = (InternalNode) startNode("node1", settings); clusterService1 = node1.injector().getInstance(ClusterService.class); testService1 = node1.injector().getInstance(MasterAwareService.class); clusterHealth = node2.client().admin().cluster().prepareHealth().setWaitForNodes("2").execute().actionGet(); assertThat(clusterHealth.timedOut(), equalTo(false)); // now that we started node1 again, a new master should be elected assertThat(clusterService1.state().nodes().masterNode(), is(notNullValue())); if ("node1".equals(clusterService1.state().nodes().masterNode().name())) { assertThat(testService1.master(), is(true)); assertThat(testService2.master(), is(false)); } else { assertThat(testService1.master(), is(false)); assertThat(testService2.master(), is(true)); } } public static class TestPlugin extends AbstractPlugin { @Override public String name() { return "test plugin"; } @Override public String description() { return "test plugin"; } @Override public Collection<Class<? extends LifecycleComponent>> services() { List<Class<? extends LifecycleComponent>> services = new ArrayList<Class<? extends LifecycleComponent>>(1); services.add(MasterAwareService.class); return services; } } @Singleton public static class MasterAwareService extends AbstractLifecycleComponent<MasterAwareService> implements LocalNodeMasterListener { private final ClusterService clusterService; private volatile boolean master; @Inject public MasterAwareService(Settings settings, ClusterService clusterService) { super(settings); clusterService.add(this); this.clusterService = clusterService; logger.info("initialized test service"); } @Override public void onMaster() { logger.info("on master [" + clusterService.state().nodes().localNode() + "]"); master = true; } @Override public void offMaster() { logger.info("off master [" + clusterService.state().nodes().localNode() + "]"); master = false; } public boolean master() { return master; } @Override protected void doStart() throws ElasticSearchException { } @Override protected void doStop() throws ElasticSearchException { } @Override protected void doClose() throws ElasticSearchException { } @Override public String executorName() { return ThreadPool.Names.SAME; } } }