| Giorgi Lekveishvili | 4ec4c02 | 2024-08-17 15:09:24 +0400 | [diff] [blame^] | 1 | # Gerrit Operator |
| 2 | |
| 3 | 1. [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 | |
| 26 | For this step, you need Java 11 and Maven installed. |
| 27 | |
| 28 | To build all components of the operator run: |
| 29 | |
| 30 | ```sh |
| 31 | cd operator |
| 32 | mvn clean install |
| 33 | ``` |
| 34 | |
| 35 | This step compiles the Java source code into `.class` bytecode files in a newly |
| 36 | generated `operator/target` folder. A `gerrit-operator` image is also created |
| 37 | locally. Moreover, the CRD helm chart is updated with the latest CRDs as part of |
| 38 | this build step. |
| 39 | |
| 40 | The jar-version and container image tag can be set using the `revision` property: |
| 41 | |
| 42 | ```sh |
| 43 | mvn clean install -Drevision=$(git describe --always --dirty) |
| 44 | ``` |
| 45 | |
| 46 | ## Versioning |
| 47 | |
| 48 | The Gerrit Operator is still in an early state of development. The operator is |
| 49 | thus at the moment not semantically versioned. The CustomResources are as of now |
| 50 | independently versioned, i.e. the `GerritCluster` resource can have a different |
| 51 | version than the `GitGarbageCollection` resource, although they are in the same |
| 52 | group. At the moment, only the current version will be supported by the operator, |
| 53 | i.e. there won't be a migration path. As soon as the API reaches some stability, |
| 54 | this will change. |
| 55 | |
| 56 | ## Publish |
| 57 | |
| 58 | Currently, 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 |
| 60 | the operator in your cluster. To publish the container image of the Gerrit |
| 61 | Operator: |
| 62 | |
| 63 | 1. Update the `docker.registry` and `docker.org` tags in the `operator/pom.xml` |
| 64 | file to point to your own Docker registry and org that you have permissions to |
| 65 | push to. |
| 66 | |
| 67 | ```xml |
| 68 | <docker.registry>my-registry</docker.registry> |
| 69 | <docker.org>my-org</docker.org> |
| 70 | ``` |
| 71 | |
| 72 | 2. run the following commands: |
| 73 | |
| 74 | ```sh |
| 75 | cd operator |
| 76 | mvn clean install -P publish |
| 77 | ``` |
| 78 | |
| 79 | This will build the operator source code, create an image out of the |
| 80 | built 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` |
| 82 | and `arm64` architectures. It is okay to run this build command from an ARM |
| 83 | Mac. |
| 84 | |
| 85 | ## Tests |
| 86 | |
| 87 | Executing the E2E tests has a few infrastructure requirements that have to be |
| 88 | provided: |
| 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 | |
| 108 | A 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 |
| 111 | components. |
| 112 | |
| 113 | In 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 | |
| 127 | The properties should be set in the `test.properties` file. Alternatively, a |
| 128 | path of a properties file can be configured by using the |
| 129 | `-Dproperties=<path to properties file>`-option. |
| 130 | |
| 131 | To run all E2E tests, use: |
| 132 | |
| 133 | ```sh |
| 134 | cd operator |
| 135 | mvn clean install -P integration-test -Dproperties=<path to properties file> |
| 136 | ``` |
| 137 | |
| 138 | Note, that running the E2E tests will also involve pushing the container image |
| 139 | to the repository configured in the properties file. |
| 140 | |
| 141 | ## Prerequisites |
| 142 | |
| 143 | Deploying Gerrit using the operator requires some additional prerequisites to be |
| 144 | fulfilled: |
| 145 | |
| 146 | ### Shared Storage (ReadWriteMany) |
| 147 | |
| 148 | Gerrit instances share the repositories and other data using shared volumes. Thus, |
| 149 | a StorageClass and a suitable provisioner have to be available in the cluster. |
| 150 | An 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 | |
| 155 | The Gerrit Operator will also set up network routing rules and an ingress point |
| 156 | for the Gerrit instances it manages. The network routing rules ensure that requests |
| 157 | will be routed to the intended GerritCluster component, e.g. in case a primary |
| 158 | Gerrit and a Gerrit Replica exist in the cluster, git fetch/clone requests will |
| 159 | be sent to the Gerrit Replica and all other requests to the primary Gerrit. |
| 160 | |
| 161 | You may specify the ingress provider by setting the `INGRESS` environment |
| 162 | variable in the operator Deployment manifest. That is, the choice of an ingress |
| 163 | provider is an operator-level setting. However, you may specify some ingress |
| 164 | configuration options (host, tls, etc) at the `GerritCluster` level, via |
| 165 | [GerritClusterIngressConfig](operator-api-reference.md#gerritclusteringressconfig). |
| 166 | |
| 167 | The 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 |
| 204 | You will need to have admin privileges for your k8s cluster in order to be able |
| 205 | to deploy the following resources. |
| 206 | |
| 207 | You may choose to deploy the operator resources using helm, or directly via |
| 208 | `kubectl apply`. |
| 209 | |
| 210 | ### Using helm charts |
| 211 | Make sure you have [helm](https://helm.sh/) installed in your environment. |
| 212 | |
| 213 | There are two relevant helm charts. |
| 214 | |
| 215 | #### gerrit-operator-crds |
| 216 | |
| 217 | This chart installs the CRDs (k8s API extensions) to your k8s cluster. No chart |
| 218 | values need to be modified. The build initiated by the `mvn install` command |
| 219 | from the [Publish](#publish) section includes a step that updates the CRDs in |
| 220 | this helm chart to reflect any changes made to them in the operator source code. |
| 221 | The CRDs installed are: GerritCluster, Gerrit, GitGarbageCollection, Receiver. |
| 222 | |
| 223 | You do not need to manually `helm install` this chart; this chart is installed |
| 224 | as a dependency of the second `gerrit-operator` helm chart as described in the |
| 225 | next subheading. |
| 226 | |
| 227 | #### gerrit-operator |
| 228 | |
| 229 | This chart installs the `gerrit-operator-crds` chart as a dependency, and the |
| 230 | following k8s resources: |
| 231 | - Deployment |
| 232 | - ServiceAccount |
| 233 | - ClusterRole |
| 234 | - ClusterRoleBinding |
| 235 | |
| 236 | The operator itself creates a Service resource and a |
| 237 | ValidationWebhookConfigurations resource behind the scenes. |
| 238 | |
| 239 | You will need to modify the values in `helm-charts/gerrit-operator/values.yaml` |
| 240 | to point the chart to the registry/org that is hosting the Docker container |
| 241 | image for the operator (from the [Publish](#publish) step earlier). Now, |
| 242 | |
| 243 | run: |
| 244 | ```sh |
| 245 | # Create a namespace for the gerrit-operator |
| 246 | kubectl create ns gerrit-operator |
| 247 | |
| 248 | # Build the gerrit-operator-crds chart and store it in the charts/ subdirectory |
| 249 | helm dependency build helm-charts/gerrit-operator/ |
| 250 | |
| 251 | # Install the gerrit-operator-crds chart and the gerrit-operator chart |
| 252 | helm -n gerrit-operator install gerrit-operator helm-charts/gerrit-operator/ |
| 253 | ``` |
| 254 | |
| 255 | The 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 | |
| 260 | First all CustomResourceDefinitions have to be deployed: |
| 261 | |
| 262 | ```sh |
| 263 | kubectl apply -f operator/target/classes/META-INF/fabric8/*-v1.yml |
| 264 | ``` |
| 265 | |
| 266 | Note that these do not include the -v1beta1.yaml files, as those are for old |
| 267 | Kubernetes versions. |
| 268 | |
| 269 | The operator requires a Java Keystore with a keypair inside to allow TLS |
| 270 | verification for Kubernetes Admission Webhooks. To create a keystore and |
| 271 | encode it with base64, run: |
| 272 | |
| 273 | ```sh |
| 274 | keytool \ |
| 275 | -genkeypair \ |
| 276 | -alias operator \ |
| 277 | -keystore keystore \ |
| 278 | -keyalg RSA \ |
| 279 | -keysize 2048 \ |
| 280 | -validity 3650 |
| 281 | cat keystore | base64 -b 0 |
| 282 | ``` |
| 283 | |
| 284 | Add the result to the Secret in `k8s/operator.yaml` (see comments in the file) |
| 285 | and also add the base64-encoded password for the keystore to the secret. |
| 286 | |
| 287 | Then the operator and associated RBAC rules can be deployed: |
| 288 | |
| 289 | ```sh |
| 290 | kubectl apply -f operator/k8s/rbac.yaml |
| 291 | kubectl apply -f operator/k8s/operator.yaml |
| 292 | ``` |
| 293 | |
| 294 | `k8s/operator.yaml` contains a basic deployment of the operator. Resources, |
| 295 | docker image name etc. might have to be adapted. For example, the ingress |
| 296 | provider has to be configured by setting the `INGRESS` environment variable |
| 297 | in `operator/k8s/operator.yaml` to either `NONE`, `INGRESS`, `ISTIO`, or |
| 298 | `AMBASSADOR`. |
| 299 | |
| 300 | ## CustomResources |
| 301 | |
| 302 | The operator manages several CustomResources that are described in more detail |
| 303 | below. |
| 304 | |
| 305 | The API reference for all CustomResources can be found [here](operator-api-reference.md). |
| 306 | |
| 307 | ### GerritCluster |
| 308 | |
| 309 | The GerritCluster CustomResource installs one or multiple Gerrit instances. The |
| 310 | operator takes over managing the state of all Gerrit instances within the cluster |
| 311 | and ensures that the state stays in sync. To this end it manages additional |
| 312 | resources that are shared between Gerrit instances or are required to synchronize |
| 313 | the state between Gerrit instances. These additional resources include: |
| 314 | |
| 315 | - storage |
| 316 | - network / service mesh |
| 317 | |
| 318 | Installing Gerrit with the GerritCluster resource is highly recommended over using |
| 319 | the [Gerrit](#gerrit) CustomResource directly, even if only a single deployment is |
| 320 | installed, since this reduces the requirements that have to be managed manually. |
| 321 | The same holds true for the [Receiver](#receiver) CustomResource, which without |
| 322 | a Gerrit instance using the same site provides little value. |
| 323 | |
| 324 | For now, only a single Gerrit CustomResource using each [mode](./operator-api-reference.md#gerritmode) |
| 325 | can be deployed in a GerritCluster, e.g. one primary Gerrit and one Gerrit Replica. |
| 326 | The reason for that is, that there is currently no sharding implemented and thus |
| 327 | multiple deployments don't bring any more value than just scaling the existing |
| 328 | deployment. Instead of a primary Gerrit also a Receiver can be installed. |
| 329 | |
| 330 | ### Gerrit |
| 331 | |
| 332 | The Gerrit CustomResource deploys a Gerrit, which can run in multiple modes. |
| 333 | |
| 334 | The Gerrit-CustomResource is mainly meant to be used by the GerritCluster-reconciler |
| 335 | to install Gerrit-instances managed by a GerritCluster. Gerrit-CustomResources |
| 336 | can however also be applied separately. Note, that the Gerrit operator will then |
| 337 | not create any storage resources or setup any network resources in addition to |
| 338 | the service. |
| 339 | |
| 340 | ### GitGarbageCollection |
| 341 | |
| 342 | The GitGarbageCollection-CustomResource is used by the operator to set up CronJobs |
| 343 | that regularly run Git garbage collection on the git repositories that are served |
| 344 | by a GerritCluster. |
| 345 | |
| 346 | A GitGarbageCollection can either handle all repositories, if no specific repository |
| 347 | is configured or a selected set of repositories. Multiple GitGarbageCollections |
| 348 | can exist as part of the same GerritCluster, but no two GitGarbageCollections |
| 349 | can 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 |
| 362 | managing a primary Gerrit to avoid conflicts when writing into repositories. |
| 363 | |
| 364 | The Receiver-CustomResource installs a Deployment running Apache with a git-http- |
| 365 | backend that is meant to receive pushes performed by Gerrit's replication plugin. |
| 366 | It can only be installed into a GerritCluster that does not include a primary |
| 367 | Gerrit, but only Gerrit Replicas. |
| 368 | |
| 369 | The Receiver-CustomResource is mainly meant to be used by the GerritCluster-reconciler |
| 370 | to install a Receiver-instance managed by a GerritCluster. Receiver-CustomResources |
| 371 | can however also be applied separately. Note, that the Gerrit operator will then |
| 372 | not create any storage resources or setup any network resources in addition to |
| 373 | the service. |
| 374 | |
| 375 | ### GerritNetwork |
| 376 | |
| 377 | The GerritNetwork CustomResource deploys network components depending on the |
| 378 | configured ingress provider to enable ingress traffic to GerritCluster components. |
| 379 | |
| 380 | The GerritNetwork CustomResource is not meant to be installed manually, but will |
| 381 | be created by the Gerrit Operator based on the GerritCluster CustomResource. |
| 382 | |
| 383 | ## Configuration of Gerrit |
| 384 | |
| 385 | The operator takes care of all configuration in Gerrit that depends on the |
| 386 | infrastructure, i.e. Kubernetes and the GerritCluster. This avoids duplicated |
| 387 | configuration and misconfiguration. |
| 388 | |
| 389 | This means that some options in the gerrit.config are not allowed to be changed. |
| 390 | If these values are set and are not matching the expected value, a ValidationWebhook |
| 391 | will reject the resource creation/update. Thus, it is best to not set these values |
| 392 | at all. To see which values the operator assigned check the ConfigMap created by |
| 393 | the operator for the respective Gerrit. |
| 394 | |
| 395 | These 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. |