@Override public boolean isOperational(ProcessId processId, boolean local) { if (local) { return operationalLocalProcesses.computeIfAbsent(processId, p -> false); } if (processId.equals(ProcessId.ELASTICSEARCH)) { return isElasticSearchAvailable(); } for (Map.Entry<ClusterProcess, Boolean> entry : operationalProcesses.entrySet()) { if (entry.getKey().getProcessId().equals(processId) && entry.getValue()) { return true; } } return false; }
private void removeOperationalProcess(String uuid) { for (ClusterProcess clusterProcess : operationalProcesses.keySet()) { if (clusterProcess.getNodeUuid().equals(uuid)) { LOGGER.debug("Set node process off for [{}:{}] : ", clusterProcess.getNodeUuid(), clusterProcess.getProcessId()); hzMember.getReplicatedMap(OPERATIONAL_PROCESSES).put(clusterProcess, Boolean.FALSE); } } } }
public ClusterAppStateImpl(AppSettings settings, HazelcastMember hzMember, EsConnector esConnector) { this.hzMember = hzMember; // Get or create the replicated map operationalProcesses = (ReplicatedMap) hzMember.getReplicatedMap(OPERATIONAL_PROCESSES); operationalProcessListenerUUID = operationalProcesses.addEntryListener(new OperationalProcessListener()); nodeDisconnectedListenerUUID = hzMember.getCluster().addMembershipListener(new NodeDisconnectedListener()); if (ClusterSettings.isLocalElasticsearchEnabled(settings)) { this.healthStateSharing = new HealthStateSharingImpl(hzMember, new SearchNodeHealthProvider(settings.getProps(), this, NetworkUtilsImpl.INSTANCE)); this.healthStateSharing.start(); } this.esConnector = esConnector; }
@Test public void get_returns_status_GREEN_if_elasticsearch_process_is_operational_in_ClusterAppState() { Properties properties = new Properties(); setRequiredPropertiesAndMocks(properties); when(clusterAppState.isOperational(ProcessId.ELASTICSEARCH, true)).thenReturn(true); SearchNodeHealthProvider underTest = new SearchNodeHealthProvider(new Props(properties), clusterAppState, networkUtils, clock); NodeHealth nodeHealth = underTest.get(); assertThat(nodeHealth.getStatus()).isEqualTo(NodeHealth.Status.GREEN); }
@Test public void registerClusterName_publishes_clusterName_on_first_call() { try (ClusterAppStateImpl underTest = new ClusterAppStateImpl(new TestAppSettings(), newHzMember(), mock(EsConnector.class))) { underTest.registerClusterName("foo"); assertThat(underTest.getHazelcastMember().getAtomicReference(CLUSTER_NAME).get()) .isEqualTo("foo"); } }
@Test public void registerSonarQubeVersion_publishes_version_on_first_call() { try (ClusterAppStateImpl underTest = new ClusterAppStateImpl(new TestAppSettings(), newHzMember(), mock(EsConnector.class))) { underTest.registerSonarQubeVersion("6.4.1.5"); assertThat(underTest.getHazelcastMember().getAtomicReference(SONARQUBE_VERSION).get()) .isEqualTo("6.4.1.5"); } }
@Test public void tryToLockWebLeader_returns_true_only_for_the_first_call() { try (ClusterAppStateImpl underTest = new ClusterAppStateImpl(new TestAppSettings(), newHzMember(), mock(EsConnector.class))) { assertThat(underTest.tryToLockWebLeader()).isEqualTo(true); assertThat(underTest.tryToLockWebLeader()).isEqualTo(false); } }
@Test public void reset_always_throws_ISE() { try (ClusterAppStateImpl underTest = new ClusterAppStateImpl(new TestAppSettings(), newHzMember(), mock(EsConnector.class))) { expectedException.expect(IllegalStateException.class); expectedException.expectMessage("state reset is not supported in cluster mode"); underTest.reset(); } }
@Override public void close() { esConnector.stop(); if (hzMember != null) { if (healthStateSharing != null) { healthStateSharing.stop(); } try { // Removing listeners operationalProcesses.removeEntryListener(operationalProcessListenerUUID); hzMember.getCluster().removeMembershipListener(nodeDisconnectedListenerUUID); // Removing the operationalProcess from the replicated map operationalProcesses.keySet().forEach( clusterNodeProcess -> { if (clusterNodeProcess.getNodeUuid().equals(hzMember.getUuid())) { operationalProcesses.remove(clusterNodeProcess); } }); // Shutdown Hazelcast properly hzMember.close(); } catch (HazelcastInstanceNotActiveException e) { // hazelcastCluster may be already closed by the shutdown hook LOGGER.debug("Unable to close Hazelcast cluster", e); } } }
@Override public NodeHealth get() { NodeHealth.Builder builder = NodeHealth.newNodeHealthBuilder(); if (clusterAppState.isOperational(ProcessId.ELASTICSEARCH, true)) { builder.setStatus(NodeHealth.Status.GREEN); } else { builder.setStatus(NodeHealth.Status.RED) .addCause("Elasticsearch is not operational"); } return builder .setDetails(nodeDetails) .build(); }
@Override public void setOperational(ProcessId processId) { operationalLocalProcesses.put(processId, true); operationalProcesses.put(new ClusterProcess(hzMember.getUuid(), processId), Boolean.TRUE); }
public AppState create() { if (ClusterSettings.shouldStartHazelcast(settings)) { EsConnector esConnector = createEsConnector(settings.getProps()); HazelcastMember hzMember = createHzMember(settings.getProps()); return new ClusterAppStateImpl(settings, hzMember, esConnector); } return new AppStateImpl(); }
@Override public void entryAdded(EntryEvent<ClusterProcess, Boolean> event) { if (event.getValue()) { listeners.forEach(appStateListener -> appStateListener.onAppStateOperational(event.getKey().getProcessId())); } }
@Override public void memberRemoved(MembershipEvent membershipEvent) { removeOperationalProcess(membershipEvent.getMember().getUuid()); }
@Test public void get_returns_status_RED_with_cause_if_elasticsearch_process_is_not_operational_in_ClusterAppState() { Properties properties = new Properties(); setRequiredPropertiesAndMocks(properties); when(clusterAppState.isOperational(ProcessId.ELASTICSEARCH, true)).thenReturn(false); SearchNodeHealthProvider underTest = new SearchNodeHealthProvider(new Props(properties), clusterAppState, networkUtils, clock); NodeHealth nodeHealth = underTest.get(); assertThat(nodeHealth.getStatus()).isEqualTo(NodeHealth.Status.RED); assertThat(nodeHealth.getCauses()).containsOnly("Elasticsearch is not operational"); }
@Test public void registerClusterName_throws_MessageException_if_clusterName_is_different() { try (ClusterAppStateImpl underTest = new ClusterAppStateImpl(new TestAppSettings(), newHzMember(), mock(EsConnector.class))) { // Register first version underTest.getHazelcastMember().getAtomicReference(CLUSTER_NAME).set("goodClusterName"); expectedException.expect(MessageException.class); expectedException.expectMessage("This node has a cluster name [badClusterName], which does not match [goodClusterName] from the cluster"); // Registering a second different cluster name must trigger an exception underTest.registerClusterName("badClusterName"); } }
@Test public void registerSonarQubeVersion_throws_ISE_if_initial_version_is_different() { // Now launch an instance that try to be part of the hzInstance cluster try (ClusterAppStateImpl underTest = new ClusterAppStateImpl(new TestAppSettings(), newHzMember(), mock(EsConnector.class))) { // Register first version underTest.getHazelcastMember().getAtomicReference(SONARQUBE_VERSION).set("6.6.0.1111"); expectedException.expect(IllegalStateException.class); expectedException.expectMessage("The local version 6.7.0.9999 is not the same as the cluster 6.6.0.1111"); // Registering a second different version must trigger an exception underTest.registerSonarQubeVersion("6.7.0.9999"); } }
@Override public NodeHealth get() { NodeHealth.Builder builder = NodeHealth.newNodeHealthBuilder(); if (clusterAppState.isOperational(ProcessId.ELASTICSEARCH, true)) { builder.setStatus(NodeHealth.Status.GREEN); } else { builder.setStatus(NodeHealth.Status.RED) .addCause("Elasticsearch is not operational"); } return builder .setDetails(nodeDetails) .build(); }
@Test public void test_equality() { ClusterProcess clusterProcess = new ClusterProcess("A", ProcessId.WEB_SERVER); assertThat(clusterProcess) .isNotEqualTo(null) .isEqualTo(clusterProcess) .isNotEqualTo(new ClusterProcess("B", ProcessId.WEB_SERVER)) .isNotEqualTo(new ClusterProcess("A", ProcessId.ELASTICSEARCH)) .isEqualTo(new ClusterProcess("A", ProcessId.WEB_SERVER)); } }
@Override public void entryUpdated(EntryEvent<ClusterProcess, Boolean> event) { if (event.getValue()) { listeners.forEach(appStateListener -> appStateListener.onAppStateOperational(event.getKey().getProcessId())); } }