Skip to content

Commit 1123b6b

Browse files
carlydfclaude
andauthored
Enable Controller-managed versioned scaling resources with WorkerResourceTemplate (#217)
<!--- Note to EXTERNAL Contributors --> <!-- Thanks for opening a PR! If it is a significant code change, please **make sure there is an open issue** for this. We work best with you when we have accepted the idea first before you code. --> <!--- For ALL Contributors 👇 --> ## What was changed ### New CRD: `WorkerResourceTemplate` (WRT) A new `WorkerResourceTemplate` CRD that lets users attach arbitrary namespaced Kubernetes resources (HPAs, PDBs, custom scalers, etc.) to a `TemporalWorkerDeployment`. The controller creates one copy of the resource per active worker version, with auto-injection of `scaleTargetRef`, `selector.matchLabels`, and metric selector labels to point at the correct versioned Deployment. #### Key behaviors: - One copy per active Build ID, named `{twdName}-{wrtName}-{buildID}` (uniquely truncated to 47 chars, DNS-safe) - Auto-injects `spec.scaleTargetRef` (when set to `{}`) to reference the versioned Deployment → enables per-version HPA autoscaling, and any other scaler that uses `scaleTargetRef` - Auto-injects `selector.matchLabels` (when set to `{}`) with the correct per-version labels → enables per-version PDB targeting, and arbitrary CRDs that use `selector.matchLabels` to target versioned Deployments - Auto-appends `worker_deployment_name`, `worker_deployment_build_id`, and `temporal_namespace` to `spec.metrics[*].external.metric.selector.matchLabels` whenever `matchLabels` is present (including `{}`). User labels like `task_type: "Activity"` coexist alongside the injected keys. Absent `matchLabels` = no injection for that metric entry. - Applied via Server-Side Apply with field manager `"temporal-worker-controller"` - Owner ref on each resource copy points to the `WorkerResourceTemplate` → k8s GC deletes all copies when the WRT is deleted - Apply status written back to `WorkerResourceTemplate.status.versions[*]` (Applied, Message, BuildID) - Resource spec lives in `spec.template` (raw JSON/YAML embedded object) - Target TWD referenced via `spec.temporalWorkerDeploymentRef.name` #### Validating Webhook A `WorkerResourceTemplateValidator` webhook enforces: - `apiVersion` and `kind` required; `metadata.name`/`metadata.namespace` forbidden (controller sets these) - Allowed resource kinds configurable via `ALLOWED_KINDS` env var (default: `HorizontalPodAutoscaler`) - `minReplicas` ≠ 0 (currently required for `approximate_task_queue_backlog` metric-based autoscaling to work when queue is idle, plan to relax this in future) - `scaleTargetRef` must be absent or `{}` (opt-in sentinel); non-empty value rejected (controller owns injection) - `selector.matchLabels` must be absent or `{}` (opt-in sentinel); non-empty value rejected (controller owns injection) - `metrics[*].external.metric.selector.matchLabels` must not contain the controller-owned keys `worker_deployment_name`, `worker_deployment_build_id`, or `temporal_namespace`; user labels (e.g. `task_type`) are allowed - SAR check: requesting user must be able to create/update the embedded resource type - SAR check: controller service account must be able to create/update the embedded resource type - `spec.temporalWorkerDeploymentRef.name` is immutable after creation ### Helm chart updates - `helm/temporal-worker-controller-crds/templates/temporal.io_workerresourcetemplates.yaml` (new CRD manifest) - `helm/temporal-worker-controller/templates/webhook.yaml` (always-on `WorkerResourceTemplate` `ValidatingWebhookConfiguration`; `TemporalWorkerDeployment` webhook now behind `webhook.enabled`) - `helm/temporal-worker-controller/templates/certmanager.yaml` (cert-manager `Issuer` + `Certificate` for TLS, default enabled) - `helm/temporal-worker-controller/Chart.yaml` (cert-manager added as optional subchart dependency; opt in via `certmanager.install: true`) - `helm/temporal-worker-controller/templates/manager.yaml` (cert volume/port always present; `ALLOWED_KINDS`, `POD_NAMESPACE`, `SERVICE_ACCOUNT_NAME` env vars) - `helm/temporal-worker-controller/templates/rbac.yaml` (`WorkerResourceTemplate` + SAR rules in manager `ClusterRole`; editor/viewer roles; configurable attached-resource RBAC) - `helm/temporal-worker-controller/values.yaml` (`workerResourceConfig.allowedResources` default: HPA, piped to `ALLOWED_KINDS` and to controller rbac) ### Integration tests New integration test subtests added to the existing envtest suite, all running through the shared `testTemporalWorkerDeploymentCreation` table-test runner: - `WorkerResourceTemplate` (7 tests): Deployment owner ref, `matchLabels` injection, multiple `WorkerResourceTemplate`s on same `TemporalWorkerDeployment`, metric selector label injection, multiple active versions, apply failure → Applied:false, SSA idempotency - Rollout gaps (5 tests): Progressive ramp to Current, ConnectionSpecHash annotation repair, gate input from ConfigMap, gate input from Secret, multiple deprecated versions - Webhook admission (5 tests, separate Ginkgo suite): Spec rejection, SAR pass, SAR fail (user), SAR fail (controller SA), `temporalWorkerDeploymentRef.name` immutability ## Why? HPA autoscaling for versioned Temporal workers requires a separate HPA per worker version, each targeting only that version's Deployment with the correct `scaleTargetRef` and label selectors. Without this CRD, users have no way to create per-version resources that the controller lifecycle-manages alongside the versioned Deployments. ## Checklist <!--- add/delete as needed ---> 1. Closes #207 2. How was this tested: - Full envtest integration test suite: new subtests covering WRT lifecycle, previously uncovered rollout scenarios, and webhook admission via the real HTTP admission path - Unit tests: webhook validator, SSA naming/injection helpers, planner integration - All tests pass: `KUBEBUILDER_ASSETS=.../bin/k8s/1.27.1-darwin-arm64 go test -tags test_dep ./...` 3. Any docs updates needed? - `docs/worker-resource-template.md` added: concept overview, HPA example with cert-manager setup, RBAC configuration guide --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 318299a commit 1123b6b

59 files changed

Lines changed: 6100 additions & 216 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/helm-image-check.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ jobs:
2828
- name: Install crane
2929
uses: imjasonh/setup-crane@v0.4
3030

31+
- name: Build chart dependencies
32+
run: helm repo add jetstack https://charts.jetstack.io && helm dependency build helm/temporal-worker-controller
33+
3134
- name: Render chart and verify images
3235
run: |
3336
set -euo pipefail

.github/workflows/helm-validate.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ jobs:
1818
- name: Install Helm
1919
uses: azure/setup-helm@v3
2020

21+
- name: Build chart dependencies
22+
run: helm repo add jetstack https://charts.jetstack.io && helm dependency build helm/temporal-worker-controller
23+
2124
- name: Lint chart
2225
run: helm lint --strict helm/temporal-worker-controller
2326

@@ -30,6 +33,9 @@ jobs:
3033
- name: Install Helm
3134
uses: azure/setup-helm@v3
3235

36+
- name: Build chart dependencies
37+
run: helm repo add jetstack https://charts.jetstack.io && helm dependency build helm/temporal-worker-controller
38+
3339
- name: Template with default values
3440
run: helm template test-release helm/temporal-worker-controller
3541

.github/workflows/helm.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@ jobs:
111111
username: ${{ secrets.DOCKER_USERNAME }}
112112
password: ${{ secrets.DOCKER_PAT}}
113113

114+
- name: Build chart dependencies
115+
run: helm repo add jetstack https://charts.jetstack.io && helm dependency build ./helm/temporal-worker-controller
116+
114117
- name: Package and Push Helm charts
115118
run: |
116119
# Use version from previous step

.github/workflows/test-integration.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,13 @@ jobs:
125125

126126
- name: Download dependencies
127127
run: go mod download
128-
128+
129+
- name: Install Helm
130+
uses: azure/setup-helm@v3
131+
132+
- name: Build Helm chart dependencies
133+
run: helm repo add jetstack https://charts.jetstack.io && helm dependency build helm/temporal-worker-controller
134+
129135
- name: Run unit tests
130136
run: make test-unit
131137

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
bin
44
dist
5+
helm/**/charts/*.tgz
56

67
secret.env
78
skaffold.env

Makefile

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,8 @@ help: ## Display this help.
150150

151151
# crd:maxDescLen=0 is to avoid error described in https://github.com/kubernetes-sigs/kubebuilder/issues/2556#issuecomment-1074844483
152152
.PHONY: manifests
153-
manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects.
154-
GOWORK=off GO111MODULE=on $(CONTROLLER_GEN) rbac:roleName=manager-role crd:allowDangerousTypes=true,maxDescLen=0,generateEmbeddedObjectMeta=true webhook paths=./api/... paths=./internal/... paths=./cmd/... \
153+
manifests: controller-gen ## Generate ClusterRole and CustomResourceDefinition objects.
154+
GOWORK=off GO111MODULE=on $(CONTROLLER_GEN) rbac:roleName=manager-role crd:allowDangerousTypes=true,maxDescLen=0,generateEmbeddedObjectMeta=true paths=./api/... paths=./internal/... paths=./cmd/... \
155155
output:crd:artifacts:config=helm/temporal-worker-controller-crds/templates
156156

157157
.PHONY: generate
@@ -188,6 +188,15 @@ apply-load-sample-workflow: ## Start a sample workflow every 15 seconds
188188
sleep 15; \
189189
done
190190

191+
.PHONY: apply-hpa-load
192+
.SILENT: apply-hpa-load
193+
apply-hpa-load: ## Start ~2 workflows/sec to build a backlog and drive HPA scaling to ~10 replicas
194+
@echo "Starting load at ~2 workflows/sec. Press Ctrl-C to stop."
195+
@while true; do \
196+
$(MAKE) -s start-sample-workflow & \
197+
sleep 0.5; \
198+
done
199+
191200
.PHONY: list-workflow-build-ids
192201
list-workflow-build-ids: ## List workflow executions and their build IDs.
193202
@$(TEMPORAL) workflow list --limit 20 --fields SearchAttributes -o json | \
@@ -214,8 +223,8 @@ test-all: manifests generate envtest ## Run tests.
214223
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test -tags test_dep ./... -coverprofile cover.out
215224

216225
.PHONY: test-unit
217-
test-unit: ## Run unit tests with minimal setup.
218-
go test ./... -coverprofile cover.out
226+
test-unit: envtest ## Run unit tests and webhook integration tests (requires envtest binaries).
227+
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test ./... -coverprofile cover.out
219228

220229
.PHONY: test-integration
221230
test-integration: manifests generate envtest ## Run integration tests against local Temporal dev server.

README.md

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
[![Go Report Card](https://goreportcard.com/badge/github.com/temporalio/temporal-worker-controller)](https://goreportcard.com/report/github.com/temporalio/temporal-worker-controller)
55

66
> 🚀 **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.
7-
>
8-
> *Dynamic auto-scaling based on workflow load is not yet implemented. Use cases must work with fixed worker replica counts.
97
108
**The Temporal Worker Controller makes it simple and safe to deploy Temporal workers on Kubernetes.**
119

@@ -20,7 +18,8 @@ Temporal's [Worker Versioning](https://docs.temporal.io/production-deployment/wo
2018
📦 **Automatic version management** - Registers versions with Temporal, manages routing rules, and tracks version lifecycle
2119
🎯 **Smart traffic routing** - New workflows automatically get routed to your target worker version
2220
🛡️ **Progressive rollouts** - Catch incompatible changes early with small traffic percentages before they spread
23-
**Easy rollbacks** - Instantly route traffic back to a previous version if issues are detected
21+
**Easy rollbacks** - Instantly route traffic back to a previous version if issues are detected
22+
📈 **Per-version autoscaling** - Attach HPAs or other custom scalers to each versioned Deployment via [`WorkerResourceTemplate`](docs/worker-resource-templates.md)
2423

2524
## Quick Example
2625

@@ -78,6 +77,7 @@ When you update the image, the controller automatically:
7877
- Helm [v3.0+](https://github.com/helm/helm/releases) if deploying via our Helm chart
7978
- [Temporal Server](https://docs.temporal.io/) (Cloud or self-hosted [v1.29.1](https://github.com/temporalio/temporal/releases/tag/v1.29.1))
8079
- 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)
80+
- **TLS for the validating webhook** *(required for `WorkerResourceTemplate`)* — the recommended path is [cert-manager](https://cert-manager.io/docs/installation/), which handles certificate provisioning automatically. Install it separately or as a subchart of the controller chart (`certmanager.install: true`). If you prefer to manage TLS yourself, see [Webhook TLS](docs/worker-resource-templates.md#webhook-tls).
8181

8282
### 🔧 Installation
8383

@@ -118,7 +118,7 @@ See [docs/crd-management.md](docs/crd-management.md) for upgrade, rollback, and
118118
- ✅ **Deletion of resources** associated with drained Worker Deployment Versions
119119
- ✅ **Multiple rollout strategies**: `Manual`, `AllAtOnce`, and `Progressive` rollouts
120120
- ✅ **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
121-
- **Load-based auto-scaling** - Not yet implemented (use fixed replica counts)
121+
- **Per-version attached resources** - Attach HPAs, PodDisruptionBudgets, or any namespaced Kubernetes resource to each worker version with running workers via [`WorkerResourceTemplate`](docs/worker-resource-templates.md) — this is also the recommended path for metric-based and backlog-based autoscaling
122122

123123

124124
## 💡 Why Use This?
@@ -143,16 +143,17 @@ The Temporal Worker Controller eliminates this operational overhead by automatin
143143

144144
## 📖 Documentation
145145

146-
| Document | Description |
147-
|----------|-------------|
148-
| [Migration Guide](docs/migration-to-versioned.md) | Step-by-step guide for migrating from traditional deployments |
149-
| [Reversion Guide](docs/migration-to-unversioned.md) | Step-by-step guide for migrating back to unversioned deployment |
150-
| [CD Rollouts](docs/cd-rollouts.md) | Helm, kubectl, ArgoCD, and Flux integration for steady-state rollouts |
151-
| [Architecture](docs/architecture.md) | Technical deep-dive into how the controller works |
152-
| [Configuration](docs/configuration.md) | Complete configuration reference |
153-
| [Concepts](docs/concepts.md) | Key concepts and terminology |
154-
| [Limits](docs/limits.md) | Technical constraints and limitations |
155-
| [CRD Management](docs/crd-management.md) | CRD upgrade, rollback, and migration guide |
146+
| Document | Description |
147+
|-------------------------------------------------------------|-----------------------------------------------------------------------|
148+
| [Migration Guide](docs/migration-to-versioned.md) | Step-by-step guide for migrating from traditional deployments |
149+
| [Reversion Guide](docs/migration-to-unversioned.md) | Step-by-step guide for migrating back to unversioned deployment |
150+
| [CD Rollouts](docs/cd-rollouts.md) | Helm, kubectl, ArgoCD, and Flux integration for steady-state rollouts |
151+
| [Architecture](docs/architecture.md) | Technical deep-dive into how the controller works |
152+
| [Configuration](docs/configuration.md) | Complete configuration reference |
153+
| [Concepts](docs/concepts.md) | Key concepts and terminology |
154+
| [Limits](docs/limits.md) | Technical constraints and limitations |
155+
| [WorkerResourceTemplate](docs/worker-resource-templates.md) | Attach HPAs, PDBs, and other resources to each versioned Deployment |
156+
| [CRD Management](docs/crd-management.md) | CRD upgrade, rollback, and migration guide |
156157

157158
## 🔧 Worker Configuration
158159

api/v1alpha1/conditions.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package v1alpha1
2+
3+
// Condition type constants.
4+
const (
5+
// ConditionReady is True for TemporalWorkerDeployment when the Temporal
6+
// connection is reachable and the target version is the current version in Temporal.
7+
// It is True for WorkerResourceTemplate when all active Build ID instances of the
8+
// WorkerResourceTemplate have been successfully applied.
9+
ConditionReady = "Ready"
10+
11+
// ConditionProgressing is True while a rollout is actively in-flight —
12+
// i.e., the target version has not yet been promoted to current.
13+
ConditionProgressing = "Progressing"
14+
)
15+
16+
// Deprecated condition type constants. Maintained for backward compatibility with
17+
// monitoring and automation built against v1.3.x. Use Ready and Progressing
18+
// instead. These will be removed in the next major version of the CRD.
19+
const (
20+
// Deprecated: Use ConditionReady and ConditionProgressing instead.
21+
ConditionTemporalConnectionHealthy = "TemporalConnectionHealthy"
22+
23+
// Deprecated: Use ConditionReady instead.
24+
ConditionRolloutComplete = "RolloutComplete"
25+
)

api/v1alpha1/webhook_suite_test.go

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,26 @@
55
package v1alpha1
66

77
import (
8+
"bytes"
89
"context"
910
"crypto/tls"
11+
"encoding/json"
1012
"fmt"
1113
"net"
14+
"os"
15+
"os/exec"
1216
"path/filepath"
17+
"strings"
1318
"testing"
1419
"time"
1520

1621
. "github.com/onsi/ginkgo/v2"
1722
. "github.com/onsi/gomega"
1823
admissionv1 "k8s.io/api/admission/v1"
24+
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
25+
authorizationv1 "k8s.io/api/authorization/v1"
26+
corev1 "k8s.io/api/core/v1"
27+
rbacv1 "k8s.io/api/rbac/v1"
1928
//+kubebuilder:scaffold:imports
2029
"k8s.io/apimachinery/pkg/runtime"
2130
"k8s.io/client-go/rest"
@@ -26,6 +35,7 @@ import (
2635
"sigs.k8s.io/controller-runtime/pkg/log/zap"
2736
"sigs.k8s.io/controller-runtime/pkg/metrics/server"
2837
"sigs.k8s.io/controller-runtime/pkg/webhook"
38+
sigsyaml "sigs.k8s.io/yaml"
2939
)
3040

3141
// These tests use Ginkgo (BDD-style Go testing framework). Refer to
@@ -44,16 +54,24 @@ func TestAPIs(t *testing.T) {
4454
}
4555

4656
var _ = BeforeSuite(func() {
57+
if os.Getenv("KUBEBUILDER_ASSETS") == "" {
58+
Skip("Skipping webhook integration tests: KUBEBUILDER_ASSETS not set")
59+
}
60+
4761
logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
4862

4963
ctx, cancel = context.WithCancel(context.TODO())
5064

5165
By("bootstrapping test environment")
66+
wrtWebhook := loadWRTWebhookFromHelmChart(filepath.Join("..", "..", "helm", "temporal-worker-controller"))
5267
testEnv = &envtest.Environment{
53-
CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")},
54-
ErrorIfCRDPathMissing: false,
68+
CRDDirectoryPaths: []string{
69+
// CRDs live in the crds chart's templates directory
70+
filepath.Join("..", "..", "helm", "temporal-worker-controller-crds", "templates"),
71+
},
72+
ErrorIfCRDPathMissing: true,
5573
WebhookInstallOptions: envtest.WebhookInstallOptions{
56-
Paths: []string{filepath.Join("..", "..", "config", "webhook")},
74+
ValidatingWebhooks: []*admissionregistrationv1.ValidatingWebhookConfiguration{wrtWebhook},
5775
},
5876
}
5977

@@ -70,6 +88,19 @@ var _ = BeforeSuite(func() {
7088
err = admissionv1.AddToScheme(scheme)
7189
Expect(err).NotTo(HaveOccurred())
7290

91+
// corev1, rbacv1, and authorizationv1 are needed by the integration tests.
92+
// corev1: create Namespaces
93+
// rbacv1: create Roles and RoleBindings for RBAC setup
94+
// authorizationv1: create SubjectAccessReview objects inside the webhook validator
95+
err = corev1.AddToScheme(scheme)
96+
Expect(err).NotTo(HaveOccurred())
97+
98+
err = rbacv1.AddToScheme(scheme)
99+
Expect(err).NotTo(HaveOccurred())
100+
101+
err = authorizationv1.AddToScheme(scheme)
102+
Expect(err).NotTo(HaveOccurred())
103+
73104
//+kubebuilder:scaffold:scheme
74105

75106
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme})
@@ -93,6 +124,17 @@ var _ = BeforeSuite(func() {
93124
err = (&TemporalWorkerDeployment{}).SetupWebhookWithManager(mgr)
94125
Expect(err).NotTo(HaveOccurred())
95126

127+
// Set env vars consumed by NewWorkerResourceTemplateValidator before constructing it.
128+
// POD_NAMESPACE and SERVICE_ACCOUNT_NAME identify the controller SA for the SAR checks.
129+
// "test-system" / "test-controller" are the values used in the integration tests.
130+
// ALLOWED_KINDS mirrors the default Helm values so integration tests can create HPAs.
131+
Expect(os.Setenv("POD_NAMESPACE", "test-system")).To(Succeed())
132+
Expect(os.Setenv("SERVICE_ACCOUNT_NAME", "test-controller")).To(Succeed())
133+
Expect(os.Setenv("ALLOWED_KINDS", "HorizontalPodAutoscaler")).To(Succeed())
134+
135+
err = NewWorkerResourceTemplateValidator(mgr).SetupWebhookWithManager(mgr)
136+
Expect(err).NotTo(HaveOccurred())
137+
96138
//+kubebuilder:scaffold:webhook
97139

98140
go func() {
@@ -115,7 +157,36 @@ var _ = BeforeSuite(func() {
115157

116158
})
117159

160+
// loadWRTWebhookFromHelmChart renders the Helm chart's webhook.yaml with default values using
161+
// `helm template` and extracts the ValidatingWebhookConfiguration for WorkerResourceTemplate.
162+
// This makes the test authoritative against the Helm chart rather than a hand-maintained copy.
163+
func loadWRTWebhookFromHelmChart(chartPath string) *admissionregistrationv1.ValidatingWebhookConfiguration {
164+
out, err := exec.Command("helm", "template", "test", chartPath, "--show-only", "templates/webhook.yaml").Output()
165+
Expect(err).NotTo(HaveOccurred(), "helm template failed — is helm installed?")
166+
167+
// The file contains multiple YAML documents; find the WRT ValidatingWebhookConfiguration.
168+
for _, doc := range bytes.Split(out, []byte("\n---\n")) {
169+
trimmed := strings.TrimSpace(string(doc))
170+
if !strings.Contains(trimmed, "kind: ValidatingWebhookConfiguration") {
171+
continue
172+
}
173+
if !strings.Contains(trimmed, "vworkerresourcetemplate.kb.io") {
174+
continue
175+
}
176+
jsonBytes, convErr := sigsyaml.YAMLToJSON([]byte(trimmed))
177+
Expect(convErr).NotTo(HaveOccurred(), "failed to convert WRT webhook YAML to JSON")
178+
var wh admissionregistrationv1.ValidatingWebhookConfiguration
179+
Expect(json.Unmarshal(jsonBytes, &wh)).To(Succeed(), "failed to decode WRT ValidatingWebhookConfiguration")
180+
return &wh
181+
}
182+
Fail("WRT ValidatingWebhookConfiguration not found in rendered helm chart webhook.yaml")
183+
return nil
184+
}
185+
118186
var _ = AfterSuite(func() {
187+
if testEnv == nil {
188+
return // BeforeSuite was skipped; nothing to tear down
189+
}
119190
cancel()
120191
By("tearing down the test environment")
121192
err := testEnv.Stop()

api/v1alpha1/worker_types.go

Lines changed: 7 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,15 @@ type WorkerOptions struct {
5050
// TemporalWorkerDeploymentSpec defines the desired state of TemporalWorkerDeployment
5151
type TemporalWorkerDeploymentSpec struct {
5252

53-
// Number of desired pods. This is a pointer to distinguish between explicit
54-
// zero and not specified. Defaults to 1.
53+
// Number of desired pods. When set, the controller manages replicas for all active
54+
// worker versions. When omitted (nil), the controller creates versioned Deployments
55+
// with nil replicas and never calls UpdateScale on active versions — following the
56+
// Kubernetes-recommended pattern for HPA and other external autoscalers
57+
// (https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#migrating-deployments-and-statefulsets-to-horizontal-autoscaling).
58+
// The controller still scales drained versions (and inactive versions that are not
59+
// the rollout target) to zero regardless.
5560
// This field makes TemporalWorkerDeploymentSpec implement the scale subresource, which is compatible with auto-scalers.
5661
// +optional
57-
// +kubebuilder:default=1
5862
Replicas *int32 `json:"replicas,omitempty" protobuf:"varint,1,opt,name=replicas"`
5963

6064
// Template describes the pods that will be created.
@@ -86,29 +90,6 @@ type TemporalWorkerDeploymentSpec struct {
8690
WorkerOptions WorkerOptions `json:"workerOptions"`
8791
}
8892

89-
// Condition type constants for TemporalWorkerDeployment.
90-
const (
91-
// ConditionReady is True when the Temporal connection is reachable and the
92-
// target version is the current version in Temporal. CD systems such as
93-
// ArgoCD and Flux use this condition to gate deployment success.
94-
ConditionReady = "Ready"
95-
96-
// ConditionProgressing is True while a rollout is actively in-flight —
97-
// i.e., the target version has not yet been promoted to current.
98-
ConditionProgressing = "Progressing"
99-
)
100-
101-
// Deprecated condition type constants. Maintained for backward compatibility with
102-
// monitoring and automation built against v1.3.x. Use Ready and Progressing
103-
// instead. These will be removed in the next major version of the CRD.
104-
const (
105-
// Deprecated: Use ConditionReady and ConditionProgressing instead.
106-
ConditionTemporalConnectionHealthy = "TemporalConnectionHealthy"
107-
108-
// Deprecated: Use ConditionReady instead.
109-
ConditionRolloutComplete = "RolloutComplete"
110-
)
111-
11293
// Condition reason constants for TemporalWorkerDeployment.
11394
//
11495
// These strings appear in status.conditions[].reason and are part of the CRD's

0 commit comments

Comments
 (0)