blob: 96dbc62ac16c687a26f908a6a3a5ed56accf83d0 [file] [log] [blame]
// Copyright (C) 2024 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.tasks.incomingrepl.dependent;
import static com.google.gerrit.k8s.operator.tasks.incomingrepl.dependent.IncomingReplicationTaskConfigMap.CONFIG_FILE_NAME;
import com.google.gerrit.k8s.operator.api.model.cluster.GerritCluster;
import com.google.gerrit.k8s.operator.api.model.shared.ContainerImageConfig;
import com.google.gerrit.k8s.operator.api.model.shared.NfsWorkaroundConfig;
import com.google.gerrit.k8s.operator.api.model.tasks.incomingrepl.IncomingReplicationTask;
import com.google.gerrit.k8s.operator.tasks.incomingrepl.IncomingReplicationTaskReconciler;
import com.google.gerrit.k8s.operator.util.CRUDReconcileAddKubernetesDependentResource;
import io.fabric8.kubernetes.api.model.Container;
import io.fabric8.kubernetes.api.model.ContainerBuilder;
import io.fabric8.kubernetes.api.model.Secret;
import io.fabric8.kubernetes.api.model.Volume;
import io.fabric8.kubernetes.api.model.VolumeBuilder;
import io.fabric8.kubernetes.api.model.VolumeMount;
import io.fabric8.kubernetes.api.model.VolumeMountBuilder;
import io.fabric8.kubernetes.api.model.batch.v1.CronJob;
import io.fabric8.kubernetes.api.model.batch.v1.CronJobBuilder;
import io.fabric8.kubernetes.api.model.batch.v1.JobTemplateSpec;
import io.fabric8.kubernetes.api.model.batch.v1.JobTemplateSpecBuilder;
import io.javaoperatorsdk.operator.api.reconciler.Context;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class IncomingReplicationTaskCronJob
extends CRUDReconcileAddKubernetesDependentResource<CronJob, IncomingReplicationTask> {
private static final String CONFIGMAP_VOLUME_NAME = "incoming-repl-config";
public IncomingReplicationTaskCronJob() {
super(CronJob.class);
}
@Override
protected CronJob desired(
IncomingReplicationTask incomingReplTask, Context<IncomingReplicationTask> context) {
String ns = incomingReplTask.getMetadata().getNamespace();
String name = incomingReplTask.getMetadata().getName();
Map<String, String> labels = getLabels(incomingReplTask);
List<Container> initContainers = new ArrayList<>();
NfsWorkaroundConfig nfsWorkaround =
incomingReplTask.getSpec().getStorage().getStorageClasses().getNfsWorkaround();
if (nfsWorkaround.isEnabled() && nfsWorkaround.isChownOnStartup()) {
boolean hasIdmapdConfig =
incomingReplTask
.getSpec()
.getStorage()
.getStorageClasses()
.getNfsWorkaround()
.getIdmapdConfig()
!= null;
ContainerImageConfig images = incomingReplTask.getSpec().getContainerImages();
initContainers.add(GerritCluster.createNfsInitContainer(hasIdmapdConfig, images));
}
JobTemplateSpec gitGcJobTemplate =
new JobTemplateSpecBuilder()
.withNewSpec()
.withNewTemplate()
.withNewMetadata()
.withAnnotations(
Map.of(
"sidecar.istio.io/inject",
"false",
"cluster-autoscaler.kubernetes.io/safe-to-evict",
"false"))
.withLabels(labels)
.endMetadata()
.withNewSpec()
.withTolerations(incomingReplTask.getSpec().getTolerations())
.withAffinity(incomingReplTask.getSpec().getAffinity())
.addAllToImagePullSecrets(
incomingReplTask.getSpec().getContainerImages().getImagePullSecrets())
.withRestartPolicy("OnFailure")
.withNewSecurityContext()
.withFsGroup(100L)
.endSecurityContext()
.addAllToInitContainers(initContainers)
.addToContainers(buildTaskContainer(incomingReplTask))
.withVolumes(getVolumes(incomingReplTask))
.endSpec()
.endTemplate()
.endSpec()
.build();
return new CronJobBuilder()
.withApiVersion("batch/v1")
.withNewMetadata()
.withNamespace(ns)
.withName(name)
.withLabels(labels)
.endMetadata()
.withNewSpec()
.withSchedule(incomingReplTask.getSpec().getSchedule())
.withConcurrencyPolicy("Forbid")
.withJobTemplate(gitGcJobTemplate)
.endSpec()
.build();
}
private static String getComponentName(String incomingReplTaskName) {
return String.format("task-incoming-replication-%s", incomingReplTaskName);
}
private static Map<String, String> getLabels(IncomingReplicationTask incomingReplTask) {
String name = incomingReplTask.getMetadata().getName();
return GerritCluster.getLabels(
name, getComponentName(name), IncomingReplicationTaskReconciler.class.getSimpleName());
}
private Container buildTaskContainer(IncomingReplicationTask incomingReplTask) {
ContainerBuilder taskContainerBuilder =
new ContainerBuilder()
.withName("incoming-replication")
.withImagePullPolicy(
incomingReplTask.getSpec().getContainerImages().getImagePullPolicy())
.withImage(
incomingReplTask
.getSpec()
.getContainerImages()
.getGerritImages()
.getFullImageName("fetch-job"))
.withResources(incomingReplTask.getSpec().getResources())
.withVolumeMounts(getVolumeMounts(incomingReplTask));
return taskContainerBuilder.build();
}
private List<VolumeMount> getVolumeMounts(IncomingReplicationTask incomingReplTask) {
List<VolumeMount> volumeMounts = new ArrayList<>();
volumeMounts.add(GerritCluster.getGitRepositoriesVolumeMount("/var/gerrit/git"));
volumeMounts.add(
new VolumeMountBuilder()
.withName(CONFIGMAP_VOLUME_NAME)
.withSubPath(CONFIG_FILE_NAME)
.withMountPath("/var/gerrit/etc/" + CONFIG_FILE_NAME)
.build());
if (incomingReplTask.getSpec().getStorage().getStorageClasses().getNfsWorkaround().isEnabled()
&& incomingReplTask
.getSpec()
.getStorage()
.getStorageClasses()
.getNfsWorkaround()
.getIdmapdConfig()
!= null) {
volumeMounts.add(GerritCluster.getNfsImapdConfigVolumeMount());
}
String secretRef = incomingReplTask.getSpec().getSecretRef();
if (secretRef != null && !secretRef.isBlank()) {
Secret secret =
client
.resources(Secret.class)
.inNamespace(incomingReplTask.getMetadata().getNamespace())
.withName(secretRef)
.get();
if (secret != null && secret.getData().containsKey(".netrc")) {
volumeMounts.add(
new VolumeMountBuilder()
.withName(secretRef)
.withSubPath(".netrc")
.withMountPath("/home/gerrit/.netrc")
.build());
}
}
return volumeMounts;
}
private List<Volume> getVolumes(IncomingReplicationTask incomingReplTask) {
List<Volume> volumes = new ArrayList<>();
volumes.add(
GerritCluster.getSharedVolume(
incomingReplTask.getSpec().getStorage().getSharedStorage().getExternalPVC()));
volumes.add(
new VolumeBuilder()
.withName(CONFIGMAP_VOLUME_NAME)
.withNewConfigMap()
.withName(IncomingReplicationTaskConfigMap.getName(incomingReplTask))
.endConfigMap()
.build());
if (incomingReplTask
.getSpec()
.getStorage()
.getStorageClasses()
.getNfsWorkaround()
.isEnabled()) {
if (incomingReplTask
.getSpec()
.getStorage()
.getStorageClasses()
.getNfsWorkaround()
.getIdmapdConfig()
!= null) {
volumes.add(GerritCluster.getNfsImapdConfigVolume());
}
}
String secretRef = incomingReplTask.getSpec().getSecretRef();
if (secretRef != null && !secretRef.isBlank()) {
volumes.add(
new VolumeBuilder()
.withName(secretRef)
.withNewSecret()
.withSecretName(secretRef)
.endSecret()
.build());
}
return volumes;
}
}