Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
c09f779
Add TemporalWorkerOwnedResource CRD for attaching resources to versio…
carlydf Feb 26, 2026
dda34a3
fix lint
carlydf Feb 27, 2026
7fd5e2b
Move owned-resource planning into planner package and add unit tests
carlydf Feb 27, 2026
24e6244
Rename Plan.OwnedResourceApplies to ApplyOwnedResources
carlydf Feb 27, 2026
c69104a
Replace .Namespace template var with .TemporalNamespace
carlydf Feb 27, 2026
c37533d
Add TemporalWorkerOwnedResource validating webhook
carlydf Feb 27, 2026
dba4d85
Wire up TWOR validating webhook and fix Helm TLS infrastructure
carlydf Feb 27, 2026
c730340
Add CRD manifest and RBAC rules for TemporalWorkerOwnedResource
carlydf Feb 27, 2026
e8c3336
Grant controller wildcard RBAC to apply TWOR-attached resources
carlydf Feb 27, 2026
c204ecd
Make controller RBAC for attached resources explicit by default
carlydf Feb 27, 2026
d0394e2
Rename ownedResources to ownedResourceConfig in Helm values
carlydf Feb 27, 2026
d3be960
Implement TWOR status write-back after SSA apply
carlydf Feb 27, 2026
e1043dc
Simplify errors.Join call for apply and status errors
carlydf Feb 28, 2026
1f12c19
Set TWD as controller owner of TWOR for Kubernetes GC
carlydf Feb 28, 2026
5ef558d
Move TWOR owner ref write from genplan to execplan
carlydf Feb 28, 2026
b5921ae
Move TWOR owner ref logic into planner package and make it testable
carlydf Feb 28, 2026
47c1ca6
Have planner build TWD OwnerReference from name+UID instead of receiv…
carlydf Feb 28, 2026
9597328
Run make generate and make manifests; fix controller-gen marker on we…
carlydf Feb 28, 2026
9a4fe06
Add integration test for TemporalWorkerOwnedResource HPA happy path
carlydf Feb 28, 2026
27987b8
Refactor TWOR integration test: extract validation steps into helper …
carlydf Feb 28, 2026
8ae649c
Add TemporalWorkerOwnedResource docs, HPA example, and cert-manager s…
carlydf Mar 2, 2026
53bf8f9
Address code review feedback on docs and resource naming
carlydf Mar 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

> 🚀 **Public Preview**: This project is in [Public Preview](https://docs.temporal.io/evaluate/development-production-features/release-stages) and ready for production use cases*. Core functionality is complete with stable APIs.
>
> *Dynamic auto-scaling based on workflow load is not yet implemented. Use cases must work with fixed worker replica counts.
> *Autoscaling based on Temporal task queue depth is not yet built in. You can attach Horizontal Pod Autoscalers or KEDA ScaledObjects to each versioned Deployment via [`TemporalWorkerOwnedResource`](docs/owned-resources.md).

**The Temporal Worker Controller makes it simple and safe to deploy Temporal workers on Kubernetes.**

Expand Down Expand Up @@ -78,6 +78,7 @@ When you update the image, the controller automatically:
- Helm [v3.0+](https://github.com/helm/helm/releases) if deploying via our Helm chart
- [Temporal Server](https://docs.temporal.io/) (Cloud or self-hosted [v1.29.1](https://github.com/temporalio/temporal/releases/tag/v1.29.1))
- Basic familiarity with Temporal [Workers](https://docs.temporal.io/workers), [Workflows](https://docs.temporal.io/workflows), and [Worker Versioning](https://docs.temporal.io/production-deployment/worker-deployments/worker-versioning)
- **[cert-manager](https://cert-manager.io/docs/installation/)** *(required for `TemporalWorkerOwnedResource`)* — the controller installs a validating webhook for TWOR objects that requires TLS. cert-manager handles certificate provisioning automatically. If cert-manager is not available in your cluster, see [Webhook TLS without cert-manager](docs/owned-resources.md#webhook-tls) for the manual setup.

### 🔧 Installation

Expand All @@ -104,7 +105,7 @@ helm install temporal-worker-controller \
- ✅ **Deletion of resources** associated with drained Worker Deployment Versions
- ✅ **Multiple rollout strategies**: `Manual`, `AllAtOnce`, and `Progressive` rollouts
- ✅ **Gate workflows** - Test new versions with a [pre-deployment test](https://docs.temporal.io/production-deployment/worker-deployments/worker-versioning#adding-a-pre-deployment-test) before routing real traffic to them
- **Load-based auto-scaling** - Not yet implemented (use fixed replica counts)
- **Per-version attached resources** - Attach HPAs, KEDA ScaledObjects, PodDisruptionBudgets, or any namespaced Kubernetes resource to each worker version with running workers via [`TemporalWorkerOwnedResource`](docs/owned-resources.md) — this is also the recommended path for metric-based and backlog-based autoscaling


## 💡 Why Use This?
Expand Down Expand Up @@ -137,6 +138,7 @@ The Temporal Worker Controller eliminates this operational overhead by automatin
| [Configuration](docs/configuration.md) | Complete configuration reference |
| [Concepts](docs/concepts.md) | Key concepts and terminology |
| [Limits](docs/limits.md) | Technical constraints and limitations |
| [TemporalWorkerOwnedResource](docs/owned-resources.md) | Attach HPAs, PDBs, and other resources to each versioned Deployment |

## 🔧 Worker Configuration

Expand Down
104 changes: 104 additions & 0 deletions api/v1alpha1/temporalworkerownedresource_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Unless explicitly stated otherwise all files in this repository are licensed under the MIT License.
//
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2024 Datadog, Inc.

package v1alpha1

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)

// WorkerDeploymentReference references a TemporalWorkerDeployment in the same namespace.
type WorkerDeploymentReference struct {
// Name of the TemporalWorkerDeployment resource in the same namespace.
// +kubebuilder:validation:Required
// +kubebuilder:validation:Pattern=`^[a-z0-9]([-a-z0-9]*[a-z0-9])?$`
Name string `json:"name"`
}

// TemporalWorkerOwnedResourceSpec defines the desired state of TemporalWorkerOwnedResource.
type TemporalWorkerOwnedResourceSpec struct {
// WorkerRef references the TemporalWorkerDeployment to attach this resource to.
// +kubebuilder:validation:Required
WorkerRef WorkerDeploymentReference `json:"workerRef"`

// Object is the Kubernetes resource template to attach to each versioned Deployment.
// One copy of this resource is created per active Build ID, owned by the corresponding
// versioned Deployment (so it is garbage collected when the Deployment is deleted).
//
// The object must include apiVersion, kind, and spec. The metadata.name and
// metadata.namespace are generated by the controller and must not be set by the user.
//
// String values in the spec may contain Go template expressions:
// {{ .DeploymentName }} - the controller-generated versioned Deployment name
// {{ .TemporalNamespace }} - the Temporal namespace the worker connects to
// {{ .BuildID }} - the Build ID for this version
//
// The controller also auto-injects two well-known fields. If your resource requires these fields,
// you must include them in the spec.object but leave them empty for the controller to fill in:
// scaleTargetRef - set to point at the versioned Deployment (for HPA, KEDA, WPA, etc.)
// matchLabels - set to the versioned Deployment's selector labels (for PDB, WPA, etc.)
// +kubebuilder:validation:Required
// +kubebuilder:pruning:PreserveUnknownFields
Object runtime.RawExtension `json:"object"`
}

// OwnedResourceVersionStatus describes the status of an owned resource for a single Build ID.
type OwnedResourceVersionStatus struct {
// BuildID is the Build ID of the versioned Deployment this status entry refers to.
BuildID string `json:"buildID"`

// Applied is true if the resource was successfully applied for this Build ID.
Applied bool `json:"applied"`

// ResourceName is the name of the applied Kubernetes resource.
// +optional
ResourceName string `json:"resourceName,omitempty"`

// Message describes any error if Applied is false.
// +optional
Message string `json:"message,omitempty"`

// LastTransitionTime is the last time this status entry was updated.
LastTransitionTime metav1.Time `json:"lastTransitionTime"`
}

// TemporalWorkerOwnedResourceStatus defines the observed state of TemporalWorkerOwnedResource.
type TemporalWorkerOwnedResourceStatus struct {
// Versions describes the per-Build-ID status of owned resources.
// +optional
Versions []OwnedResourceVersionStatus `json:"versions,omitempty"`
}

//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
//+kubebuilder:resource:shortName=twor
//+kubebuilder:printcolumn:name="Worker",type="string",JSONPath=".spec.workerRef.name",description="Referenced TemporalWorkerDeployment"
//+kubebuilder:printcolumn:name="Kind",type="string",JSONPath=".spec.object.kind",description="Kind of owned resource"
//+kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Age"

// TemporalWorkerOwnedResource attaches an arbitrary namespaced Kubernetes resource
// (HPA, PDB, WPA, custom CRDs, etc.) to each per-Build-ID versioned Deployment
// managed by a TemporalWorkerDeployment. One copy of the resource is created per
// active Build ID and is owned by the corresponding versioned Deployment.
type TemporalWorkerOwnedResource struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec TemporalWorkerOwnedResourceSpec `json:"spec,omitempty"`
Status TemporalWorkerOwnedResourceStatus `json:"status,omitempty"`
}

//+kubebuilder:object:root=true

// TemporalWorkerOwnedResourceList contains a list of TemporalWorkerOwnedResource.
type TemporalWorkerOwnedResourceList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []TemporalWorkerOwnedResource `json:"items"`
}

func init() {
SchemeBuilder.Register(&TemporalWorkerOwnedResource{}, &TemporalWorkerOwnedResourceList{})
}
Loading
Loading