app/gerrit/kube: implement

This change impelements the k8s machinery for Gerrit.

This might look somewhat complex at first, but the gist of it is:

 - k8s mounts etc, git, cache, db, index as RW PVs
 - k8s mounts a configmap containing gerrit.conf into an external
   directory
 - k8s mounts a secret containing secure.conf into an external directory
 - on startup, gerrit's entrypoint will copy over {gerrit,secure}.conf
   and start a small updater script that copies over gerrit.conf if
   there's any change. This should, in theory, make gerrit reload its
   config.

This is already running on production. You're probably looking at this
change through the instance deployed by itself :)

Change-Id: Ida9dff721c17cf4da7fb6ccbb54d2c4024672572
master
q3k 2019-06-21 20:38:35 +02:00
parent 573da78859
commit a7e26ccfe1
6 changed files with 316 additions and 0 deletions

3
.bazelrc Normal file
View File

@ -0,0 +1,3 @@
# Required for app/gerrit/gerrit-oauth-provider
build --workspace_status_command=./tools/workspace-status.sh
test --build_tests_only

View File

@ -60,6 +60,7 @@ container_repositories()
# Docker base images
load("@io_bazel_rules_docker//container:container.bzl", "container_pull")
container_pull(
name = "prodimage-bionic",
registry = "index.docker.io",
@ -68,6 +69,14 @@ container_pull(
digest = "sha256:b36667c98cf8f68d4b7f1fb8e01f742c2ed26b5f0c965a788e98dfe589a4b3e4",
)
container_pull(
name = "gerrit-3.0.0",
registry = "index.docker.io",
repository = "gerritcodereview/gerrit",
tag = "3.0.0-ubuntu18",
digest = "sha256:f107729011d8b81611e35a0ad452f21a424c1820664e9f95d135ad411e87b9bb",
)
# HTTP stuff from the Internet
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_file")
http_file(

33
app/gerrit/BUILD Normal file
View File

@ -0,0 +1,33 @@
load("@io_bazel_rules_docker//container:container.bzl", "container_image")
container_image(
name="with_plugins",
base="@gerrit-3.0.0//image",
files = [
"//app/gerrit/gerrit-oauth-provider:gerrit-oauth-provider",
],
# we cannot drop it directly in /var/gerrit/plugins as that changes the
# directory owner to 0:0 and then breaks the gerrit installer that wants
# to overwrite plugins.
directory = "/var/gerrit-plugins",
)
container_image(
name="3.0.0-r7",
base=":with_plugins",
files = [":entrypoint.sh"],
directory = "/",
entrypoint = ["/entrypoint.sh"],
)
genrule(
name = "push_latest",
srcs = [":3.0.0-r7"],
outs = ["version.sh"],
executable = True,
cmd = """
tag=3.0.0-r7
docker tag bazel/app/gerrit:$$tag registry.k0.hswaw.net/app/gerrit:$$tag
docker push registry.k0.hswaw.net/app/gerrit:$$tag
echo -ne "#!/bin/sh\necho Pushed $$tag.\n" > $(OUTS)
""",
)

43
app/gerrit/entrypoint.sh Executable file
View File

@ -0,0 +1,43 @@
#!/bin/bash -e
ls -la /var/gerrit/*
if [ ! -d /var/gerrit/git/All-Projects.git ] || [ "$1" == "init" ]
then
echo "Initializing Gerrit site ..."
java -jar /var/gerrit/bin/gerrit.war init --batch --install-all-plugins -d /var/gerrit
java -jar /var/gerrit/bin/gerrit.war reindex -d /var/gerrit
fi
echo "Running hscloud init setup..."
rm -f /var/gerrit/etc/gerrit.config
cp /var/gerrit-config/gerrit.config /var/gerrit/etc/gerrit.config
rm -f /var/gerrit/etc/secure.config
cp /var/gerrit-secure/secure.config /var/gerrit/etc/secure.config
cp /var/gerrit-plugins/* /var/gerrit/plugins/
echo "Starting config updater..."
# Keep copying config over in background. We cannot run directly from
# the configmap filesystem as gerrit really wants a read-write FS.
(
src=/var/gerrit-config/gerrit.config
dst=/var/gerrit/etc/gerrit.config
while true; do
sleep 60
if ! cmp -s $src $dst; then
echo "HSCLOUD: bumping config"
cp $src $dst
fi
done
) &
ls -la /var/gerrit/*
if [ "$1" != "init" ]
then
echo "Running Gerrit ..."
exec /var/gerrit/bin/gerrit.sh run
fi

View File

@ -0,0 +1,209 @@
local kube = import "../../../kube/kube.libsonnet";
{
local gerrit = self,
local cfg = gerrit.cfg,
cfg:: {
namespace: error "namespace must be set",
appName: "gerrit",
prefix: "", # if set, should be 'foo-'
domain: error "domain must be set",
identity: error "identity (UUID) must be set",
// The secret must contain a key named 'secure.config' containing (at least):
// [auth]
// registerEmailPrivateKey = <random>
// [plugin "gerrit-oauth-provider-warsawhackerspace-oauth"]
// client-id = foo
// client-secret = bar
// [sendemail]
// smtpPass = foo
// [receiveemail]
// password = bar
secureSecret: error "secure secret name must be set",
storageClass: error "storage class must be set",
storageSize: {
git: "50Gi", // Main storage for repositories and NoteDB.
index: "10Gi", // Secondary Lucene index
cache: "10Gi", // H2 cache databases
db: "1Gi", // NoteDB is used, so database is basically empty (H2 accountPatchReviewDatabase)
etc: "1Gi", // Random site stuff.
},
email: {
server: "mail.hackerspace.pl",
username: "gerrit",
address: "gerrit@hackerspace.pl",
},
tag: "3.0.0-r7",
image: "registry.k0.hswaw.net/app/gerrit:" + cfg.tag,
resources: {
requests: {
cpu: "100m",
memory: "500Mi",
},
limits: {
cpu: "1",
memory: "2Gi",
},
},
},
name(suffix):: cfg.prefix + suffix,
metadata(component):: {
namespace: cfg.namespace,
labels: {
"app.kubernetes.io/name": cfg.appName,
"app.kubernetes.io/managed-by": "kubecfg",
"app.kubernetes.io/component": "component",
},
},
configmap: kube.ConfigMap(gerrit.name("gerrit")) {
metadata+: gerrit.metadata("configmap"),
data: {
"gerrit.config": |||
[gerrit]
basePath = git
canonicalWebUrl = https://%(domain)s/
serverId = %(identity)s
[container]
javaOptions = -Djava.security.edg=file:/dev/./urandom
[auth]
type = OAUTH
gitBasicAuthPolicy = HTTP
[httpd]
listenUrl = proxy-http://*:8080
[sshd]
advertisedAddress = %(domain)s
[user]
email = %(emailAddress)s
[sendemail]
enable = true
from = MIXED
smtpServer = %(emailServer)s
smtpServerPort = 465
smtpEncryption = ssl
smtpUser = %(emailUser)s
[receiveemail]
protocol = IMAP
host = %(emailServer)s
username = %(emailUser)s
encryption = TLS
enableImapIdle = true
||| % {
domain: cfg.domain,
identity: cfg.identity,
emailAddress: cfg.email.address,
emailServer: cfg.email.server,
emailUser: cfg.email.username,
},
},
},
volumes: {
[name]: kube.PersistentVolumeClaim(gerrit.name(name)) {
metadata+: gerrit.metadata("storage"),
spec+: {
storageClassName: cfg.storageClassName,
accessModes: ["ReadWriteOnce"],
resources: {
requests: {
storage: cfg.storageSize[name],
},
},
},
}
for name in ["etc", "git", "index", "cache", "db"]
},
local volumeMounts = {
[name]: { mountPath: "/var/gerrit/%s" % name }
for name in ["etc", "git", "index", "cache", "db"]
} {
// ConfigMap gets mounted here
config: { mountPath: "/var/gerrit-config" },
// SecureSecret gets mounted here
secure: { mountPath: "/var/gerrit-secure" },
},
deployment: kube.Deployment(gerrit.name("gerrit")) {
metadata+: gerrit.metadata("deployment"),
spec+: {
replicas: 1,
template+: {
spec+: {
securityContext: {
fsGroup: 1000, # gerrit uid
},
volumes_: {
config: kube.ConfigMapVolume(gerrit.configmap),
secure: { secret: { secretName: cfg.secureSecret} },
} {
[name]: kube.PersistentVolumeClaimVolume(gerrit.volumes[name])
for name in ["etc", "git", "index", "cache", "db"]
},
containers_: {
gerrit: kube.Container(gerrit.name("gerrit")) {
image: cfg.image,
ports_: {
http: { containerPort: 8080 },
ssh: { containerPort: 29418 },
},
resources: cfg.resources,
volumeMounts_: volumeMounts,
},
},
},
},
},
},
svc: kube.Service(gerrit.name("gerrit")) {
metadata+: gerrit.metadata("service"),
target_pod:: gerrit.deployment.spec.template,
spec+: {
ports: [
{ name: "http", port: 80, targetPort: 8080, protocol: "TCP" },
{ name: "ssh", port: 22, targetPort: 29418, protocol: "TCP" },
],
type: "ClusterIP",
},
},
ingress: kube.Ingress(gerrit.name("gerrit")) {
metadata+: gerrit.metadata("ingress") {
annotations+: {
"kubernetes.io/tls-acme": "true",
"certmanager.k8s.io/cluster-issuer": "letsencrypt-prod",
"nginx.ingress.kubernetes.io/proxy-body-size": "0",
},
},
spec+: {
tls: [
{ hosts: [cfg.domain], secretName: gerrit.name("acme") },
],
rules: [
{
host: cfg.domain,
http: {
paths: [
{ path: "/", backend: gerrit.svc.name_port },
],
},
}
],
},
},
}

View File

@ -0,0 +1,19 @@
local kube = import "../../../kube/kube.libsonnet";
local gerrit = import "gerrit.libsonnet";
{
namespace: kube.Namespace("gerrit"),
gerrit: gerrit {
cfg+: {
namespace: "gerrit",
prefix: "",
domain: "gerrit.hackerspace.pl",
identity: "7b6244cf-e30b-42c5-ba91-c329ef4e6cf1",
storageClassName: "waw-hdd-redundant-1",
secureSecret: "gerrit",
},
},
}