# CI values for the keycloak install-test. # # WHY THIS CHART IS TESTED RATHER THAN EXCLUDED, and what a green run is worth. # Keycloak has no volume, needs one throwaway PostgreSQL and reaches Ready on # its own, so an exclusion would be hard to justify. More importantly, this is # the only place where the four riskiest assumptions of the chart are exercised # at all - none of them is observable in a render. # # WHAT A GREEN RUN PROVES, point by point (the measurement points of Gitea #83): # # 1. THE TWO INIT CONTAINERS WORK AT ALL. The seed container copies the # image's own augmentation output into the volume, the build container # mounts that volume at /opt/keycloak/lib/quarkus and runs `kc.sh build` # into it - both with readOnlyRootFilesystem: true. If the seeding # assumption were wrong (QuarkusEntryPoint loads # quarkus/quarkus-application.dat before it dispatches any command, # `build` included), the build container could not start, and the pod # would sit in Init with the reason only in its log. # 2. readOnlyRootFilesystem: true HOLDS FOR THE SERVING CONTAINER, with # emptyDirs only at /opt/keycloak/lib/quarkus, /opt/keycloak/data and # /tmp - and NOT at /opt/keycloak/conf, which would hide the # cache-ispn.xml that cache=ispn reads. # 3. `start --optimized` ACCEPTS THE PRE-BUILT INSTALLATION and does not # re-augment. A disagreement between the build options and the serving # container would show up here as a crash loop rather than as a slow # start, because the path it would want to rewrite is read-only. # 4. TWO REPLICAS BOTH COME UP AND STAY UP against one database - the # rollout is complete only when BOTH pods are Ready, so a second replica # that crashed, deadlocked on the schema migration lock or fought the # first over the database would fail the run. Measured in Gitea PR #87: # 71.8s from `helm install` to both Ready, 0 restarts - three runs, all # three 72s and 0 restarts. # THE FOURTH RUN WAS NOT THAT, AND THE NUMBER IS RECORDED RATHER THAN # SMOOTHED (Gitea PR #92, run 585): 101s, and ONE restart on one of the two # pods. The same run was measurably slower everywhere - the UNCHANGED # postgres fixture took 51.5s to roll out instead of 27.4s - so the runner # was about half as fast, and this chart's first install is where that # shows: two replicas serialize on the Liquibase migration lock, which is # the whole reason settings.env budgets 900s. WHAT THE RESTART WAS HAS NOT # BEEN ESTABLISHED, because the harness dumps container logs only when a # run FAILS, and this one passed - which is the lasting finding here: the # green path cannot explain a restart it reports. # WHAT DID SETTLE THE QUESTION WAS A SECOND RUN OF THE IDENTICAL MANIFESTS # (run 590): 82s, 0 restarts, postgres back to 23.4s. Same chart, same # theme mount, fast runner - so the restart belongs to the environment and # not to the pod spec. Keep both numbers: "0 restarts" is an observation # about four runs on a runner of varying speed, not an invariant of the # chart, and a first install that serializes two replicas on a schema # migration lock is exactly the shape that gets slower-than-linearly worse # on a loaded machine. # 5. A MOUNTED LOGIN THEME SURVIVES ALL OF THAT (Gitea #88) - the full # argument sits at keycloak.extraVolumes further down, including what it # still does not prove. # Plus the mechanical half: the Secret key names match what the Deployment # references, /health/started, /health/live and /health/ready all answer on # the management port (so health-enabled really was baked in by the init # container), the resource quantities are valid, and the schema accepts a # realistic non-default values file. # # WHAT IT DOES NOT PROVE, and the first entry is the one not to misread: # - THAT jdbc-ping ACTUALLY FOUND THE PEER. Two replicas both reaching Ready # is NOT that proof, and believing it was is the mistake this paragraph # exists to prevent: readiness reports this node's own health, so a node # that never found its peer is Ready, healthy and alone - and two such # nodes are two islands that each believe they are the cluster. The # Infinispan view has to be read out of the log, which is what step 2 of # NOTES.txt tells the operator to do. So measurement point 4 of the plan # stays open after this test; what the test does rule out is the louder # half (a second replica that cannot start or cannot share the database). # - that hostname-admin serves the console cleanly on a second hostname. No # ingress controller answers in CI and nothing drives a browser, so # measurement point 5 stays open too. What IS checked is that both Ingress # objects are accepted, that the Service port they name exists, and that # Keycloak accepts both hostname values as full URLs at start. # - anything about an upgrade. The rolling-update restriction between minor # versions is a property of two versions meeting, and this test installs # one. # - that the PostgreSQL here resembles production. It is one pod with an # emptyDir, so nothing about durability, connection limits or latency # transfers. secrets: # Materialized by ci/keycloak/fixtures.yaml as ci-keycloak-db-secret and # ci-keycloak-bootstrap-admin-secret (the OnePasswordItem stub CRD comes from # ci/common/). databaseSecretRef: op://ci/keycloak-db bootstrapAdminSecretRef: op://ci/keycloak-bootstrap-admin keycloak: # TWO, i.e. the chart's default, and the whole reason this fixture is not a # one-replica shortcut. With one replica the install-test would be green # without ever showing whether the second node is found - and "a path the # test does not walk is a path we have not proven" is the lesson Gitea #80 # paid for. The install-test script waits for the Deployment's rollout, and a # rollout is only complete when BOTH pods are Ready, so cluster formation is # inside the assertion rather than next to it. replicas: 2 db: # The throwaway PostgreSQL from fixtures.yaml. Plain Service name: both are # in the same namespace. host: postgres database: keycloak # Small but not degenerate: 2 replicas x 5 = 10 connections against # postgres:16-alpine's default max_connections of 100, which leaves the # fixture comfortable while still exercising the pool options. pool: initialSize: 1 minSize: 1 maxSize: 5 # The chart default, spelled out because it is load-bearing for what this # test means: metrics-enabled is what puts the DATABASE check into # /health/ready (upstream's check table, "Requires Metrics: Yes"). With it # off, readiness would pass without ever touching the database, and a green # run would say nothing about the connection at all. # # It is also a BUILD option, so with it true the init container has to build # an installation differing from the published image in three options rather # than two - which is what makes a build that silently did nothing fail the # run instead of looking identical. metrics: enabled: true # Set on purpose, although one release cannot collide with itself: it # exercises the KC_CACHE_EMBEDDED_CLUSTER_NAME path, and a name Keycloak # rejected would fail the start rather than pass unnoticed. cache: clusterName: ci-keycloak resources: # Below the chart's defaults to stay inside the runner's budget, and the # memory number is chosen rather than rounded: the image sizes the heap at # 70% of the LIMIT, so 600Mi x limitFactor 3 = 1800Mi gives a ~1260Mi heap. # Upstream's floor is "at least 750 MB" for the limit; going much below # this makes the JVM the reason a run fails. requests: cpu: 250m memory: 600Mi ephemeral-storage: 256Mi build: resources: requests: cpu: 250m memory: 512Mi ephemeral-storage: 256Mi # THE EXTRA VOLUME PAIR, WALKED RATHER THAN RENDERED (Gitea #88). The theme # ConfigMap comes from fixtures.yaml. Three things this puts inside the # assertion that no render reaches: # # 1. A MOUNT ON A SUBPATH OF A DIRECTORY THAT EXISTS IN THE IMAGE IS # ACCEPTED ALONGSIDE readOnlyRootFilesystem: true. # /opt/keycloak/themes/ci-theme does not exist in the image, so the # runtime has to create the mount point inside a root filesystem that is # about to be read-only. If that did not work, every container of this # pod would fail to start. # 2. `kc.sh build` STILL SUCCEEDS WITH THE EXTRA MOUNT PRESENT. The mounts # are rendered on the build init container too (that is what makes SPI # providers work at all), so this fixture is also the test that the extra # mount does not disturb the augmentation - a failure would leave the pod # in Init with the reason only in a log. # 3. `start --optimized` COMES UP WITH A RUNTIME THEME IN PLACE. The plan of # #88 called that "a reasoned expectation, not verified"; a Ready pod with # this mounted is the verification, and it is the one that matters for the # unusual two-init-container construction of this chart. # # WHAT IT STILL DOES NOT PROVE, and it would be easy to overclaim here: that # the LOGIN PAGE renders and that base/keycloak/keycloak.v2 are still listed. # The health endpoints answer regardless of the themes directory, and nothing # in this harness makes an HTTP request to / or runs a command in the pod. That # the subpath hides nothing is a property of the mount path rather than a # measurement - and the kcLogoIdP line needs a browser plus a realm setting, # neither of which exists in CI. extraVolumes: - name: login-theme configMap: name: ci-keycloak-theme items: - key: theme.properties path: login/theme.properties extraVolumeMounts: - name: login-theme # THE SUBPATH, AND THE WHOLE POINT OF THE EXAMPLE: a volume on # /opt/keycloak/themes would hide the image's own three themes and break # the login page while the pod stayed Ready. mountPath: /opt/keycloak/themes/ci-theme readOnly: true ingress: # Left ON although no ingress controller answers in CI: the point is that # both objects are accepted and that the Service port they name BY NAME # ("http") actually exists - a typo there is otherwise invisible until # somebody opens the URL. enabled: true domain: keycloak.ci.example.com clusterIssuer: ci-selfsigned admin: # The second hostname, on as well, so the chart renders KC_HOSTNAME_ADMIN # and Keycloak has to accept it as a full URL. That is the half of # measurement point 5 a cluster-less test can reach: Keycloak validates # both hostname values at start ("verified that full URL is used, incl. # scheme and hostname"), so a chart that built a bare host here would fail # the start instead of serving a broken console later. enabled: true domain: keycloak-admin.ci.example.com