From 3fdcc9bba7eb871750341d6c1f2dd97d18a6757c Mon Sep 17 00:00:00 2001 From: Miles-Garnsey Date: Tue, 5 Sep 2023 13:56:49 +1000 Subject: [PATCH] Implement HttpCassandraManagementProxy.getEndpointToHostId() and HttpCassandraManagementProxy.getLocalEndpoint() --- .../http/HttpCassandraManagementProxy.java | 32 ++++++++++++- .../HttpCassandraManagementProxyTest.java | 46 +++++++++++++++++++ 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/src/server/src/main/java/io/cassandrareaper/management/http/HttpCassandraManagementProxy.java b/src/server/src/main/java/io/cassandrareaper/management/http/HttpCassandraManagementProxy.java index aa989eabd..49f9f07f8 100644 --- a/src/server/src/main/java/io/cassandrareaper/management/http/HttpCassandraManagementProxy.java +++ b/src/server/src/main/java/io/cassandrareaper/management/http/HttpCassandraManagementProxy.java @@ -27,6 +27,7 @@ import java.io.IOException; import java.math.BigInteger; +import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.ArrayList; @@ -61,7 +62,9 @@ import com.datastax.mgmtapi.client.model.StatusChange; import com.datastax.mgmtapi.client.model.TakeSnapshotRequest; import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableBiMap; import com.google.common.collect.Maps; +import com.google.common.net.InetAddresses; import org.apache.cassandra.repair.RepairParallelism; import org.apache.cassandra.utils.progress.ProgressEventType; import org.slf4j.Logger; @@ -135,13 +138,38 @@ public Map, List> getRangeToEndpointMap(String keyspace) th @NotNull @Override public String getLocalEndpoint() throws ReaperException { - return null; // TODO: implement me. + // TODO: validate that this works in all situations. I suspect that if any address translation is + // happening we'll see failures here, but address translation is not in scope in this phase. + // The logic is that host is either a DNS address, or an IP address. If it's a DNS address, we do a + // reverse lookup to get the IP. + String regex = "^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." + + "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"; + if (host.matches(regex)) { + return host; + } else { + try { + return InetAddress.getByName(host).toString().split("/")[1]; + } catch (UnknownHostException e) { + throw new ReaperException(e); + } + } } @NotNull @Override public Map getEndpointToHostId() { - return null; // TODO: implement me. + try { + return apiClient.getEndpointStates().getEntity().stream() + .collect( + Collectors.toMap( + i -> i.get("ENDPOINT_IP"), + i -> i.get("HOST_ID") + ) + ); + } catch (ApiException ae) { + LOG.error("Failed to retrieve endpoint states - does the HTTP proxy have connectivity?", ae); + return Collections.emptyMap(); + } } @Override diff --git a/src/server/src/test/java/io/cassandrareaper/management/http/HttpCassandraManagementProxyTest.java b/src/server/src/test/java/io/cassandrareaper/management/http/HttpCassandraManagementProxyTest.java index def19cf5f..8c8a0b42b 100644 --- a/src/server/src/test/java/io/cassandrareaper/management/http/HttpCassandraManagementProxyTest.java +++ b/src/server/src/test/java/io/cassandrareaper/management/http/HttpCassandraManagementProxyTest.java @@ -19,6 +19,7 @@ import io.cassandrareaper.core.Snapshot; import io.cassandrareaper.core.Table; +import io.cassandrareaper.management.ICassandraManagementProxy; import io.cassandrareaper.management.RepairStatusHandler; import io.cassandrareaper.management.http.models.JobStatusTracker; @@ -361,5 +362,50 @@ public void testGetTokens() throws Exception { new BigInteger("4")); } + @Test + public void getEndpointToHostId() throws Exception { + DefaultApi mockClient = Mockito.mock(DefaultApi.class); + List> mockEntity = new ArrayList<>(); + mockEntity.add(ImmutableMap.of( + "ENDPOINT_IP", "127.0.0.1", + "HOST_ID", "fakehostID1" + ) + ); + mockEntity.add(ImmutableMap.of( + "ENDPOINT_IP", "127.0.0.2", + "HOST_ID", "fakehostID2" + ) + ); + EndpointStates mockEndpointStates = new EndpointStates().entity(mockEntity); + when(mockClient.getEndpointStates()).thenReturn(mockEndpointStates); + mockProxy(mockClient); + assertThat(mockProxy(mockClient).getEndpointToHostId()).containsAllEntriesOf( + ImmutableMap.of( + "127.0.0.1", "fakehostID1", + "127.0.0.1", "fakehostID2" + ) + ); + } + + @Test + public void testGetLocalEndpoint() throws Exception { + DefaultApi mockClient = Mockito.mock(DefaultApi.class); + ScheduledExecutorService executorService = mock(ScheduledExecutorService.class); + when(executorService.submit(any(Callable.class))).thenAnswer(i -> { + Callable callable = i.getArgument(0); + callable.call(); + return ConcurrentUtils.constantFuture(null); + }); + + ICassandraManagementProxy mockProxyIp = new HttpCassandraManagementProxy( + null, "/", InetSocketAddress.createUnresolved("192.168.1.1", 8080), executorService, mockClient); + assertThat(mockProxyIp.getLocalEndpoint()).isEqualTo("192.168.1.1"); + + ICassandraManagementProxy mockProxyDns = new HttpCassandraManagementProxy( + null, "/", InetSocketAddress.createUnresolved("localhost", 8080), executorService, mockClient); + assertThat(mockProxyDns.getLocalEndpoint()).isEqualTo("127.0.0.1"); + + + } } \ No newline at end of file