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": "" + } +}