# 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. # THAT WAS LUCK, OR A FAST WEEK, AND NOT AN INVARIANT - which took an A/B # inside Gitea PR #92 to establish, because the first five runs there made # it look like the new theme mount had destabilized the start: # WITH the theme mount (spec of #88) # run 585: 101s, ONE restart on one pod # run 590: 82s, 0 # run 593: 191s, 0 # run 595: 101s, ONE # run 597: 111s, ONE -> 3 of 5 # WITHOUT it (the same branch, the pair commented out; pod template # hash 5844db56b6, i.e. BYTE-IDENTICAL to the spec PR #87 measured) # run 598: 81s, 0 # run 599: 171s, ONE -> 1 of 2 # So the restart happens WITHOUT the mount as well, and #88 is not its # cause. It is pre-existing, intermittent behaviour of THIS chart's first # install: always exactly one restart on one pod, 35-80s before that pod # reports Ready. It does not track the runner's speed either - the fastest # (81s) and the slowest (191s) run of the whole series restarted nothing. # WHAT THE RESTART IS HAS NOT BEEN ESTABLISHED, and that is the finding # worth keeping: the harness dumps container logs only when a run FAILS # (diagnose() hangs off the EXIT trap behind "RC -ne 0") and all seven runs # PASSED - A GREEN RUN CANNOT EXPLAIN A RESTART IT REPORTS. Answering it # means making the final-state step print the restart reasons whenever a # counter is above zero; that is a change to ci/run-install-test.sh with a # CI-CD wiki obligation of its own, and it was deliberately NOT made inside # #88. A first install that serializes two replicas on the Liquibase # migration lock is a plausible place for it - stated as the hypothesis it # is, because nothing here measured it. # THE METHOD IS THE PART TO REUSE: a counter that moved in the same round # as a change is not evidence against the change until the control case has # run on the SAME machine. Three clean runs two days earlier were not that # control, and treating them as one would have cost this PR its conclusion # in both directions. # 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