Skip to content

feat: customization - #160

Merged
alix-graylog merged 4 commits into
mainfrom
feature/extendable
Aug 4, 2026
Merged

feat: customization#160
alix-graylog merged 4 commits into
mainfrom
feature/extendable

Conversation

@alix-graylog

@alix-graylog alix-graylog commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

Lets users adopt the chart into an existing ecosystem without forking it. Every rendered object accepts custom labels and annotations, both workloads accept arbitrary volumes and containers, pods can be spread across failure domains, and anything the chart does not model can be deployed alongside it via extraObjects.

Details

Labels and annotations on every object

Three helpers in _helpers.tplgraylog.metadata.labels, graylog.metadata.annotations and graylog.pod.labels — render the metadata block for every object. Each merges, lowest to highest precedence: global.commonLabels / global.commonAnnotations, the object's own labels / annotations, then the chart-owned identity labels and Helm hook/resource-policy annotations, which always win.

All 33 rendered objects are covered, including the MongoDBCommunity CR, the external-OpenSearch Secret, the Helm test Pods and the waiting-room objects.

Extra volumes, mounts and containers

extraVolumes, extraVolumeMounts, extraInitContainers and extraContainers on both workloads, plus extraInitVolumeMounts for the chart's copy-data init container. The Datanode's volumes: block was previously omitted entirely when both persistent volumes were enabled, so it was restructured to always render when extra volumes are present.

Pod scheduling and runtime

topologySpreadConstraints, priorityClassName, schedulerName, runtimeClassName, dnsPolicy, dnsConfig and hostAliases on both workloads.

topologySpreadConstraints is the substantive one. The chart's existing anti-affinity is a soft hostname preference — it does not guarantee a spread across zones, so a single-zone failure could take every Data Node replica with it. There was previously no way to express a real multi-AZ layout.

extraObjects

A top-level list rendered through tpl, so entries can reference the release context. Mapping and string forms both work — the string form is what you need when the manifest itself contains Helm syntax. global.commonLabels / commonAnnotations are merged in; anything set on the object wins. An entry that renders without a kind fails the render with an actionable message rather than emitting an invalid manifest.

Lifecycle hooks

graylog.lifecycle.postStart / preStop for user-supplied hooks, alongside the chart-managed preStopDrain from #147. Setting preStop together with preStopDrain.enabled fails the render, because a container can only have one preStop hook and silently dropping either would be worse.

Truststore handling in the init container

The BYO Graylog certificate and the BYO OpenSearch CA each built the Java truststore their own way, and the two paths disagreed about where the JDK bundle lives — the OpenSearch path read ${JAVA_HOME} while the Graylog path derived it by running java. They are now one build, gated by a graylog.truststore.enabled helper, always seeded from the JDK bundle in the image so the default CAs survive and our certificates are only ever added on top.

Two defects fell out of that:

  • The lookup could not work in this init container at all. java in the Graylog image carries a file capability (cap_net_bind_service=ep), and the init container runs allowPrivilegeEscalation: false, which sets no_new_privs. The kernel refuses to execve a file with capabilities under that flag, so the JVM died with Operation not permitted (exit 126) and the derived path came back empty. On main that path is unchecked (init-graylog.yaml:78): cp "${JAVA_HOME_LOCAL}/lib/security/cacerts" degrades to cp /lib/security/cacerts, fails silently, and keytool then creates a fresh keystore holding only the BYO certificate. Since the chart passes -Djavax.net.ssl.trustStore=/usr/share/graylog/data/cacerts/graylog.jks to the JVM, that becomes Graylog's entire trust anchor set — every outbound TLS connection to anything not signed by that one certificate would fail. The lookup now reads ${JAVA_HOME} with a fallback across known JDK paths and never starts a JVM; keytool has no file capability and is unaffected.
  • A custom keyStorePass could never be applied. The JDK bundle ships as changeit, so importing with a different -storepass failed outright. The seeded copy is now re-keyed first.

The store is also only moved into place when its content actually changed, and keyStorePass is single-quoted so a password with shell metacharacters is safe.

Incidental fixes

  • values.schema.json still described the pre-feat: Safe Graylog Journal Draining #147 flat ingress.forwarder.* shape. Rewritten to the two-channel form with a shared $defs/forwarderChannel.
  • Per-channel labels added to the forwarder Ingresses (they already had annotations).
  • datanode.persistence.data accepted no annotations/labels, even though the template already read them and its sibling nativeLibs claim exposed them. Gap closed, so all four claim templates behave the same way.
  • mongodb.users was still declared in values.schema.json after the value itself was dropped. Removed.
  • External OpenSearch credentials are URL-encoded before being interpolated into the connection URI, so a password containing @, /, : or ? no longer splits it at the wrong place.
  • Empty annotations: and initContainers: keys are no longer emitted as bare nulls.

Breaking changes

Verified there are no breaking changes to immutable objects.

PR Checklist

  • Tests added/updated
  • Documentation updated
  • This PR includes a new feature
  • This PR includes a bugfix
  • This PR includes a refactor

Testing Checklist

Static Validation

  • Linter check passes: helm lint ./charts/graylog
  • Helm renders local template sucessfully: helm template graylog ./charts/graylog --validate

Installation

  • Fresh installation completes successfully: helm install graylog ./charts/graylog
  • All pods reach Running state: kubectl rollout status statefulset/graylog
  • Helm tests pass: helm test graylog

Functional (if applicable)

  • Web UI accessible and login works
  • DataNodes visible in System > Cluster Configuration
  • Inputs can be created and receive data

Upgrade (if applicable)

  • Upgrade from previous release succeeds — see the caveat below
  • Scaling up/down works correctly
  • Configuration changes apply correctly

Specific to this PR

  • helm unittest ./charts/graylog — 330 tests across 28 suites
  • Rendered origin/main and this branch with identical values and diffed the output object-by-object; every difference is accounted for above
  • Immutability suite mutation-tested both ways (selector leak → 4 of 9 fail; claim-metadata helper swapped out → 3 of 9 fail; both clean on revert)
  • Init-container truststore lookup executed inside a pod carrying the real init container's securityContext, uid and envFrom
  • MongoDB claim-metadata propagation confirmed against the live operator
  • Renders verified across topologies: defaults, external MongoDB, BYO OpenSearch, ingress + forwarder + cert-manager issuer, AWS provider, GeoIP sidecar, preStop drain, existingSecretName, nativeLibs.enabled
  • Deployed to graylog-helm-1 on glc-central-dev with BYO OpenSearch; Graylog pods reached Running through the rebuilt init container
  • Upgrade from a release installed off current main (render-diff clean; the live upgrade that was run started from a polluted baseline, see above)
  • extraObjects applied against a live cluster with a real CRD (e.g. a ServiceMonitor) — only render-tested so far

Notes for reviewers

  • Verify all applicable tests above pass
  • Validate that the linked issues are no longer reproducible, if applicable
  • Sync up with the author before merging
  • The commit history should be preserved - use rebase-merge or standard merge options when applicable

Two conflict resolutions during the rebase onto #147 are worth a look, since both were judgement calls rather than mechanical merges:

  • templates/service/ingress/graylog-forwarder.yamlfeat: Safe Graylog Journal Draining #147 rewrote this into one Ingress per gRPC channel. The metadata helpers were moved to per-channel and a labels key added to each channel, rather than keeping a flat ingress.forwarder.labels.
  • templates/workload/statefulsets/graylog.yaml — kept a kindIs "invalid" guard for terminationGracePeriodSeconds in preference to with, since with skips a legitimate value of 0.

Known issue, pre-existing, not addressed here

A numeric MaxMind account ID (--set graylog.config.geolocation.maxmindGeoIp.accountId=1, unquoted) fails with b64enc: wrong type for value; expected string; got int64. Reproduced on origin/main, so it is not a regression from this PR. Workaround is --set-string. Tracked separately.

@alix-graylog alix-graylog self-assigned this Aug 3, 2026
@alix-graylog alix-graylog added documentation Improvements or additions to documentation feature New feature. labels Aug 3, 2026
@alix-graylog
alix-graylog marked this pull request as ready for review August 4, 2026 01:49
keytool -storepasswd -keystore "${CACERTS_TMP}" -storepass changeit -new "${TRUSTSTORE_PASS}" || { echo "Error: failed to set the truststore password."; exit 1; }
fi
{{- if and .Values.graylog.config.tls.enabled .Values.graylog.config.tls.updateKeyStore }}
keytool -importcert -noprompt -alias byoc -file "/mnt/tls/tls.crt" -keystore "${CACERTS_TMP}" -storepass "${TRUSTSTORE_PASS}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May be worth adding a || { echo "Error: failed to import the custom cert CA."; exit 1; } guard here. If invalid, this keystore might cause a broken truststore

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a great idea, that should be caught early! I'll implement that in this PR if possible.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 2539414. Both the byoc and byoc-ca imports now fail the init container, matching the OpenSearch CA import that already had a guard.

Comment on lines 380 to 399
DISABLED 2026-07-30 -- the settings are inert: the forwarder listener is
configured as attributes on a Graylog input of type "Forwarder", not through
server.conf, and unrecognised GRAYLOG_* env vars are silently dropped.
graylog.config.forwarder was removed from values.yaml, so this helper reads a
path that no longer exists -- re-add those values before restoring it.

Whether the Graylog server should listen for forwarder connections.
Defaults to ingress.forwarder.enabled so that exposing the ingest endpoint also
binds the ports behind it; set graylog.config.forwarder.enabled explicitly to
override (e.g. to bind the ports without creating an Ingress).

{{- define "graylog.forwarder.enabled" -}}
{{- $configured := .Values.graylog.config.forwarder.enabled -}}
{{- if kindIs "bool" $configured -}}
{{- $configured -}}
{{- else -}}
{{- .Values.ingress.forwarder.enabled | ternary true false -}}
{{- end -}}
{{- end }}
*/}}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May be worth removing if deprecated.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, not entirely sure what the api sunset strategy should be yet, but taking this into consideration!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving this one for now. It's a commented-out block, so there's no render impact either way, and the note above it is the part that has value. The deprecation warning in the templates will help for the next api breaking change release of the chart.

Sourced from graylog.inputs, so it must stay in sync with that entry's name.
*/}}
{{- define "graylog.service.port.forwarder.message" -}}
{{- print "input-forwarder" }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not positive this is an issue but there may be a potential gap if someone renames the forwarder. Apologies if just missing context!

Eg.

  graylog:
    inputs:
      - name: my-forwarder        # renamed from "input-forwarder"
         port: 13301
        targetPort: 13301
        protocol: TCP

Service might be named my-forwarder but ingress would still look for input-forwarder

potential fix:

  {{/*
  Graylog service port name for the forwarder message channel (default 13301).
  Sourced from graylog.inputs, so it must stay in sync with that entry's name.
  */}}
  {{- define "graylog.service.port.forwarder.message" -}}
  {{- $entry := "" -}}
  {{- range .Values.graylog.inputs -}}
    {{- if eq (int .port) 13301 -}}{{- $entry = .name -}}{{- end -}}
  {{- end -}}
  {{- if not $entry -}}
    {{- fail "No entry in graylog.inputs with port 13301 — the forwarder message channel Ingress has nothing to bind to. Add an input with port: 13301 or disable ingress.forwarder.messageChannel." -}}
  {{- end -}}
  {{- $entry -}}
  {{- end }}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for that catch! These issues, while real are not related to this effort. But I'm working on resolution to this in a different branch.

@aaronmgn
aaronmgn self-requested a review August 4, 2026 02:59
@alix-graylog
alix-graylog merged commit 29c1430 into main Aug 4, 2026
9 checks passed
@alix-graylog
alix-graylog deleted the feature/extendable branch August 4, 2026 16:34
alix-graylog added a commit that referenced this pull request Aug 27, 2026
No commit in the 1.0.0 to 2.0.0 window carried a breaking-change footer, so
release-please had no breaking-changes section to generate. This adds the 16
found by rendering the chart at both versions and diffing resource names,
StatefulSet immutable fields, security contexts and values keys.

Also adds the mongodb entry from #176, which the parser dropped, corrects
"starup" to "startup" in the #169 entry, and gives #160 a description of what
it actually does.

The publish job generates artifacthub.io/changes from this file, so these
notes reach Artifact Hub as written.
alix-graylog added a commit that referenced this pull request Aug 27, 2026
* chore(main): release graylog 2.0.0

* chore(main): write the 2.0.0 breaking changes into the changelog

No commit in the 1.0.0 to 2.0.0 window carried a breaking-change footer, so
release-please had no breaking-changes section to generate. This adds the 16
found by rendering the chart at both versions and diffing resource names,
StatefulSet immutable fields, security contexts and values keys.

Also adds the mongodb entry from #176, which the parser dropped, corrects
"starup" to "startup" in the #169 entry, and gives #160 a description of what
it actually does.

The publish job generates artifacthub.io/changes from this file, so these
notes reach Artifact Hub as written.

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: alix-graylog <alix.fullerton@graylog.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation feature New feature.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants