blob: 919e21766e7319c28a1dfc91d0aaa02c7549d639 [file] [log] [blame] [view]
Giorgi Lekveishvili4ec4c022024-08-17 15:09:24 +04001# Gerrit Operator
2
31. [Gerrit Operator](#gerrit-operator)
4 1. [Build](#build)
5 2. [Versioning](#versioning)
6 3. [Publish](#publish)
7 4. [Tests](#tests)
8 5. [Prerequisites](#prerequisites)
9 1. [Shared Storage (ReadWriteMany)](#shared-storage-readwritemany)
10 2. [Ingress provider](#ingress-provider)
11 6. [Deploy](#deploy)
12 1. [Using helm charts](#using-helm-charts)
13 1. [gerrit-operator-crds](#gerrit-operator-crds)
14 2. [gerrit-operator](#gerrit-operator-1)
15 2. [Without the helm charts](#without-the-helm-charts)
16 7. [CustomResources](#customresources)
17 1. [GerritCluster](#gerritcluster)
18 2. [Gerrit](#gerrit)
19 3. [GitGarbageCollection](#gitgarbagecollection)
20 4. [Receiver](#receiver)
21 5. [GerritNetwork](#gerritnetwork)
22 8. [Configuration of Gerrit](#configuration-of-gerrit)
23
24## Build
25
26For this step, you need Java 11 and Maven installed.
27
28To build all components of the operator run:
29
30```sh
31cd operator
32mvn clean install
33```
34
35This step compiles the Java source code into `.class` bytecode files in a newly
36generated `operator/target` folder. A `gerrit-operator` image is also created
37locally. Moreover, the CRD helm chart is updated with the latest CRDs as part of
38this build step.
39
40The jar-version and container image tag can be set using the `revision` property:
41
42```sh
43mvn clean install -Drevision=$(git describe --always --dirty)
44```
45
46## Versioning
47
48The Gerrit Operator is still in an early state of development. The operator is
49thus at the moment not semantically versioned. The CustomResources are as of now
50independently versioned, i.e. the `GerritCluster` resource can have a different
51version than the `GitGarbageCollection` resource, although they are in the same
52group. At the moment, only the current version will be supported by the operator,
53i.e. there won't be a migration path. As soon as the API reaches some stability,
54this will change.
55
56## Publish
57
58Currently, there does not exist a container image for the operator in the
59`docker.io/k8sgerrit` registry. You must build your own image in order to run
60the operator in your cluster. To publish the container image of the Gerrit
61Operator:
62
631. Update the `docker.registry` and `docker.org` tags in the `operator/pom.xml`
64file to point to your own Docker registry and org that you have permissions to
65push to.
66
67```xml
68<docker.registry>my-registry</docker.registry>
69<docker.org>my-org</docker.org>
70```
71
722. run the following commands:
73
74```sh
75cd operator
76mvn clean install -P publish
77```
78
79This will build the operator source code, create an image out of the
80built artifacts, and publish this image to the registry specified in the
81`pom.xml` file. The built image is multi-platform - it will run on both `amd64`
82and `arm64` architectures. It is okay to run this build command from an ARM
83Mac.
84
85## Tests
86
87Executing the E2E tests has a few infrastructure requirements that have to be
88provided:
89
90- An (unused) Kubernetes cluster
91- The 'default' StorageClass that supports ReadWriteOnce access. It has to be
92 possible to provision volumes using this StorageClass.
93- A StorageClass that supports ReadWriteMany access. It has to be possible to
94 provision volumes using this StorageClass. Such a StorageClass could be provided
95 by the [NFS-subdir-provisioner chart](https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner).
96- An [Nginx Ingress Controller](https://github.com/kubernetes/ingress-nginx)
97- An installation of [OpenLDAP](../supplements/test-cluster/ldap/openldap.yaml)
98 with at least one user.
99- Istio installed with the [profile](../istio/gerrit.profile.yaml) provided by
100 this project
101- A secret containing valid certificates for the given hostnames. For istio this
102 secret has to be named `tls-secret` and be present in the `istio-system` namespace.
103 For the Ingress controller, the secret has to be either set as the default
104 secret to be used or somehow automatically be provided in the namespaces created
105 by the tests and named `tls-secret`, e.g. by using Gardener to manage DNS and
106 certificates.
107
108A sample setup for components required in the cluster is provided under
109`$REPO_ROOT/supplements/test-cluster`. Some configuration has to be done manually
110(marked by `#TODO`) and the `deploy.sh`-script can be used to install/update all
111components.
112
113In addition, some properties have to be set to configure the tests:
114
115- `rwmStorageClass`: Name of the StorageClass providing RWM-access (default:nfs-client)
116- `registry`: Registry to pull container images from
117- `RegistryOrg`: Organization of the container images
118- `tag`: Container tag
119- `registryUser`: User for the container registry
120- `registryPwd`: Password for the container registry
121- `ingressDomain`: Domain to be used for the ingress
122- `istioDomain`: Domain to be used for istio
123- `ldapAdminPwd`: Admin password for LDAP server
124- `gerritUser`: Username of a user in LDAP
125- `gerritPwd`: The password of `gerritUser`
126
127The properties should be set in the `test.properties` file. Alternatively, a
128path of a properties file can be configured by using the
129`-Dproperties=<path to properties file>`-option.
130
131To run all E2E tests, use:
132
133```sh
134cd operator
135mvn clean install -P integration-test -Dproperties=<path to properties file>
136```
137
138Note, that running the E2E tests will also involve pushing the container image
139to the repository configured in the properties file.
140
141## Prerequisites
142
143Deploying Gerrit using the operator requires some additional prerequisites to be
144fulfilled:
145
146### Shared Storage (ReadWriteMany)
147
148Gerrit instances share the repositories and other data using shared volumes. Thus,
149a StorageClass and a suitable provisioner have to be available in the cluster.
150An example for such a provisioner would be the
151[NFS-subdir-external-provisioner](https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner).
152
153### Ingress provider
154
155The Gerrit Operator will also set up network routing rules and an ingress point
156for the Gerrit instances it manages. The network routing rules ensure that requests
157will be routed to the intended GerritCluster component, e.g. in case a primary
158Gerrit and a Gerrit Replica exist in the cluster, git fetch/clone requests will
159be sent to the Gerrit Replica and all other requests to the primary Gerrit.
160
161You may specify the ingress provider by setting the `INGRESS` environment
162variable in the operator Deployment manifest. That is, the choice of an ingress
163provider is an operator-level setting. However, you may specify some ingress
164configuration options (host, tls, etc) at the `GerritCluster` level, via
165[GerritClusterIngressConfig](operator-api-reference.md#gerritclusteringressconfig).
166
167The Gerrit Operator currently supports the following Ingress providers:
168
169- **NONE**
170
171 The operator will install no Ingress components. Services will still be available.
172 No prerequisites are required for this case.
173
174 If `spec.ingress.enabled` is set to `true` in GerritCluster, the operator will
175 still configure network related options like `http.listenUrl` in Gerrit based on
176 the other options in `spec.ingress`.
177
178- **INGRESS**
179
180 The operator will install an Ingress. Currently only the
181 [Nginx-Ingress-Controller](https://docs.nginx.com/nginx-ingress-controller/) is
182 supported, which will have to be installed in the cluster and has to be configured
183 to [allow snippet configurations](https://docs.nginx.com/nginx-ingress-controller/configuration/ingress-resources/advanced-configuration-with-snippets/).
184 An example of a working deployment can be found [here](../supplements/test-cluster/ingress/).
185
186 SSH support is not fully managed by the operator, since it has to be enabled and
187 [configured in the nginx ingress controller itself](https://kubernetes.github.io/ingress-nginx/user-guide/exposing-tcp-udp-services/).
188
189- **ISTIO**
190
191 The operator supports the use of [Istio](https://istio.io/) as a service mesh.
192 An example on how to set up Istio can be found [here](../istio/gerrit.profile.yaml).
193
194- **AMBASSADOR**
195
196 The operator also supports [Ambassador](https://www.getambassador.io/) for
197 setting up ingress to the Gerrits deployed by the operator. If you use
198 Ambassador's "Edge Stack" or "Emissary Ingress" to provide ingress to your k8s
199 services, you should set INGRESS=AMBASSADOR. Currently, SSH is not directly
200 supported when using INGRESS=AMBASSADOR.
201
202
203## Deploy
204You will need to have admin privileges for your k8s cluster in order to be able
205to deploy the following resources.
206
207You may choose to deploy the operator resources using helm, or directly via
208`kubectl apply`.
209
210### Using helm charts
211Make sure you have [helm](https://helm.sh/) installed in your environment.
212
213There are two relevant helm charts.
214
215#### gerrit-operator-crds
216
217This chart installs the CRDs (k8s API extensions) to your k8s cluster. No chart
218values need to be modified. The build initiated by the `mvn install` command
219from the [Publish](#publish) section includes a step that updates the CRDs in
220this helm chart to reflect any changes made to them in the operator source code.
221The CRDs installed are: GerritCluster, Gerrit, GitGarbageCollection, Receiver.
222
223You do not need to manually `helm install` this chart; this chart is installed
224as a dependency of the second `gerrit-operator` helm chart as described in the
225next subheading.
226
227#### gerrit-operator
228
229This chart installs the `gerrit-operator-crds` chart as a dependency, and the
230following k8s resources:
231- Deployment
232- ServiceAccount
233- ClusterRole
234- ClusterRoleBinding
235
236The operator itself creates a Service resource and a
237ValidationWebhookConfigurations resource behind the scenes.
238
239You will need to modify the values in `helm-charts/gerrit-operator/values.yaml`
240to point the chart to the registry/org that is hosting the Docker container
241image for the operator (from the [Publish](#publish) step earlier). Now,
242
243run:
244```sh
245# Create a namespace for the gerrit-operator
246kubectl create ns gerrit-operator
247
248# Build the gerrit-operator-crds chart and store it in the charts/ subdirectory
249helm dependency build helm-charts/gerrit-operator/
250
251# Install the gerrit-operator-crds chart and the gerrit-operator chart
252helm -n gerrit-operator install gerrit-operator helm-charts/gerrit-operator/
253```
254
255The chart itself, and all the bundled namespaced resources, are installed in the
256`gerrit-operator` namespace, as per the `-n` option in the helm command.
257
258### Without the helm charts
259
260First all CustomResourceDefinitions have to be deployed:
261
262```sh
263kubectl apply -f operator/target/classes/META-INF/fabric8/*-v1.yml
264```
265
266Note that these do not include the -v1beta1.yaml files, as those are for old
267Kubernetes versions.
268
269The operator requires a Java Keystore with a keypair inside to allow TLS
270verification for Kubernetes Admission Webhooks. To create a keystore and
271encode it with base64, run:
272
273```sh
274keytool \
275 -genkeypair \
276 -alias operator \
277 -keystore keystore \
278 -keyalg RSA \
279 -keysize 2048 \
280 -validity 3650
281cat keystore | base64 -b 0
282```
283
284Add the result to the Secret in `k8s/operator.yaml` (see comments in the file)
285and also add the base64-encoded password for the keystore to the secret.
286
287Then the operator and associated RBAC rules can be deployed:
288
289```sh
290kubectl apply -f operator/k8s/rbac.yaml
291kubectl apply -f operator/k8s/operator.yaml
292```
293
294`k8s/operator.yaml` contains a basic deployment of the operator. Resources,
295docker image name etc. might have to be adapted. For example, the ingress
296provider has to be configured by setting the `INGRESS` environment variable
297in `operator/k8s/operator.yaml` to either `NONE`, `INGRESS`, `ISTIO`, or
298`AMBASSADOR`.
299
300## CustomResources
301
302The operator manages several CustomResources that are described in more detail
303below.
304
305The API reference for all CustomResources can be found [here](operator-api-reference.md).
306
307### GerritCluster
308
309The GerritCluster CustomResource installs one or multiple Gerrit instances. The
310operator takes over managing the state of all Gerrit instances within the cluster
311and ensures that the state stays in sync. To this end it manages additional
312resources that are shared between Gerrit instances or are required to synchronize
313the state between Gerrit instances. These additional resources include:
314
315- storage
316- network / service mesh
317
318Installing Gerrit with the GerritCluster resource is highly recommended over using
319the [Gerrit](#gerrit) CustomResource directly, even if only a single deployment is
320installed, since this reduces the requirements that have to be managed manually.
321The same holds true for the [Receiver](#receiver) CustomResource, which without
322a Gerrit instance using the same site provides little value.
323
324For now, only a single Gerrit CustomResource using each [mode](./operator-api-reference.md#gerritmode)
325can be deployed in a GerritCluster, e.g. one primary Gerrit and one Gerrit Replica.
326The reason for that is, that there is currently no sharding implemented and thus
327multiple deployments don't bring any more value than just scaling the existing
328deployment. Instead of a primary Gerrit also a Receiver can be installed.
329
330### Gerrit
331
332The Gerrit CustomResource deploys a Gerrit, which can run in multiple modes.
333
334The Gerrit-CustomResource is mainly meant to be used by the GerritCluster-reconciler
335to install Gerrit-instances managed by a GerritCluster. Gerrit-CustomResources
336can however also be applied separately. Note, that the Gerrit operator will then
337not create any storage resources or setup any network resources in addition to
338the service.
339
340### GitGarbageCollection
341
342The GitGarbageCollection-CustomResource is used by the operator to set up CronJobs
343that regularly run Git garbage collection on the git repositories that are served
344by a GerritCluster.
345
346A GitGarbageCollection can either handle all repositories, if no specific repository
347is configured or a selected set of repositories. Multiple GitGarbageCollections
348can exist as part of the same GerritCluster, but no two GitGarbageCollections
349can work on the same project. This is prevented in three ways:
350
351- ValidationWebhooks will prohibit the creation of a second GitGarbageCollection
352 that does not specify projects, i.e. that would work on all projects.
353- Projects for which a GitGarbageCollections that specifically selects it exists
354 will be excluded from the GitGarbageCollection that works on all projects, if
355 it exists.
356- ValidationWebhooks will prohibit the creation of a GitGarbageCollection that
357 specifies a project that was already specified by another GitGarbageCollection.
358
359### Receiver
360
361**NOTE:** A Receiver should never be installed for a GerritCluster that is already
362managing a primary Gerrit to avoid conflicts when writing into repositories.
363
364The Receiver-CustomResource installs a Deployment running Apache with a git-http-
365backend that is meant to receive pushes performed by Gerrit's replication plugin.
366It can only be installed into a GerritCluster that does not include a primary
367Gerrit, but only Gerrit Replicas.
368
369The Receiver-CustomResource is mainly meant to be used by the GerritCluster-reconciler
370to install a Receiver-instance managed by a GerritCluster. Receiver-CustomResources
371can however also be applied separately. Note, that the Gerrit operator will then
372not create any storage resources or setup any network resources in addition to
373the service.
374
375### GerritNetwork
376
377The GerritNetwork CustomResource deploys network components depending on the
378configured ingress provider to enable ingress traffic to GerritCluster components.
379
380The GerritNetwork CustomResource is not meant to be installed manually, but will
381be created by the Gerrit Operator based on the GerritCluster CustomResource.
382
383## Configuration of Gerrit
384
385The operator takes care of all configuration in Gerrit that depends on the
386infrastructure, i.e. Kubernetes and the GerritCluster. This avoids duplicated
387configuration and misconfiguration.
388
389This means that some options in the gerrit.config are not allowed to be changed.
390If these values are set and are not matching the expected value, a ValidationWebhook
391will reject the resource creation/update. Thus, it is best to not set these values
392at all. To see which values the operator assigned check the ConfigMap created by
393the operator for the respective Gerrit.
394
395These options are:
396
397- `cache.directory`
398
399 This should stay in the volume mounted to contain the Gerrit site and will
400 thus be set to `cache`.
401
402- `container.javaHome`
403
404 This has to be set to `/usr/lib/jvm/java-11-openjdk-amd64`, since this is
405 the path of the Java installation in the container.
406
407- `container.javaOptions = -Djavax.net.ssl.trustStore`
408
409 The keystore will be mounted to `/var/gerrit/etc/keystore`.
410
411- `container.replica`
412
413 This has to be set in the Gerrit-CustomResource under `spec.isReplica`.
414
415- `container.user`
416
417 The technical user in the Gerrit container is called `gerrit`.
418
419- `gerrit.basePath`
420
421 The git repositories are mounted to `/var/gerrit/git` in the container.
422
423- `gerrit.canonicalWebUrl`
424
425 The canonical web URL has to be set to the hostname used by the Ingress/Istio.
426
427- `httpd.listenURL`
428
429 This has to be set to `proxy-http://*:8080/` or `proxy-https://*:8080`,
430 depending of TLS is enabled in the Ingress or not, otherwise the Jetty
431 servlet will run into an endless redirect loop.
432
433- `sshd.advertisedAddress`
434
435 This is only enforced, if Istio is enabled. It can be configured otherwise.
436
437- `sshd.listenAddress`
438
439 Since the container port for SSH is fixed, this will be set automatically.
440 If no SSH port is configured in the service, the SSHD is disabled.