remove k8s NAT method (#8289)

* remove k8s NAT method

Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com>

* changelog - update pr number in changelog

Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com>

---------

Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com>
This commit is contained in:
Sally MacFarlane
2025-02-14 09:53:40 +10:00
committed by GitHub
parent 46e74d1949
commit 6791c5fea6
17 changed files with 3 additions and 823 deletions

View File

@@ -2,10 +2,10 @@
## Unreleased
### Breaking Changes
- k8s (KUBERNETES) Nat method is removed. Use docker or none instead. [#8289](https://github.com/hyperledger/besu/pull/8289)
### Upcoming Breaking Changes
- `MetricSystem::createLabelledGauge` is deprecated and will be removed in a future release, replace it with `MetricSystem::createLabelledSuppliedGauge`
- k8s (KUBERNETES) Nat method is now deprecated and will be removed in a future release. Use docker or none instead.
- `--Xsnapsync-synchronizer-flat-db-healing-enabled` is deprecated, use `--Xbonsai-full-flat-db-enabled` instead.
- `--Xbonsai-limit-trie-logs-enabled` is deprecated, use `--bonsai-limit-trie-logs-enabled` instead.
- `--Xbonsai-trie-log-pruning-enabled` is deprecated, use `--bonsai-limit-trie-logs-enabled` instead.

View File

@@ -118,8 +118,6 @@ import org.hyperledger.besu.nat.NatService;
import org.hyperledger.besu.nat.core.NatManager;
import org.hyperledger.besu.nat.docker.DockerDetector;
import org.hyperledger.besu.nat.docker.DockerNatManager;
import org.hyperledger.besu.nat.kubernetes.KubernetesDetector;
import org.hyperledger.besu.nat.kubernetes.KubernetesNatManager;
import org.hyperledger.besu.nat.upnp.UpnpNatManager;
import org.hyperledger.besu.plugin.BesuPlugin;
import org.hyperledger.besu.plugin.data.EnodeURL;
@@ -170,7 +168,6 @@ public class RunnerBuilder {
private String p2pListenInterface = NetworkUtility.INADDR_ANY;
private int p2pListenPort;
private NatMethod natMethod = NatMethod.AUTO;
private String natManagerServiceName;
private boolean natMethodFallbackEnabled;
private EthNetworkConfig ethNetworkConfig;
private EthstatsOptions ethstatsOptions;
@@ -313,17 +310,6 @@ public class RunnerBuilder {
return this;
}
/**
* Add Nat manager service name.
*
* @param natManagerServiceName the nat manager service name
* @return the runner builder
*/
public RunnerBuilder natManagerServiceName(final String natManagerServiceName) {
this.natManagerServiceName = natManagerServiceName;
return this;
}
/**
* Enable Nat method fallback.
*
@@ -1213,15 +1199,13 @@ public class RunnerBuilder {
final NatMethod detectedNatMethod =
Optional.of(natMethod)
.filter(not(isEqual(NatMethod.AUTO)))
.orElse(NatService.autoDetectNatMethod(new KubernetesDetector(), new DockerDetector()));
.orElse(NatService.autoDetectNatMethod(new DockerDetector()));
switch (detectedNatMethod) {
case UPNP:
return Optional.of(new UpnpNatManager());
case DOCKER:
return Optional.of(
new DockerNatManager(p2pAdvertisedHost, p2pListenPort, jsonRpcConfiguration.getPort()));
case KUBERNETES:
return Optional.of(new KubernetesNatManager(natManagerServiceName));
case NONE:
default:
return Optional.empty();

View File

@@ -26,7 +26,6 @@ import static org.hyperledger.besu.cli.util.CommandLineUtils.DEPENDENCY_WARNING_
import static org.hyperledger.besu.cli.util.CommandLineUtils.isOptionSet;
import static org.hyperledger.besu.controller.BesuController.DATABASE_PATH;
import static org.hyperledger.besu.ethereum.api.jsonrpc.authentication.EngineAuthService.EPHEMERAL_JWT_FILE;
import static org.hyperledger.besu.nat.kubernetes.KubernetesNatManager.DEFAULT_BESU_SERVICE_NAME_FILTER;
import org.hyperledger.besu.BesuInfo;
import org.hyperledger.besu.Runner;
@@ -1509,22 +1508,6 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
@SuppressWarnings("ConstantConditions")
private void validateNatParams() {
if (natMethod.equals(NatMethod.KUBERNETES)) {
logger.warn("Kubernetes NAT method is deprecated. Please use Docker or UPNP");
}
if (!unstableNatOptions.getNatManagerServiceName().equals(DEFAULT_BESU_SERVICE_NAME_FILTER)) {
logger.warn(
"`--Xnat-kube-service-name` and Kubernetes NAT method are deprecated. Please use Docker or UPNP");
}
if (!(natMethod.equals(NatMethod.AUTO) || natMethod.equals(NatMethod.KUBERNETES))
&& !unstableNatOptions
.getNatManagerServiceName()
.equals(DEFAULT_BESU_SERVICE_NAME_FILTER)) {
throw new ParameterException(
this.commandLine,
"The `--Xnat-kube-service-name` parameter is only used in kubernetes mode. Either remove --Xnat-kube-service-name"
+ " or select the KUBERNETES mode (via --nat--method=KUBERNETES)");
}
if (natMethod.equals(NatMethod.AUTO) && !unstableNatOptions.getNatMethodFallbackEnabled()) {
throw new ParameterException(
this.commandLine,
@@ -2264,7 +2247,6 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
.besuController(controller)
.p2pEnabled(p2pEnabled)
.natMethod(natMethod)
.natManagerServiceName(unstableNatOptions.getNatManagerServiceName())
.natMethodFallbackEnabled(unstableNatOptions.getNatMethodFallbackEnabled())
.discoveryEnabled(peerDiscoveryEnabled)
.ethNetworkConfig(ethNetworkConfig)

View File

@@ -14,21 +14,11 @@
*/
package org.hyperledger.besu.cli.options;
import static org.hyperledger.besu.nat.kubernetes.KubernetesNatManager.DEFAULT_BESU_SERVICE_NAME_FILTER;
import picocli.CommandLine;
/** The Nat Cli options. */
public class NatOptions {
@SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"}) // PicoCLI requires non-final Strings.
@CommandLine.Option(
hidden = true,
names = {"--Xnat-kube-service-name"},
description =
"Specify the name of the service that will be used by the nat manager in Kubernetes. (default: ${DEFAULT-VALUE})")
private String natManagerServiceName = DEFAULT_BESU_SERVICE_NAME_FILTER;
@CommandLine.Option(
hidden = true,
names = {"--Xnat-method-fallback-enabled"},
@@ -49,15 +39,6 @@ public class NatOptions {
return new NatOptions();
}
/**
* Gets nat manager service name.
*
* @return the nat manager service name
*/
public String getNatManagerServiceName() {
return natManagerServiceName;
}
/**
* Whether nat method fallback is enabled.
*

View File

@@ -327,7 +327,6 @@ public abstract class CommandTestAbstract {
when(mockRunnerBuilder.permissioningConfiguration(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.p2pEnabled(anyBoolean())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.natMethod(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.natManagerServiceName(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.natMethodFallbackEnabled(anyBoolean())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.jsonRpcConfiguration(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.engineJsonRpcConfiguration(any())).thenReturn(mockRunnerBuilder);

View File

@@ -16,7 +16,6 @@ package org.hyperledger.besu.cli;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.nat.kubernetes.KubernetesNatManager.DEFAULT_BESU_SERVICE_NAME_FILTER;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
@@ -47,16 +46,6 @@ public class NatOptionsTest extends CommandTestAbstract {
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test
public void natManagerPodNamePropertyDefaultIsBesu() {
parseCommand();
verify(mockRunnerBuilder).natManagerServiceName(eq(DEFAULT_BESU_SERVICE_NAME_FILTER));
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test
public void natMethodOptionIsParsedCorrectly() {
@@ -75,52 +64,6 @@ public class NatOptionsTest extends CommandTestAbstract {
parseCommand("--nat-method", "DOCKER");
verify(mockRunnerBuilder).natMethod(eq(NatMethod.DOCKER));
parseCommand("--nat-method", "KUBERNETES");
verify(mockRunnerBuilder).natMethod(eq(NatMethod.KUBERNETES));
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test
public void natManagerPodNamePropertyIsCorrectlyUpdated() {
final String podName = "besu-updated";
parseCommand("--Xnat-kube-service-name", podName);
verify(mockRunnerBuilder).natManagerServiceName(eq(podName));
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test
public void natManagerPodNameCannotBeUsedWithNatDockerMethod() {
parseCommand("--nat-method", "DOCKER", "--Xnat-kube-service-name", "besu-updated");
Mockito.verifyNoInteractions(mockRunnerBuilder);
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8))
.contains(
"The `--Xnat-kube-service-name` parameter is only used in kubernetes mode. Either remove --Xnat-kube-service-name or select the KUBERNETES mode (via --nat--method=KUBERNETES)");
}
@Test
public void natManagerPodNameCannotBeUsedWithNatNoneMethod() {
parseCommand("--nat-method", "NONE", "--Xnat-kube-service-name", "besu-updated");
Mockito.verifyNoInteractions(mockRunnerBuilder);
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8))
.contains(
"The `--Xnat-kube-service-name` parameter is only used in kubernetes mode. Either remove --Xnat-kube-service-name or select the KUBERNETES mode (via --nat--method=KUBERNETES)");
}
@Test
public void natMethodFallbackEnabledPropertyIsCorrectlyUpdatedWithKubernetes() {
parseCommand("--nat-method", "KUBERNETES", "--Xnat-method-fallback-enabled", "false");
verify(mockRunnerBuilder).natMethodFallbackEnabled(eq(false));
parseCommand("--nat-method", "KUBERNETES", "--Xnat-method-fallback-enabled", "true");
verify(mockRunnerBuilder).natMethodFallbackEnabled(eq(true));
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@@ -167,6 +110,6 @@ public class NatOptionsTest extends CommandTestAbstract {
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8))
.contains(
"Invalid value for option '--nat-method': expected one of [UPNP, UPNPP2PONLY, DOCKER, KUBERNETES, AUTO, NONE] (case-insensitive) but was 'invalid'");
"Invalid value for option '--nat-method': expected one of [UPNP, UPNPP2PONLY, DOCKER, AUTO, NONE] (case-insensitive) but was 'invalid'");
}
}

View File

@@ -26,7 +26,6 @@ security-module="localfile"
identity="PegaSysEng"
p2p-enabled=true
nat-method="NONE"
Xnat-kube-service-name="besu"
Xnat-method-fallback-enabled=true
discovery-enabled=false
poa-discovery-retry-bootnodes=true

View File

@@ -35,7 +35,6 @@ dependencies {
implementation 'com.squareup.okhttp3:okhttp'
implementation 'org.jupnp:org.jupnp'
implementation 'org.jupnp:org.jupnp.support'
implementation 'io.kubernetes:client-java'
// test dependencies.
testImplementation project(path: ':ethereum:core', configuration: 'testArtifacts')

View File

@@ -22,8 +22,6 @@ public enum NatMethod {
UPNPP2PONLY,
/** Docker nat method. */
DOCKER,
/** Kubernetes nat method. */
KUBERNETES,
/** Auto nat method. */
AUTO,
/** None nat method. */

View File

@@ -1,48 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.nat.kubernetes;
import org.hyperledger.besu.nat.NatMethod;
import org.hyperledger.besu.nat.core.NatMethodDetector;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;
/** The Kubernetes detector. */
public class KubernetesDetector implements NatMethodDetector {
// When a Pod runs on a Node, the kubelet adds a set of environment variables for each active
// Service.
// https://kubernetes.io/docs/concepts/services-networking/connect-applications-service/#environment-variables
private static final Optional<String> KUBERNETES_SERVICE_HOST =
Optional.ofNullable(System.getenv("KUBERNETES_SERVICE_HOST"));
private static final Path KUBERNETES_WATERMARK_FILE = Paths.get("var/run/secrets/kubernetes.io");
/** Default constructor */
public KubernetesDetector() {}
@Override
public Optional<NatMethod> detect() {
return KUBERNETES_SERVICE_HOST
.map(__ -> NatMethod.KUBERNETES)
.or(
() ->
Files.exists(KUBERNETES_WATERMARK_FILE)
? Optional.of(NatMethod.KUBERNETES)
: Optional.empty());
}
}

View File

@@ -1,174 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.nat.kubernetes;
import org.hyperledger.besu.nat.NatMethod;
import org.hyperledger.besu.nat.core.AbstractNatManager;
import org.hyperledger.besu.nat.core.IpDetector;
import org.hyperledger.besu.nat.core.domain.NatPortMapping;
import org.hyperledger.besu.nat.core.domain.NatServiceType;
import org.hyperledger.besu.nat.core.domain.NetworkProtocol;
import org.hyperledger.besu.nat.core.exception.NatInitializationException;
import org.hyperledger.besu.nat.kubernetes.service.KubernetesServiceType;
import org.hyperledger.besu.nat.kubernetes.service.LoadBalancerBasedDetector;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import com.google.common.annotations.VisibleForTesting;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.Configuration;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.openapi.models.V1Service;
import io.kubernetes.client.util.ClientBuilder;
import io.kubernetes.client.util.KubeConfig;
import io.kubernetes.client.util.authenticators.GCPAuthenticator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class describes the behavior of the Kubernetes NAT manager. Kubernetes Nat manager add
* support for Kubernetess NAT implementation when Besu is being run from a Kubernetes cluster
*/
public class KubernetesNatManager extends AbstractNatManager {
private static final Logger LOG = LoggerFactory.getLogger(KubernetesNatManager.class);
/** The constant DEFAULT_BESU_SERVICE_NAME_FILTER. */
public static final String DEFAULT_BESU_SERVICE_NAME_FILTER = "besu";
private String internalAdvertisedHost;
private final String besuServiceNameFilter;
private final List<NatPortMapping> forwardedPorts = new ArrayList<>();
/**
* Instantiates a new Kubernetes nat manager.
*
* @param besuServiceNameFilter the besu service name filter
*/
public KubernetesNatManager(final String besuServiceNameFilter) {
super(NatMethod.KUBERNETES);
this.besuServiceNameFilter = besuServiceNameFilter;
}
@Override
protected void doStart() throws NatInitializationException {
LOG.info("Starting kubernetes NAT manager.");
try {
KubeConfig.registerAuthenticator(new GCPAuthenticator());
LOG.debug("Trying to update information using Kubernetes client SDK.");
final ApiClient client = ClientBuilder.cluster().build();
// set the global default api-client to the in-cluster one from above
Configuration.setDefaultApiClient(client);
// the CoreV1Api loads default api-client from global configuration.
final CoreV1Api api = new CoreV1Api();
// invokes the CoreV1Api client
final V1Service service =
api
.listServiceForAllNamespaces(
null, null, null, null, null, null, null, null, null, null, null)
.getItems()
.stream()
.filter(
v1Service -> v1Service.getMetadata().getName().contains(besuServiceNameFilter))
.findFirst()
.orElseThrow(() -> new NatInitializationException("Service not found"));
updateUsingBesuService(service);
} catch (Exception e) {
throw new NatInitializationException(e.getMessage(), e);
}
}
/**
* Update using besu service. Visible for testing.
*
* @param service the service
* @throws RuntimeException the runtime exception
*/
@VisibleForTesting
void updateUsingBesuService(final V1Service service) throws RuntimeException {
try {
LOG.info("Found Besu service: {}", service.getMetadata().getName());
internalAdvertisedHost =
getIpDetector(service)
.detectAdvertisedIp()
.orElseThrow(
() -> new NatInitializationException("Unable to retrieve IP from service"));
LOG.info("Setting host IP to: {}.", internalAdvertisedHost);
final String internalHost = queryLocalIPAddress().get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
service
.getSpec()
.getPorts()
.forEach(
v1ServicePort -> {
try {
final NatServiceType natServiceType =
NatServiceType.fromString(v1ServicePort.getName());
forwardedPorts.add(
new NatPortMapping(
natServiceType,
natServiceType.equals(NatServiceType.DISCOVERY)
? NetworkProtocol.UDP
: NetworkProtocol.TCP,
internalHost,
internalAdvertisedHost,
v1ServicePort.getPort(),
v1ServicePort.getTargetPort().getIntValue()));
} catch (IllegalStateException e) {
LOG.warn("Ignored unknown Besu port: {}", e.getMessage());
}
});
} catch (Exception e) {
throw new RuntimeException(
"Failed update information using pod metadata : " + e.getMessage(), e);
}
}
@Override
protected void doStop() {
LOG.info("Stopping kubernetes NAT manager.");
}
@Override
protected CompletableFuture<String> retrieveExternalIPAddress() {
return CompletableFuture.completedFuture(internalAdvertisedHost);
}
@Override
public CompletableFuture<List<NatPortMapping>> getPortMappings() {
return CompletableFuture.completedFuture(forwardedPorts);
}
private IpDetector getIpDetector(final V1Service v1Service) throws NatInitializationException {
final String serviceType = v1Service.getSpec().getType();
switch (KubernetesServiceType.fromName(serviceType)) {
case CLUSTER_IP:
return () -> Optional.ofNullable(v1Service.getSpec().getClusterIP());
case LOAD_BALANCER:
return new LoadBalancerBasedDetector(v1Service);
default:
throw new NatInitializationException(String.format("%s is not implemented", serviceType));
}
}
}

View File

@@ -1,47 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.nat.kubernetes.service;
/** The enum Kubernetes service type. */
public enum KubernetesServiceType {
/** Cluster ip kubernetes service type. */
CLUSTER_IP("ClusterIP"),
/** Load balancer kubernetes service type. */
LOAD_BALANCER("LoadBalancer"),
/** Unknown kubernetes service type. */
UNKNOWN("");
/** The Name. */
String name;
KubernetesServiceType(final String name) {
this.name = name;
}
/**
* Map KubernetesServiceType from String value.
*
* @param name the name
* @return the kubernetes service type
*/
public static KubernetesServiceType fromName(final String name) {
for (KubernetesServiceType value : values()) {
if (value.name.equals(name)) {
return value;
}
}
return UNKNOWN;
}
}

View File

@@ -1,57 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.nat.kubernetes.service;
import org.hyperledger.besu.nat.core.IpDetector;
import org.hyperledger.besu.nat.core.exception.NatInitializationException;
import java.net.InetAddress;
import java.util.Optional;
import io.kubernetes.client.openapi.models.V1LoadBalancerIngress;
import io.kubernetes.client.openapi.models.V1Service;
/** The Load balancer based detector. */
public class LoadBalancerBasedDetector implements IpDetector {
private final V1Service v1Service;
/**
* Instantiates a new Load balancer based detector.
*
* @param v1Service the v 1 service
*/
public LoadBalancerBasedDetector(final V1Service v1Service) {
this.v1Service = v1Service;
}
@Override
public Optional<String> detectAdvertisedIp() throws Exception {
final V1LoadBalancerIngress v1LoadBalancerIngress =
v1Service.getStatus().getLoadBalancer().getIngress().stream()
.filter(
v1LoadBalancerIngress1 ->
v1LoadBalancerIngress1.getHostname() != null
|| v1LoadBalancerIngress1.getIp() != null)
.findFirst()
.orElseThrow(() -> new NatInitializationException("Ingress not found"));
if (v1LoadBalancerIngress.getHostname() != null) {
return Optional.ofNullable(
InetAddress.getByName(v1LoadBalancerIngress.getHostname()).getHostAddress());
} else {
return Optional.ofNullable(v1LoadBalancerIngress.getIp());
}
}
}

View File

@@ -1,157 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.nat.kubernetes;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.nat.kubernetes.KubernetesNatManager.DEFAULT_BESU_SERVICE_NAME_FILTER;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.nat.core.domain.NatPortMapping;
import org.hyperledger.besu.nat.core.domain.NatServiceType;
import org.hyperledger.besu.nat.core.domain.NetworkProtocol;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.concurrent.ExecutionException;
import io.kubernetes.client.custom.IntOrString;
import io.kubernetes.client.openapi.models.V1ObjectMeta;
import io.kubernetes.client.openapi.models.V1Service;
import io.kubernetes.client.openapi.models.V1ServicePort;
import io.kubernetes.client.openapi.models.V1ServiceSpec;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public final class KubernetesClusterIpNatManagerTest {
private final String detectedAdvertisedHost = "199.45.69.12";
private final int p2pPort = 1;
private final int rpcHttpPort = 2;
@Mock private V1Service v1Service;
private KubernetesNatManager natManager;
@BeforeEach
public void initialize() throws IOException {
when(v1Service.getSpec())
.thenReturn(
new V1ServiceSpec()
.type("ClusterIP")
.clusterIP(detectedAdvertisedHost)
.ports(
Arrays.asList(
new V1ServicePort()
.name(NatServiceType.JSON_RPC.getValue())
.port(rpcHttpPort)
.targetPort(new IntOrString(rpcHttpPort)),
new V1ServicePort()
.name(NatServiceType.RLPX.getValue())
.port(p2pPort)
.targetPort(new IntOrString(p2pPort)),
new V1ServicePort()
.name(NatServiceType.DISCOVERY.getValue())
.port(p2pPort)
.targetPort(new IntOrString(p2pPort)))));
when(v1Service.getMetadata())
.thenReturn(new V1ObjectMeta().name(DEFAULT_BESU_SERVICE_NAME_FILTER));
natManager = new KubernetesNatManager(DEFAULT_BESU_SERVICE_NAME_FILTER);
try {
natManager.start();
} catch (Exception ignored) {
System.err.println("Ignored missing Kube config file in testing context.");
}
natManager.updateUsingBesuService(v1Service);
}
@Test
public void assertThatExternalIPIsEqualToRemoteHost()
throws ExecutionException, InterruptedException {
assertThat(natManager.queryExternalIPAddress().get()).isEqualTo(detectedAdvertisedHost);
}
@Test
public void assertThatLocalIPIsEqualToLocalHost()
throws ExecutionException, InterruptedException, UnknownHostException {
final String internalHost = InetAddress.getLocalHost().getHostAddress();
assertThat(natManager.queryLocalIPAddress().get()).isEqualTo(internalHost);
}
@Test
public void assertThatMappingForDiscoveryWorks() throws UnknownHostException {
final String internalHost = InetAddress.getLocalHost().getHostAddress();
final NatPortMapping mapping =
natManager.getPortMapping(NatServiceType.DISCOVERY, NetworkProtocol.UDP);
final NatPortMapping expectedMapping =
new NatPortMapping(
NatServiceType.DISCOVERY,
NetworkProtocol.UDP,
internalHost,
detectedAdvertisedHost,
p2pPort,
p2pPort);
assertThat(mapping).usingRecursiveComparison().isEqualTo(expectedMapping);
}
@Test
public void assertThatMappingForJsonRpcWorks() throws UnknownHostException {
final String internalHost = InetAddress.getLocalHost().getHostAddress();
final NatPortMapping mapping =
natManager.getPortMapping(NatServiceType.JSON_RPC, NetworkProtocol.TCP);
final NatPortMapping expectedMapping =
new NatPortMapping(
NatServiceType.JSON_RPC,
NetworkProtocol.TCP,
internalHost,
detectedAdvertisedHost,
rpcHttpPort,
rpcHttpPort);
assertThat(mapping).usingRecursiveComparison().isEqualTo(expectedMapping);
}
@Test
public void assertThatMappingForRlpxWorks() throws UnknownHostException {
final String internalHost = InetAddress.getLocalHost().getHostAddress();
final NatPortMapping mapping =
natManager.getPortMapping(NatServiceType.RLPX, NetworkProtocol.TCP);
final NatPortMapping expectedMapping =
new NatPortMapping(
NatServiceType.RLPX,
NetworkProtocol.TCP,
internalHost,
detectedAdvertisedHost,
p2pPort,
p2pPort);
assertThat(mapping).usingRecursiveComparison().isEqualTo(expectedMapping);
}
}

View File

@@ -1,164 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.nat.kubernetes;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.nat.kubernetes.KubernetesNatManager.DEFAULT_BESU_SERVICE_NAME_FILTER;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.nat.core.domain.NatPortMapping;
import org.hyperledger.besu.nat.core.domain.NatServiceType;
import org.hyperledger.besu.nat.core.domain.NetworkProtocol;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.concurrent.ExecutionException;
import io.kubernetes.client.custom.IntOrString;
import io.kubernetes.client.openapi.models.V1LoadBalancerIngress;
import io.kubernetes.client.openapi.models.V1LoadBalancerStatus;
import io.kubernetes.client.openapi.models.V1ObjectMeta;
import io.kubernetes.client.openapi.models.V1Service;
import io.kubernetes.client.openapi.models.V1ServicePort;
import io.kubernetes.client.openapi.models.V1ServiceSpec;
import io.kubernetes.client.openapi.models.V1ServiceStatus;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public final class KubernetesLoadManagerNatManagerTest {
private final String detectedAdvertisedHost = "199.45.69.12";
private final int p2pPort = 1;
private final int rpcHttpPort = 2;
@Mock private V1Service v1Service;
private KubernetesNatManager natManager;
@BeforeEach
public void initialize() throws IOException {
final V1ServiceStatus v1ServiceStatus =
new V1ServiceStatus()
.loadBalancer(
new V1LoadBalancerStatus()
.addIngressItem(new V1LoadBalancerIngress().ip(detectedAdvertisedHost)));
when(v1Service.getStatus()).thenReturn(v1ServiceStatus);
when(v1Service.getSpec())
.thenReturn(
new V1ServiceSpec()
.type("LoadBalancer")
.ports(
Arrays.asList(
new V1ServicePort()
.name(NatServiceType.JSON_RPC.getValue())
.port(rpcHttpPort)
.targetPort(new IntOrString(rpcHttpPort)),
new V1ServicePort()
.name(NatServiceType.RLPX.getValue())
.port(p2pPort)
.targetPort(new IntOrString(p2pPort)),
new V1ServicePort()
.name(NatServiceType.DISCOVERY.getValue())
.port(p2pPort)
.targetPort(new IntOrString(p2pPort)))));
when(v1Service.getMetadata())
.thenReturn(new V1ObjectMeta().name(DEFAULT_BESU_SERVICE_NAME_FILTER));
natManager = new KubernetesNatManager(DEFAULT_BESU_SERVICE_NAME_FILTER);
try {
natManager.start();
} catch (Exception ignored) {
System.err.println("Ignored missing Kube config file in testing context.");
}
natManager.updateUsingBesuService(v1Service);
}
@Test
public void assertThatExternalIPIsEqualToRemoteHost()
throws ExecutionException, InterruptedException {
assertThat(natManager.queryExternalIPAddress().get()).isEqualTo(detectedAdvertisedHost);
}
@Test
public void assertThatLocalIPIsEqualToLocalHost()
throws ExecutionException, InterruptedException, UnknownHostException {
final String internalHost = InetAddress.getLocalHost().getHostAddress();
assertThat(natManager.queryLocalIPAddress().get()).isEqualTo(internalHost);
}
@Test
public void assertThatMappingForDiscoveryWorks() throws UnknownHostException {
final String internalHost = InetAddress.getLocalHost().getHostAddress();
final NatPortMapping mapping =
natManager.getPortMapping(NatServiceType.DISCOVERY, NetworkProtocol.UDP);
final NatPortMapping expectedMapping =
new NatPortMapping(
NatServiceType.DISCOVERY,
NetworkProtocol.UDP,
internalHost,
detectedAdvertisedHost,
p2pPort,
p2pPort);
assertThat(mapping).usingRecursiveComparison().isEqualTo(expectedMapping);
}
@Test
public void assertThatMappingForJsonRpcWorks() throws UnknownHostException {
final String internalHost = InetAddress.getLocalHost().getHostAddress();
final NatPortMapping mapping =
natManager.getPortMapping(NatServiceType.JSON_RPC, NetworkProtocol.TCP);
final NatPortMapping expectedMapping =
new NatPortMapping(
NatServiceType.JSON_RPC,
NetworkProtocol.TCP,
internalHost,
detectedAdvertisedHost,
rpcHttpPort,
rpcHttpPort);
assertThat(mapping).usingRecursiveComparison().isEqualTo(expectedMapping);
}
@Test
public void assertThatMappingForRlpxWorks() throws UnknownHostException {
final String internalHost = InetAddress.getLocalHost().getHostAddress();
final NatPortMapping mapping =
natManager.getPortMapping(NatServiceType.RLPX, NetworkProtocol.TCP);
final NatPortMapping expectedMapping =
new NatPortMapping(
NatServiceType.RLPX,
NetworkProtocol.TCP,
internalHost,
detectedAdvertisedHost,
p2pPort,
p2pPort);
assertThat(mapping).usingRecursiveComparison().isEqualTo(expectedMapping);
}
}

View File

@@ -1,56 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.nat.kubernetes;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.hyperledger.besu.nat.kubernetes.KubernetesNatManager.DEFAULT_BESU_SERVICE_NAME_FILTER;
import static org.mockito.Mockito.when;
import io.kubernetes.client.openapi.models.V1ObjectMeta;
import io.kubernetes.client.openapi.models.V1Service;
import io.kubernetes.client.openapi.models.V1ServiceSpec;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public final class KubernetesUnknownNatManagerTest {
@Mock private V1Service v1Service;
private KubernetesNatManager natManager;
@BeforeEach
public void initialize() {
when(v1Service.getSpec()).thenReturn(new V1ServiceSpec().type("Unknown"));
when(v1Service.getMetadata())
.thenReturn(new V1ObjectMeta().name(DEFAULT_BESU_SERVICE_NAME_FILTER));
natManager = new KubernetesNatManager(DEFAULT_BESU_SERVICE_NAME_FILTER);
try {
natManager.start();
} catch (Exception ignored) {
System.err.println("Ignored missing Kube config file in testing context.");
}
}
@Test
public void assertThatNatExceptionIsThrownWithUnknownServiceType() {
assertThatThrownBy(() -> natManager.updateUsingBesuService(v1Service))
.isInstanceOf(RuntimeException.class);
}
}

View File

@@ -96,8 +96,6 @@ dependencies {
api 'info.picocli:picocli:4.7.6'
api 'info.picocli:picocli-codegen:4.7.6'
api 'io.kubernetes:client-java:21.0.1-legacy'
api 'io.opentelemetry.instrumentation:opentelemetry-okhttp-3.0:2.9.0-alpha'
api 'io.opentelemetry.proto:opentelemetry-proto:1.3.2-alpha'
api 'io.opentelemetry.semconv:opentelemetry-semconv:1.28.0-alpha'