Merge branch 'stable-3.6' into stable-3.7
* stable-3.6:
Remove `@NoHttpd` from IT tests that interact with Gerrit Rest API
Change-Id: Ia25c8da867a3e68251e2f1b7cef6fd7efad3a4d3
diff --git a/BUILD b/BUILD
index 7f32bc1..80d202b 100644
--- a/BUILD
+++ b/BUILD
@@ -21,6 +21,7 @@
resources = glob(["src/main/resources/**/*"]),
deps = [
"@jgroups//jar",
+ "@jgroups-kubernetes//jar",
"@global-refdb//jar:neverlink",
],
)
diff --git a/external_plugin_deps.bzl b/external_plugin_deps.bzl
index eaa632e..09f34de 100644
--- a/external_plugin_deps.bzl
+++ b/external_plugin_deps.bzl
@@ -9,12 +9,18 @@
maven_jar(
name = "jgroups",
- artifact = "org.jgroups:jgroups:3.6.15.Final",
- sha1 = "755afcfc6c8a8ea1e15ef0073417c0b6e8c6d6e4",
+ artifact = "org.jgroups:jgroups:5.2.16.Final",
+ sha1 = "d2dceef4c6917239350f2a604b4116745a1e84ae",
+ )
+
+ maven_jar(
+ name = "jgroups-kubernetes",
+ artifact = "org.jgroups.kubernetes:jgroups-kubernetes:2.0.1.Final",
+ sha1 = "4e259af98c3b1fbdc8ebaebe42496ef560dfc30f",
)
maven_jar(
name = "global-refdb",
- artifact = "com.gerritforge:global-refdb:3.6.3.1",
- sha1 = "0f5229856d6a17e9c2382c8a404f43851f1f0287",
+ artifact = "com.gerritforge:global-refdb:3.7.4",
+ sha1 = "a5f3fcdbc04b7e98c52ecd50d2a56424e60b0575",
)
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/Configuration.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/Configuration.java
index e0d5a50..fd5f2cb 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/Configuration.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/Configuration.java
@@ -58,6 +58,9 @@
// common parameter to peerInfo section
static final String PEER_INFO_SECTION = "peerInfo";
+ // common parameter to jgroups section
+ static final String JGROUPS_SECTION = "jgroups";
+
// common parameters to cache and index sections
static final String THREAD_POOL_SIZE_KEY = "threadPoolSize";
static final String BATCH_THREAD_POOL_SIZE_KEY = "batchThreadPoolSize";
@@ -70,6 +73,7 @@
private final AutoReindex autoReindex;
private final PeerInfo peerInfo;
private final JGroups jgroups;
+ private final JGroupsKubernetes jgroupsKubernetes;
private final Http http;
private final Cache cache;
private final Event event;
@@ -106,6 +110,7 @@
throw new IllegalArgumentException("Not supported strategy: " + peerInfo.strategy);
}
jgroups = new JGroups(site, cfg);
+ jgroupsKubernetes = new JGroupsKubernetes(cfg);
http = new Http(cfg);
cache = new Cache(cfg);
event = new Event(cfg);
@@ -152,6 +157,10 @@
return jgroups;
}
+ public JGroupsKubernetes jgroupsKubernetes() {
+ return jgroupsKubernetes;
+ }
+
public Http http() {
return http;
}
@@ -316,9 +325,9 @@
}
public static class JGroups {
- static final String JGROUPS_SECTION = "jgroups";
static final String SKIP_INTERFACE_KEY = "skipInterface";
static final String CLUSTER_NAME_KEY = "clusterName";
+ static final String KUBERNETES_KEY = "kubernetes";
static final String PROTOCOL_STACK_KEY = "protocolStack";
static final ImmutableList<String> DEFAULT_SKIP_INTERFACE_LIST =
ImmutableList.of("lo*", "utun*", "awdl*");
@@ -326,6 +335,7 @@
private final ImmutableList<String> skipInterface;
private final String clusterName;
+ private final boolean useKubernetes;
private final Optional<Path> protocolStack;
private JGroups(SitePaths site, Config cfg) {
@@ -334,6 +344,7 @@
log.atFine().log("Skip interface(s): %s", skipInterface);
clusterName = getString(cfg, JGROUPS_SECTION, null, CLUSTER_NAME_KEY, DEFAULT_CLUSTER_NAME);
log.atFine().log("Cluster name: %s", clusterName);
+ useKubernetes = cfg.getBoolean(JGROUPS_SECTION, KUBERNETES_KEY, false);
protocolStack = getProtocolStack(cfg, site);
log.atFine().log(
"Protocol stack config %s",
@@ -362,6 +373,32 @@
public String clusterName() {
return clusterName;
}
+
+ public boolean useKubernetes() {
+ return useKubernetes;
+ }
+ }
+
+ public static class JGroupsKubernetes {
+ static final String KUBERNETES_SUBSECTION = "kubernetes";
+ static final String NAMESPACE_KEY = "namespace";
+ static final String LABEL_KEY = "label";
+
+ private final String namespace;
+ private final List<String> labels;
+
+ public JGroupsKubernetes(Config cfg) {
+ namespace = cfg.getString(JGROUPS_SECTION, KUBERNETES_SUBSECTION, NAMESPACE_KEY);
+ labels = Arrays.asList(cfg.getStringList(JGROUPS_SECTION, KUBERNETES_SUBSECTION, LABEL_KEY));
+ }
+
+ public String namespace() {
+ return namespace;
+ }
+
+ public List<String> labels() {
+ return labels;
+ }
}
public static class Http {
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/EnvModule.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/EnvModule.java
new file mode 100644
index 0000000..d503f09
--- /dev/null
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/EnvModule.java
@@ -0,0 +1,36 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// 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.ericsson.gerrit.plugins.highavailability;
+
+import com.google.common.base.CharMatcher;
+import com.google.gerrit.common.Nullable;
+import com.google.inject.AbstractModule;
+import com.google.inject.name.Names;
+
+public class EnvModule extends AbstractModule {
+ public static final String MY_URL_ENV_VAR = "GERRIT_URL";
+
+ @Override
+ protected void configure() {
+ bind(String.class)
+ .annotatedWith(Names.named(MY_URL_ENV_VAR))
+ .toInstance(urlWithTrimmedTrailingSlash(System.getenv(MY_URL_ENV_VAR)));
+ }
+
+ @Nullable
+ private static String urlWithTrimmedTrailingSlash(@Nullable String in) {
+ return in == null ? "" : CharMatcher.is('/').trimTrailingFrom(in);
+ }
+}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/Module.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/Module.java
index ddc294b..688e567 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/Module.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/Module.java
@@ -39,6 +39,7 @@
@Override
protected void configure() {
+ install(new EnvModule());
install(new ForwarderModule());
install(new RestForwarderModule());
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JGroupsPeerInfoProvider.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JGroupsPeerInfoProvider.java
index 0e3fdc0..68efbec 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JGroupsPeerInfoProvider.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JGroupsPeerInfoProvider.java
@@ -30,7 +30,8 @@
import org.jgroups.Address;
import org.jgroups.JChannel;
import org.jgroups.Message;
-import org.jgroups.ReceiverAdapter;
+import org.jgroups.ObjectMessage;
+import org.jgroups.Receiver;
import org.jgroups.View;
/**
@@ -44,8 +45,8 @@
* cluster.
*/
@Singleton
-public class JGroupsPeerInfoProvider extends ReceiverAdapter
- implements Provider<Set<PeerInfo>>, LifecycleListener {
+public class JGroupsPeerInfoProvider
+ implements Receiver, Provider<Set<PeerInfo>>, LifecycleListener {
private static final FluentLogger log = FluentLogger.forEnclosingClass();
private static final String JGROUPS_LOG_FACTORY_PROPERTY = "jgroups.logging.log_factory_class";
@@ -56,6 +57,7 @@
}
private final Configuration.JGroups jgroupsConfig;
+ private final Configuration.JGroupsKubernetes jgroupsKubernetesConfig;
private final InetAddressFinder finder;
private final String myUrl;
@@ -67,6 +69,7 @@
JGroupsPeerInfoProvider(
Configuration pluginConfiguration, InetAddressFinder finder, MyUrlProvider myUrlProvider) {
this.jgroupsConfig = pluginConfiguration.jgroups();
+ this.jgroupsKubernetesConfig = pluginConfiguration.jgroupsKubernetes();
this.finder = finder;
this.myUrl = myUrlProvider.get();
}
@@ -105,7 +108,7 @@
}
if (view.size() > 1) {
try {
- channel.send(new Message(null, myUrl));
+ channel.send(new ObjectMessage(null, myUrl));
} catch (Exception e) {
// channel communication caused an error. Can't do much about it.
log.atSevere().withCause(e).log(
@@ -149,6 +152,16 @@
if (protocolStack.isPresent()) {
return new JChannel(protocolStack.get().toString());
}
+ if (jgroupsConfig.useKubernetes()) {
+ if (jgroupsKubernetesConfig.namespace() != null) {
+ System.setProperty("KUBERNETES_NAMESPACE", jgroupsKubernetesConfig.namespace());
+ }
+ if (!jgroupsKubernetesConfig.labels().isEmpty()) {
+ System.setProperty(
+ "KUBERNETES_LABELS", String.join(",", jgroupsKubernetesConfig.labels()));
+ }
+ return new JChannel(getClass().getResource("kubernetes.xml").toString());
+ }
return new JChannel();
} catch (Exception e) {
log.atSevere().withCause(e).log(
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/MyUrlProvider.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/MyUrlProvider.java
index a257eb3..d972e08 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/MyUrlProvider.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/MyUrlProvider.java
@@ -14,6 +14,8 @@
package com.ericsson.gerrit.plugins.highavailability.peers.jgroups;
+import static com.ericsson.gerrit.plugins.highavailability.EnvModule.MY_URL_ENV_VAR;
+
import com.ericsson.gerrit.plugins.highavailability.Configuration;
import com.google.common.base.CharMatcher;
import com.google.common.flogger.FluentLogger;
@@ -22,6 +24,7 @@
import com.google.inject.Provider;
import com.google.inject.ProvisionException;
import com.google.inject.Singleton;
+import com.google.inject.name.Named;
import java.net.InetAddress;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
@@ -40,8 +43,12 @@
private final String myUrl;
@Inject
- MyUrlProvider(@GerritServerConfig Config srvConfig, Configuration pluginConfiguration) {
+ MyUrlProvider(
+ @GerritServerConfig Config srvConfig,
+ Configuration pluginConfiguration,
+ @Named(MY_URL_ENV_VAR) String myUrlEnvVar) {
String url = pluginConfiguration.peerInfoJGroups().myUrl();
+ url = url == null ? myUrlEnvVar : url;
if (url == null) {
log.atInfo().log("myUrl not configured; attempting to determine from %s", LISTEN_URL);
try {
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
index d94a91e..2d9c10c 100644
--- a/src/main/resources/Documentation/config.md
+++ b/src/main/resources/Documentation/config.md
@@ -98,7 +98,11 @@
configured, add as many url entries as necessary.
```peerInfo.jgroups.myUrl```
-: The URL of this instance to be broadcast to other peers. If not specified, the
+: The URL of this instance to be broadcast to other peers. Alternatively, this URL
+ can also be specified using the environment variable `GERRIT_URL`. This is useful
+ in environments like Kubernetes, where manual configuration of each Gerrit
+ instance is not possible.
+ If neither the configuration option nor the system property is specified, the
URL is determined from the `httpd.listenUrl` in the `gerrit.config`.
If `httpd.listenUrl` is configured with multiple values, is configured to work
with a reverse proxy (i.e. uses `proxy-http` or `proxy-https` scheme), or is
@@ -127,6 +131,22 @@
its configuration file syntax please refer to JGroups documentation.
See [JGroups - Advanced topics](http://jgroups.org/manual-3.x/html/user-advanced.html).
+```jgroups.kubernetes```
+: If true, a protocol stack optimized for Kubernetes will be used. Peers will be discovered
+ by querying the Kubernetes API server for pods. The functionality is provided by the
+ [jgroups-kubernetes extension](https://github.com/jgroups-extras/jgroups-kubernetes).
+ To enable Gerrit to use the Kubernetes API, the pods require a ServiceAccount with
+ permissions to list pods ([example](https://github.com/jgroups-extras/jgroups-kubernetes#demo)).
+ Further, Gerrit requires a valid TLS certificate in its keystore, since the Kubernetes
+ API server requires TLS. (Default: false)
+
+```jgroups.kubernetes.namespace```
+: The namespace in which to query for pods. (Default: default)
+
+```jgroups.kubernetes.label```
+: A label that will be used to select the pods in the format `label=value`. Can be set
+ multiple times.
+
NOTE: To work properly in certain environments, JGroups needs the System property
`java.net.preferIPv4Stack` to be set to `true`.
See [JGroups - Trouble shooting](http://jgroups.org/tutorial/index.html#_trouble_shooting).
diff --git a/src/main/resources/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/kubernetes.xml b/src/main/resources/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/kubernetes.xml
new file mode 100644
index 0000000..6e89dbc
--- /dev/null
+++ b/src/main/resources/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/kubernetes.xml
@@ -0,0 +1,49 @@
+<!--
+ Based on a configuration written by Bela Ban [1].
+
+ [1] https://koudingspawn.de/jgroups-on-kubernetes/
+-->
+<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns="urn:org:jgroups"
+ xsi:schemaLocation="urn:org:jgroups http://www.jgroups.org/schema/jgroups.xsd"
+>
+ <TCP bind_addr="loopback,match-interface:eth0"
+ bind_port="7800"
+ external_addr="match-interface:eth0"
+ thread_pool.min_threads="0"
+ thread_pool.max_threads="200"
+ thread_pool.keep_alive_time="30000" />
+ <RED />
+
+ <org.jgroups.protocols.kubernetes.KUBE_PING
+ port_range="1"
+ namespace="${KUBERNETES_NAMESPACE:default}"
+ labels="${KUBERNETES_LABELS:app=gerrit}"
+ />
+
+ <MERGE3 max_interval="30000"
+ min_interval="10000" />
+ <FD_SOCK external_addr="${JGROUPS_EXTERNAL_ADDR}"
+ start_port="${FD_SOCK_PORT:9000}" />
+ <FD_ALL timeout="30000" interval="5000" />
+ <VERIFY_SUSPECT timeout="1500" />
+ <BARRIER />
+ <pbcast.NAKACK2 xmit_interval="500"
+ xmit_table_num_rows="100"
+ xmit_table_msgs_per_row="2000"
+ xmit_table_max_compaction_time="30000"
+ use_mcast_xmit="false"
+ discard_delivered_msgs="true" />
+ <UNICAST3
+ xmit_table_num_rows="100"
+ xmit_table_msgs_per_row="1000"
+ xmit_table_max_compaction_time="30000" />
+ <pbcast.STABLE desired_avg_gossip="50000" max_bytes="8m" />
+ <pbcast.GMS print_local_addr="true" print_physical_addrs="true" join_timeout="3000" />
+ <MFC max_credits="2M"
+ min_threshold="0.4" />
+ <FRAG2 frag_size="60K" />
+ <pbcast.STATE_TRANSFER />
+ <CENTRAL_LOCK />
+ <COUNTER />
+</config>
diff --git a/src/test/docker/gerrit/Dockerfile b/src/test/docker/gerrit/Dockerfile
index d305e21..4c4d092 100644
--- a/src/test/docker/gerrit/Dockerfile
+++ b/src/test/docker/gerrit/Dockerfile
@@ -1,6 +1,6 @@
-FROM gerritcodereview/gerrit:3.6.4
+FROM gerritcodereview/gerrit:3.7.1
-ENV GERRIT_BRANCH=stable-3.6
+ENV GERRIT_BRANCH=stable-3.7
ENV GERRIT_CI_URL=https://archive-ci.gerritforge.com/job
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/ConfigurationTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/ConfigurationTest.java
index fb1b8d0..b6986db 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/ConfigurationTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/ConfigurationTest.java
@@ -39,10 +39,10 @@
import static com.ericsson.gerrit.plugins.highavailability.Configuration.Index.DEFAULT_SYNCHRONIZE_FORCED;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.Index.INDEX_SECTION;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.Index.SYNCHRONIZE_FORCED_KEY;
+import static com.ericsson.gerrit.plugins.highavailability.Configuration.JGROUPS_SECTION;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.JGroups.CLUSTER_NAME_KEY;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.JGroups.DEFAULT_CLUSTER_NAME;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.JGroups.DEFAULT_SKIP_INTERFACE_LIST;
-import static com.ericsson.gerrit.plugins.highavailability.Configuration.JGroups.JGROUPS_SECTION;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.JGroups.PROTOCOL_STACK_KEY;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.JGroups.SKIP_INTERFACE_KEY;
import static com.ericsson.gerrit.plugins.highavailability.Configuration.Main.DEFAULT_SHARED_DIRECTORY;
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JGroupsKubernetesPeerInfoProviderTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JGroupsKubernetesPeerInfoProviderTest.java
new file mode 100644
index 0000000..0dd27e1
--- /dev/null
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/JGroupsKubernetesPeerInfoProviderTest.java
@@ -0,0 +1,131 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// 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.ericsson.gerrit.plugins.highavailability.peers.jgroups;
+
+import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
+import static com.github.tomakehurst.wiremock.client.WireMock.get;
+import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
+import static com.github.tomakehurst.wiremock.client.WireMock.verify;
+import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
+import static com.google.common.truth.Truth.assertThat;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.mockito.Answers.RETURNS_DEEP_STUBS;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.ericsson.gerrit.plugins.highavailability.Configuration;
+import com.github.tomakehurst.wiremock.junit.WireMockRule;
+import com.google.common.io.Resources;
+import com.google.gerrit.extensions.restapi.Url;
+import java.net.Inet4Address;
+import java.net.NetworkInterface;
+import java.util.List;
+import java.util.Optional;
+import org.apache.http.HttpStatus;
+import org.jgroups.Message;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+import wiremock.com.fasterxml.jackson.databind.ObjectMapper;
+
+@RunWith(MockitoJUnitRunner.class)
+public class JGroupsKubernetesPeerInfoProviderTest {
+
+ @Mock(answer = RETURNS_DEEP_STUBS)
+ private Configuration pluginConfigurationMock;
+
+ @Mock private InetAddressFinder finder;
+ private JGroupsPeerInfoProvider jGroupsPeerInfoProvider;
+ private JGroupsPeerInfoProvider jGroupsPeerInfoProvider2;
+ @Mock private MyUrlProvider myUrlProviderTest;
+
+ @Rule public WireMockRule kubeApiMock = new WireMockRule(options().port(48443));
+
+ @Before
+ public void setUp() throws Exception {
+ System.setProperty("KUBERNETES_MASTER_PROTOCOL", "http");
+ System.setProperty("KUBERNETES_SERVICE_HOST", "localhost");
+ System.setProperty("KUBERNETES_SERVICE_PORT", "48443");
+ System.setProperty("java.net.preferIPv4Stack", "true");
+ }
+
+ @After
+ public void shutdown() {
+ System.clearProperty("KUBERNETES_MASTER_PROTOCOL");
+ System.clearProperty("KUBERNETES_SERVICE_HOST");
+ System.clearProperty("KUBERNETES_SERVICE_PORT");
+ System.clearProperty("java.net.preferIPv4Stack");
+ }
+
+ @Test
+ public void testPeerDiscoveryInKubernetesSuccessful() throws Exception {
+ String namespace = "gerrit";
+ List<String> labels = List.of("app=gerrit", "mode=primary");
+
+ when(pluginConfigurationMock.jgroups().useKubernetes()).thenReturn(true);
+ when(pluginConfigurationMock.jgroups().clusterName()).thenReturn("gerritCluster");
+ when(pluginConfigurationMock.jgroupsKubernetes().namespace()).thenReturn(namespace);
+ when(pluginConfigurationMock.jgroupsKubernetes().labels()).thenReturn(labels);
+
+ NetworkInterface eth0 = NetworkInterface.getByName("eth0");
+ if (eth0 != null) {
+ when(finder.findAddress()).thenReturn(eth0.inetAddresses().findFirst());
+ } else {
+ when(finder.findAddress()).thenReturn(Optional.of(Inet4Address.getByName("127.0.0.1")));
+ }
+ jGroupsPeerInfoProvider =
+ Mockito.spy(
+ new JGroupsPeerInfoProvider(pluginConfigurationMock, finder, myUrlProviderTest));
+ jGroupsPeerInfoProvider2 =
+ Mockito.spy(
+ new JGroupsPeerInfoProvider(pluginConfigurationMock, finder, myUrlProviderTest));
+
+ StringBuilder kubeApiUrlBuilder = new StringBuilder();
+ kubeApiUrlBuilder.append("/api/v1/namespaces/");
+ kubeApiUrlBuilder.append(namespace);
+ kubeApiUrlBuilder.append("/pods?labelSelector=");
+ kubeApiUrlBuilder.append(Url.encode(String.join(",", labels)));
+ String kubeApiUrl = kubeApiUrlBuilder.toString();
+
+ String respJson = Resources.toString(this.getClass().getResource("pod-list.json"), UTF_8);
+ respJson = respJson.replaceAll("\\$\\{IP\\}", finder.findAddress().get().getHostAddress());
+
+ kubeApiMock.stubFor(
+ get(urlEqualTo(kubeApiUrl))
+ .willReturn(
+ aResponse()
+ .withJsonBody(new ObjectMapper().readTree(respJson))
+ .withStatus(HttpStatus.SC_OK)));
+ jGroupsPeerInfoProvider.connect();
+ verify(getRequestedFor(urlEqualTo(kubeApiUrl)));
+ jGroupsPeerInfoProvider2.connect();
+
+ verify(jGroupsPeerInfoProvider, timeout(10000)).receive(any(Message.class));
+
+ assertThat(jGroupsPeerInfoProvider.get().isEmpty()).isFalse();
+ assertThat(jGroupsPeerInfoProvider.get().size()).isEqualTo(1);
+
+ assertThat(jGroupsPeerInfoProvider2.get().isEmpty()).isFalse();
+ assertThat(jGroupsPeerInfoProvider2.get().size()).isEqualTo(1);
+ }
+}
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/MyUrlProviderTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/MyUrlProviderTest.java
index c337947..1a27de0 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/MyUrlProviderTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/MyUrlProviderTest.java
@@ -21,6 +21,7 @@
import static org.mockito.Mockito.when;
import com.ericsson.gerrit.plugins.highavailability.Configuration;
+import com.google.gerrit.common.Nullable;
import com.google.inject.ProvisionException;
import org.eclipse.jgit.lib.Config;
import org.junit.Before;
@@ -48,7 +49,11 @@
}
private MyUrlProvider getMyUrlProvider() {
- return new MyUrlProvider(gerritServerConfig, configurationMock);
+ return getMyUrlProvider(null);
+ }
+
+ private MyUrlProvider getMyUrlProvider(@Nullable String myUrlEnvVar) {
+ return new MyUrlProvider(gerritServerConfig, configurationMock, myUrlEnvVar);
}
@Test
@@ -66,6 +71,12 @@
}
@Test
+ public void testGetJGroupsMyUrlFromEnvVariable() throws Exception {
+ String hostName = "https://foo:8080";
+ assertThat(getMyUrlProvider(hostName).get()).isEqualTo(hostName);
+ }
+
+ @Test
public void testGetJGroupsMyUrlFromListenUrlWhenNoListenUrlSpecified() throws Exception {
ProvisionException thrown = assertThrows(ProvisionException.class, () -> getMyUrlProvider());
assertThat(thrown).hasMessageThat().contains("exactly 1 value configured; found 0");
@@ -97,4 +108,10 @@
when(configurationMock.peerInfoJGroups().myUrl()).thenReturn("http://somehost");
assertThat(getMyUrlProvider().get()).isEqualTo("http://somehost");
}
+
+ @Test
+ public void testGetJGroupsMyUrlOverridesEnvVariable() throws Exception {
+ when(configurationMock.peerInfoJGroups().myUrl()).thenReturn("http://somehost");
+ assertThat(getMyUrlProvider("https://foo:8080").get()).isEqualTo("http://somehost");
+ }
}
diff --git a/src/test/resources/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/pod-list.json b/src/test/resources/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/pod-list.json
new file mode 100644
index 0000000..4be7c10
--- /dev/null
+++ b/src/test/resources/com/ericsson/gerrit/plugins/highavailability/peers/jgroups/pod-list.json
@@ -0,0 +1,109 @@
+{
+ "apiVersion": "v1",
+ "items": [
+ {
+ "apiVersion": "v1",
+ "kind": "Pod",
+ "metadata": {
+ "labels": {
+ "app": "gerrit",
+ "mode": "primary",
+ "controller-revision-hash": "gerrit-primary-1234"
+ },
+ "name": "gerrit-primary-0",
+ "namespace": "gerrit"
+ },
+ "spec": {
+ "containers": [
+ {
+ "image": "gerrit:test",
+ "name": "gerrit"
+ }
+ ],
+ "hostname": "gerrit-primary-0"
+ },
+ "status": {
+ "conditions": [
+ {
+ "status": "True",
+ "type": "Ready"
+ }
+ ],
+ "containerStatuses": [
+ {
+ "image": "gerrit:test",
+ "name": "gerrit",
+ "ready": true,
+ "started": true,
+ "state": {
+ "running": {
+ "startedAt": "2023-08-23T17:26:19Z"
+ }
+ }
+ }
+ ],
+ "phase": "Running",
+ "podIP": "${IP}",
+ "podIPs": [
+ {
+ "ip": "${IP}"
+ }
+ ]
+ }
+ },
+ {
+ "apiVersion": "v1",
+ "kind": "Pod",
+ "metadata": {
+ "labels": {
+ "app": "gerrit",
+ "mode": "primary",
+ "controller-revision-hash": "gerrit-primary-1234"
+ },
+ "name": "gerrit-primary-1",
+ "namespace": "gerrit"
+ },
+ "spec": {
+ "containers": [
+ {
+ "image": "gerrit:test",
+ "name": "gerrit"
+ }
+ ],
+ "hostname": "gerrit-primary-1"
+ },
+ "status": {
+ "conditions": [
+ {
+ "status": "True",
+ "type": "Ready"
+ }
+ ],
+ "containerStatuses": [
+ {
+ "image": "gerrit:test",
+ "name": "gerrit",
+ "ready": true,
+ "started": true,
+ "state": {
+ "running": {
+ "startedAt": "2023-08-23T17:26:19Z"
+ }
+ }
+ }
+ ],
+ "phase": "Running",
+ "podIP": "${IP}",
+ "podIPs": [
+ {
+ "ip": "${IP}"
+ }
+ ]
+ }
+ }
+ ],
+ "kind": "List",
+ "metadata": {
+ "resourceVersion": ""
+ }
+}