blob: 670368a3f7ecd2892390d2c6ec3afaebc73b69ba [file] [log] [blame]
// Copyright (C) 2022 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.google.gerrit.k8s.operator.gerrit;
import static com.google.gerrit.k8s.operator.test.TestGerritCluster.CLUSTER_NAME;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.awaitility.Awaitility.await;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.extensions.api.GerritApi;
import com.google.gerrit.k8s.operator.gerrit.GerritSpec.GerritMode;
import com.google.gerrit.k8s.operator.test.AbstractGerritOperatorE2ETest;
import com.urswolfer.gerrit.client.rest.GerritAuthData;
import com.urswolfer.gerrit.client.rest.GerritRestApiFactory;
import io.fabric8.kubernetes.api.model.LoadBalancerIngress;
import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
import io.fabric8.kubernetes.api.model.Quantity;
import io.fabric8.kubernetes.api.model.ResourceRequirementsBuilder;
import io.fabric8.kubernetes.api.model.Secret;
import io.fabric8.kubernetes.api.model.SecretBuilder;
import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
import io.fabric8.kubernetes.api.model.networking.v1.IngressStatus;
import java.util.Base64;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
public class GerritE2E extends AbstractGerritOperatorE2ETest {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private static final String INGRESS_NAME = "gerrit-ingress";
private static final String INGRESS_DOMAIN = testProps.getIngressDomain();
private static final String SECURE_CONFIG_SECRET_NAME = "gerrit-secret";
private static final String DEFAULT_GERRIT_CONFIG =
"[gerrit]\n"
+ " serverId = gerrit-1\n"
+ "[index]\n"
+ " type = LUCENE\n"
+ "[auth]\n"
+ " type = LDAP\n"
+ "[ldap]\n"
+ " server = ldap://openldap.openldap.svc.cluster.local:1389\n"
+ " accountBase = dc=example,dc=org\n"
+ " username = cn=admin,dc=example,dc=org\n"
+ "[httpd]\n"
+ " listenUrl = proxy-https://*:8080/\n"
+ " requestLog = true\n"
+ " gracefulStopTimeout = 1m\n"
+ "[sshd]\n"
+ " listenAddress = off\n"
+ "[transfer]\n"
+ " timeout = 120 s\n"
+ "[user]\n"
+ " name = Gerrit Code Review\n"
+ " email = gerrit@example.com\n"
+ " anonymousCoward = Unnamed User\n"
+ "[cache]\n"
+ " directory = cache\n"
+ "[container]\n"
+ " javaOptions = -Xmx4g";
@Test
void testPrimaryGerritIsCreated() throws Exception {
gerritCluster.setIngressEnabled(true);
Secret secureConfig = createSecureConfig();
client.resource(secureConfig).createOrReplace();
Gerrit gerrit = createGerritCR(GerritMode.PRIMARY);
client.resource(gerrit).createOrReplace();
waitForGerritReadiness(gerrit);
logger.atInfo().log("Waiting max 2 minutes for the Ingress to have an external IP.");
await()
.atMost(2, MINUTES)
.untilAsserted(
() -> {
Ingress ingress =
client
.network()
.v1()
.ingresses()
.inNamespace(operator.getNamespace())
.withName(INGRESS_NAME)
.get();
assertThat(ingress, is(notNullValue()));
IngressStatus status = ingress.getStatus();
assertThat(status, is(notNullValue()));
List<LoadBalancerIngress> lbIngresses = status.getLoadBalancer().getIngress();
assertThat(lbIngresses, hasSize(1));
assertThat(lbIngresses.get(0).getIp(), is(notNullValue()));
});
GerritApi gerritApi =
new GerritRestApiFactory()
.create(
new GerritAuthData.Basic(
String.format(
"http://%s.%s", ServiceDependentResource.getName(gerrit), INGRESS_DOMAIN)));
await()
.atMost(2, MINUTES)
.untilAsserted(
() -> {
assertDoesNotThrow(() -> gerritApi.config().server().getVersion());
assertThat(gerritApi.config().server().getVersion(), notNullValue());
assertThat(gerritApi.config().server().getVersion(), not(is("<2.8")));
logger.atInfo().log("Gerrit version: %s", gerritApi.config().server().getVersion());
});
}
@Test
void testGerritReplicaIsCreated() throws Exception {
Secret secureConfig = createSecureConfig();
client.resource(secureConfig).createOrReplace();
Gerrit gerrit = createGerritCR(GerritMode.REPLICA);
client.resource(gerrit).createOrReplace();
waitForGerritReadiness(gerrit);
assertTrue(
client
.pods()
.inNamespace(operator.getNamespace())
.withName(gerrit.getMetadata().getName() + "-0")
.inContainer("gerrit")
.getLog()
.contains("Gerrit Code Review [replica]"));
}
@Test
void testRestartHandlingOnConfigChange() {
Secret secureConfig = createSecureConfig();
client.resource(secureConfig).createOrReplace();
Gerrit gerrit = createGerritCR(GerritMode.PRIMARY);
client.resource(gerrit).createOrReplace();
logger.atInfo().log("Waiting max 2 minutes for the Gerrit StatefulSet to be ready.");
await()
.atMost(2, MINUTES)
.untilAsserted(
() -> {
assertTrue(
client
.apps()
.statefulSets()
.inNamespace(operator.getNamespace())
.withName(gerrit.getMetadata().getName())
.isReady());
});
GerritServiceConfig svcConfig = new GerritServiceConfig();
int changedPort = 8081;
svcConfig.setHttpPort(changedPort);
GerritSpec gerritSpec = gerrit.getSpec();
gerritSpec.setService(svcConfig);
gerrit.setSpec(gerritSpec);
client.resource(gerrit).createOrReplace();
await()
.atMost(30, SECONDS)
.untilAsserted(
() -> {
assertTrue(
client
.services()
.inNamespace(operator.getNamespace())
.withName(ServiceDependentResource.getName(gerrit))
.get()
.getSpec()
.getPorts()
.stream()
.anyMatch(port -> port.getPort() == changedPort));
});
Mockito.verify(gerritReconciler, times(1)).restartGerritStatefulSet(any());
String changedConfig =
DEFAULT_GERRIT_CONFIG.replace("proxy-https://*:8080/", "proxy-https://*:8081/");
gerritSpec.setConfigFiles(Map.of("gerrit.config", changedConfig));
gerrit.setSpec(gerritSpec);
client.resource(gerrit).createOrReplace();
await()
.atMost(2, MINUTES)
.untilAsserted(
() -> {
Mockito.verify(gerritReconciler, times(2)).restartGerritStatefulSet(any());
});
secureConfig.setData(
Map.of(
"secure.config",
Base64.getEncoder().encodeToString("[section]\nkey = value_new".getBytes())));
client.resource(secureConfig).createOrReplace();
await()
.atMost(2, MINUTES)
.untilAsserted(
() -> {
Mockito.verify(gerritReconciler, times(3)).restartGerritStatefulSet(any());
});
}
private Gerrit createGerritCR(GerritMode mode) {
Gerrit gerrit = new Gerrit();
ObjectMeta gerritMeta =
new ObjectMetaBuilder().withName("gerrit").withNamespace(operator.getNamespace()).build();
gerrit.setMetadata(gerritMeta);
GerritSpec gerritSpec = new GerritSpec();
gerritSpec.setCluster(CLUSTER_NAME);
GerritSite site = new GerritSite();
site.setSize(new Quantity("1Gi"));
gerritSpec.setMode(mode);
gerritSpec.setSite(site);
gerritSpec.setResources(
new ResourceRequirementsBuilder()
.withRequests(Map.of("cpu", new Quantity("1"), "memory", new Quantity("5Gi")))
.build());
gerritSpec.setConfigFiles(Map.of("gerrit.config", DEFAULT_GERRIT_CONFIG));
gerritSpec.setSecrets(Set.of(SECURE_CONFIG_SECRET_NAME));
gerrit.setSpec(gerritSpec);
return gerrit;
}
private Secret createSecureConfig() {
return new SecretBuilder()
.withNewMetadata()
.withNamespace(operator.getNamespace())
.withName(SECURE_CONFIG_SECRET_NAME)
.endMetadata()
.withData(
Map.of(
"secure.config",
Base64.getEncoder()
.encodeToString(
String.format("[ldap]\npassword = %s", testProps.getLdapAdminPwd())
.getBytes())))
.build();
}
private void waitForGerritReadiness(Gerrit gerrit) {
logger.atInfo().log("Waiting max 1 minutes for the configmaps to be created.");
await()
.atMost(1, MINUTES)
.untilAsserted(
() -> {
assertThat(
client
.configMaps()
.inNamespace(operator.getNamespace())
.withName(GerritConfigMapDependentResource.getName(gerrit))
.get(),
is(notNullValue()));
assertThat(
client
.configMaps()
.inNamespace(operator.getNamespace())
.withName(GerritInitConfigMapDependentResource.getName(gerrit))
.get(),
is(notNullValue()));
});
logger.atInfo().log("Waiting max 1 minutes for the Gerrit StatefulSet to be created.");
await()
.atMost(1, MINUTES)
.untilAsserted(
() -> {
assertThat(
client
.apps()
.statefulSets()
.inNamespace(operator.getNamespace())
.withName(gerrit.getMetadata().getName())
.get(),
is(notNullValue()));
});
logger.atInfo().log("Waiting max 1 minutes for the Gerrit Service to be created.");
await()
.atMost(1, MINUTES)
.untilAsserted(
() -> {
assertThat(
client
.services()
.inNamespace(operator.getNamespace())
.withName(ServiceDependentResource.getName(gerrit))
.get(),
is(notNullValue()));
});
logger.atInfo().log("Waiting max 2 minutes for the Gerrit StatefulSet to be ready.");
await()
.atMost(2, MINUTES)
.untilAsserted(
() -> {
assertTrue(
client
.apps()
.statefulSets()
.inNamespace(operator.getNamespace())
.withName(gerrit.getMetadata().getName())
.isReady());
});
}
}