Skip to content

feat: Add custom search attributes support to TemporalNamespace - #984

Merged
alexandrevilain merged 6 commits into
alexandrevilain:mainfrom
ido-trumer:feat/custom-search-attributes
Mar 29, 2026
Merged

alexandrevilain merged 6 commits into
alexandrevilain:mainfrom
ido-trumer:feat/custom-search-attributes

Conversation

@ido-trumer

@ido-trumer ido-trumer commented Mar 16, 2026 •

Copy link
Copy Markdown
Contributor

Closes #567
Supersedes #776

Summary

Adds the ability to declare custom search attributes on TemporalNamespace resources. Attributes are reconciled against the Temporal server via the OperatorService API during each namespace reconciliation cycle.

New spec fields

apiVersion: temporal.io/v1beta1
kind: TemporalNamespace
metadata:
  name: default
spec:
  clusterRef:
    name: temporal
  retentionPeriod: 336h
  customSearchAttributes:
    CustomerId: Keyword
    OrderTotal: Double
    IsVIP: Bool
  allowSearchAttributeDeletion: false
  • customSearchAttributes — map of attribute name → type. Supported types: Text, Keyword, Int, Double, Bool, DateTime, KeywordList.
  • allowSearchAttributeDeletion — safety flag. When false (default), attributes removed from the spec are left on the server. When true, stale attributes are deleted.

Reconciliation behavior

  1. Lists existing custom attributes from the Temporal server
  2. Detects type mismatches (returns error — Temporal does not allow type changes)
  3. Adds new attributes not yet on the server
  4. Removes stale attributes only if allowSearchAttributeDeletion: true

Files changed

  • api/v1beta1/temporalnamespace_types.go — two new fields on TemporalNamespaceSpec
  • api/v1beta1/zz_generated.deepcopy.go — generated
  • config/crd/bases/temporal.io_temporalnamespaces.yaml — generated CRD
  • controllers/temporalnamespace_controller.go — +15 lines to call ReconcileSearchAttributes after namespace register/update
  • pkg/temporal/search_attributes.go — core reconciliation logic with OperatorServiceClient interface for testability
  • pkg/temporal/search_attributes_test.go — 11 unit test cases

Difference from #776

This is a fresh implementation, not a continuation of #776. Key differences:

#776 This PR
Controller changes +166 lines (inline logic) +15 lines (delegates to pkg/temporal)
Unit tests None 11 test cases
Safe deletion flag No (allowSearchAttributeDeletion) Yes
Type mismatch handling None Explicit error
Testable via interface No Yes (OperatorServiceClient)

Note on test location

Unit tests are co-located at pkg/temporal/search_attributes_test.go. While the other files in pkg/temporal/ (e.g., namespace.go, client.go) don't have co-located unit tests, this file uses a mock OperatorServiceClient interface and doesn't require a live cluster, so it doesn't fit in tests/e2e/. The co-located pattern is consistent with other packages in the repo (pkg/version/, pkg/kubernetes/, internal/resource/persistence/). Happy to move it if you prefer a different location.

Test plan

  • Unit tests pass (go test ./pkg/temporal/... — 11 search attribute test cases)
  • go vet clean
  • golangci-lint clean (v1.64.8, matching CI)
  • License check clean (make check-license)
  • Full project compiles (go build ./...)
  • Manual e2e on minikube: deployed operator with PostgreSQL-backed TemporalCluster, verified attributes appear on server via ListSearchAttributes API and Temporal UI
    • Create namespace with 4 attributes → all 4 present
    • Add 2 more → 6 total
    • Remove 4 with allowSearchAttributeDeletion: true → 2 remain
    • Remove attribute with allowSearchAttributeDeletion: false → preserved on server

Adds the ability to configure custom search attributes on TemporalNamespace
resources, enabling workflow search by custom attributes. Attributes are
reconciled via the Temporal OperatorService API during namespace reconciliation.

Closes alexandrevilain#567
@ido-trumer ido-trumer changed the title feat: Add custom search attributes support to TemporalNamespace [WIP] feat: Add custom search attributes support to TemporalNamespace Mar 16, 2026
Reduce cognitive complexity of ReconcileSearchAttributes by extracting
helper functions. Replace duplicated test namespace literal with constant
and fix gofmt/unparam lint issues.
Log key lifecycle events: existing attribute count, when attributes are
up to date, additions with count, and removals with count and names.
Only log at INFO when actually adding or removing attributes.
Listing and up-to-date checks are debug (V(1)) to reduce noise.
Match namespace.go's import style (unaliased enums instead of enumsv1).
Replace init() with a static map literal for the reverse type mapping.
@ido-trumer ido-trumer changed the title [WIP] feat: Add custom search attributes support to TemporalNamespace feat: Add custom search attributes support to TemporalNamespace Mar 16, 2026
Remove TestSearchAttributeTypeFromString, TestSearchAttributeTypeToString
(just mirror the map data), and error propagation tests (just verify
fmt.Errorf wrapping). Keep tests that exercise actual reconciliation logic.
@sonarqubecloud

Copy link
Copy Markdown

@ido-trumer

Copy link
Copy Markdown
Contributor Author

@alexandrevilain Thank you for triggering the CI, looks like everything works. Let me know if you have any comments or other thoughts :)

@eliavra

eliavra commented Mar 26, 2026

Copy link
Copy Markdown

@alexandrevilain
hey is there any option that we can move it forward
this feature is highly anticipated

@alexandrevilain
alexandrevilain merged commit 1398896 into alexandrevilain:main Mar 29, 2026
10 checks passed
@alexandrevilain

Copy link
Copy Markdown
Owner

Thanks for the contribution @ido-trumer !

ElanHasson added a commit to eh-homelab/temporal-operator that referenced this pull request Oct 27, 2026
…in#987, plus 1.32)

Squashes alexandrevilain#987 (head 97a684d, by
CrunchyMonkies, with cherry-picked fixes by johnduhart): support for
Temporal 1.29, 1.30 and 1.31, including 1.30's removal of dockerize from
the server image and of curl/jq from admin-tools; Go 1.26.4,
go.temporal.io/server v1.31.1, controller-runtime v0.23.3, controller-gen
v0.21.0, kustomize v5.8.1. It also brings upstream main's custom search
attributes on TemporalNamespace (alexandrevilain#984), which alexandrevilain#987 is based on.

On top:
- Supported range raised to < 1.33.0. 1.32 changes no image contents or
  static config format; its breaking changes are dynamic config and APIs.
- TestValidateSupportedRange pins both range ends and the sequential path
  1.28.4 -> 1.29.7 -> 1.30.8 -> 1.31.4 -> 1.32.1.
- The upstream-golden config test now allows exactly three new lines, the
  empty values of fields go.temporal.io/server v1.31 added
  (sql.passwordCommand, authorization.audience/permissionsRegex). Temporal
  < 1.31 loads its config with plain yaml.Unmarshal and ignores them; they
  do roll the server pods once when the operator is swapped.
- Bundle CRDs copied from config/crd/bases: alexandrevilain#987's operator-sdk pin no
  longer supports this project's go.kubebuilder.io/v3 layout, so
  `make bundle` fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149PKDkbRQfDuVf5UU6vuPZ
ElanHasson added a commit to eh-homelab/temporal-operator that referenced this pull request Oct 27, 2026
… images (#1)

* feat: optional clusterMetadata for multi-cluster replication

Adds spec.clusterMetadata to TemporalCluster: enableGlobalNamespace,
initialFailoverVersion (1-9, default 1) and dcRedirectionPolicy. Unset, the
rendered server config is what v0.22.0 renders. Once global namespaces are
enabled, the webhook makes initialFailoverVersion immutable.

Also points the release manifest at ghcr.io/eh-homelab/temporal-operator and
adds eh-release.yaml, which builds and pushes the image and publishes the CRD
and operator manifests on a v*-eh.* tag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149PKDkbRQfDuVf5UU6vuPZ

* fix: lock global-namespace settings once enabled; verify the image is public

Temporal 1.28 persists enableGlobalNamespace and initialFailoverVersion when
global namespaces are first enabled, then ignores config changes to either
(temporal/fx.go, updateCurrentClusterMetadataRecord and
overwriteCurrentClusterMetadataWithDBRecord). The webhook now forbids
disabling global namespaces or changing the version after that point, so the
CR cannot drift from the running cluster.

- Nil-safe ClusterMetadataSpec helpers and a FailoverVersionIncrement
  constant shared by the builder and the webhook.
- Five ValidateUpdate cases for the new rules.
- Release: set the image with kustomize edit instead of sed, and fail if
  the pushed image is not anonymously pullable (a new GHCR package is private).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149PKDkbRQfDuVf5UU6vuPZ

* test: pin the unset config to upstream v0.22.0; regenerate chart and bundle CRDs

- TestConfigmapBuilderMatchesUpstream renders a scadbuddy-shaped cluster
  without clusterMetadata and compares it byte for byte with a golden file
  generated by upstream v0.22.0's own builder.
- TestConfigmapBuilderClusterMetadata covers the rendered clusterMetadata and
  dcRedirectionPolicy when the block is set.
- make helm bundle: the chart and OLM bundle CRDs now carry
  spec.clusterMetadata. The chart's copy had been left at v0.20.0's CRDs
  upstream, so it also gains the fields added since.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149PKDkbRQfDuVf5UU6vuPZ

* feat: support Temporal 1.29 to 1.32 (squash of upstream alexandrevilain#987, plus 1.32)

Squashes alexandrevilain#987 (head 97a684d, by
CrunchyMonkies, with cherry-picked fixes by johnduhart): support for
Temporal 1.29, 1.30 and 1.31, including 1.30's removal of dockerize from
the server image and of curl/jq from admin-tools; Go 1.26.4,
go.temporal.io/server v1.31.1, controller-runtime v0.23.3, controller-gen
v0.21.0, kustomize v5.8.1. It also brings upstream main's custom search
attributes on TemporalNamespace (alexandrevilain#984), which alexandrevilain#987 is based on.

On top:
- Supported range raised to < 1.33.0. 1.32 changes no image contents or
  static config format; its breaking changes are dynamic config and APIs.
- TestValidateSupportedRange pins both range ends and the sequential path
  1.28.4 -> 1.29.7 -> 1.30.8 -> 1.31.4 -> 1.32.1.
- The upstream-golden config test now allows exactly three new lines, the
  empty values of fields go.temporal.io/server v1.31 added
  (sql.passwordCommand, authorization.audience/permissionsRegex). Temporal
  < 1.31 loads its config with plain yaml.Unmarshal and ignores them; they
  do roll the server pods once when the operator is swapped.
- Bundle CRDs copied from config/crd/bases: alexandrevilain#987's operator-sdk pin no
  longer supports this project's go.kubebuilder.io/v3 layout, so
  `make bundle` fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149PKDkbRQfDuVf5UU6vuPZ

* feat: accept fully pinned image references, so Dependabot can bump them

Dependabot's docker parser reads only `image: name:tag` values, and the CR
split every image into `image` plus a separate version, while the schema
jobs computed their admin-tools tag inside the operator. Now:

- spec.image may carry a tag (and digest). The defaulting webhook sets
  spec.version from the tag, so a Dependabot tag bump moves the version and
  the schema migrations with it; the sequential-upgrade check still applies.
  A tag that is not a version is rejected.
- spec.admintools.image may be pinned (e.g. 1.28@sha256:...) and the schema
  jobs use it as written. Its tag must be for the server's major.minor,
  since those jobs apply the schema the image carries.
- spec.ui.image may be pinned.
- Untagged images behave as before.
- .github/dependabot.yml for the fork's Dockerfile and Actions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149PKDkbRQfDuVf5UU6vuPZ

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Custom Search Attributes to TemporalNamespace

3 participants