Skip to content

Commit 58f61f6

Browse files
committed
integration test passes
1 parent 3f735ef commit 58f61f6

File tree

11 files changed

+520
-256
lines changed

11 files changed

+520
-256
lines changed

Makefile

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -103,19 +103,8 @@ test: manifests generate fmt vet envtest ## Run tests.
103103

104104
.PHONY: test-integration
105105
test-integration: manifests generate fmt vet envtest ## Run integration tests against local Temporal dev server.
106-
# @echo "Starting Temporal dev server..."
107-
# @docker run -d --name temporal-dev-test -p 7233:7233 temporalio/auto-setup:1.22.3 || true
108-
# @sleep 10
109106
@echo "Running integration tests..."
110107
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test -v ./tests -run TestIntegration
111-
@echo "Cleaning up Temporal dev server..."
112-
@docker stop temporal-dev-test || true
113-
@docker rm temporal-dev-test || true
114-
115-
.PHONY: test-integration-clean
116-
test-integration-clean: ## Clean up any leftover Temporal dev server containers from integration tests.
117-
@docker stop temporal-dev-test || true
118-
@docker rm temporal-dev-test || true
119108

120109
##@ Build
121110

api/v1alpha1/make.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,11 @@ func MakeTWD(
4949
}
5050

5151
// MakePodSpec creates a pod spec. Feel free to add parameters as needed.
52-
func MakePodSpec(containers []v1.Container, labels map[string]string) v1.PodTemplateSpec {
52+
func MakePodSpec(containers []v1.Container, labels map[string]string, taskQueue string) v1.PodTemplateSpec {
53+
for i := range containers {
54+
containers[i].Env = append(containers[i].Env, v1.EnvVar{Name: "TEMPORAL_TASK_QUEUE", Value: taskQueue})
55+
}
56+
5357
return v1.PodTemplateSpec{
5458
ObjectMeta: metav1.ObjectMeta{
5559
Labels: labels,
@@ -61,11 +65,11 @@ func MakePodSpec(containers []v1.Container, labels map[string]string) v1.PodTemp
6165
}
6266

6367
func MakeTWDWithImage(imageName string) *TemporalWorkerDeployment {
64-
return MakeTWD(1, MakePodSpec([]v1.Container{{Image: imageName}}, nil), nil, nil, nil)
68+
return MakeTWD(1, MakePodSpec([]v1.Container{{Image: imageName}}, nil, ""), nil, nil, nil)
6569
}
6670

6771
func MakeTWDWithName(name string) *TemporalWorkerDeployment {
68-
twd := MakeTWD(1, MakePodSpec(nil, nil), nil, nil, nil)
72+
twd := MakeTWD(1, MakePodSpec(nil, nil, ""), nil, nil, nil)
6973
twd.ObjectMeta.Name = name
7074
twd.Name = name
7175
return twd

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/controller/genplan.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ func (r *TemporalWorkerDeploymentReconciler) generatePlan(
7575

7676
// Check if we need to force manual strategy due to external modification
7777
rolloutStrategy := w.Spec.RolloutStrategy
78-
if w.Status.LastModifierIdentity != controllerIdentity {
78+
if w.Status.LastModifierIdentity != controllerIdentity && w.Status.LastModifierIdentity != "" {
7979
l.Info("Forcing manual rollout strategy since deployment was modified externally")
8080
rolloutStrategy.Strategy = temporaliov1alpha1.UpdateManual
8181
}

internal/demo/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Build the manager binary
2-
FROM golang:1.23 as builder
2+
FROM golang:1.24 as builder
33
ARG TARGETOS
44
ARG TARGETARCH
55
ARG WORKER

internal/k8s/deployments.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@ package k8s
77
import (
88
"context"
99
"fmt"
10+
"regexp"
11+
"sort"
12+
"strings"
13+
1014
"github.com/distribution/reference"
1115
appsv1 "k8s.io/api/apps/v1"
1216
v1 "k8s.io/api/core/v1"
1317
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
14-
"regexp"
1518
"sigs.k8s.io/controller-runtime/pkg/client"
16-
"sort"
17-
"strings"
1819

1920
temporaliov1alpha1 "github.com/temporalio/temporal-worker-controller/api/v1alpha1"
2021
"github.com/temporalio/temporal-worker-controller/internal/controller/k8s.io/utils"

internal/k8s/deployments_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -218,8 +218,8 @@ func TestGenerateBuildID(t *testing.T) {
218218
name: "same image different pod specs",
219219
generateInputs: func() (*temporaliov1alpha1.TemporalWorkerDeployment, *temporaliov1alpha1.TemporalWorkerDeployment) {
220220
img := "my.test_image"
221-
pod1 := temporaliov1alpha1.MakePodSpec([]v1.Container{{Image: img}}, map[string]string{"pod": "1"})
222-
pod2 := temporaliov1alpha1.MakePodSpec([]v1.Container{{Image: img}}, map[string]string{"pod": "2"})
221+
pod1 := temporaliov1alpha1.MakePodSpec([]v1.Container{{Image: img}}, map[string]string{"pod": "1"}, "")
222+
pod2 := temporaliov1alpha1.MakePodSpec([]v1.Container{{Image: img}}, map[string]string{"pod": "2"}, "")
223223

224224
twd1 := temporaliov1alpha1.MakeTWD(1, pod1, nil, nil, nil)
225225
twd2 := temporaliov1alpha1.MakeTWD(1, pod2, nil, nil, nil)
@@ -233,7 +233,7 @@ func TestGenerateBuildID(t *testing.T) {
233233
name: "same pod specs different TWD spec",
234234
generateInputs: func() (*temporaliov1alpha1.TemporalWorkerDeployment, *temporaliov1alpha1.TemporalWorkerDeployment) {
235235
img := "my.test_image"
236-
pod := temporaliov1alpha1.MakePodSpec([]v1.Container{{Image: img}}, nil)
236+
pod := temporaliov1alpha1.MakePodSpec([]v1.Container{{Image: img}}, nil, "")
237237

238238
twd1 := temporaliov1alpha1.MakeTWD(1, pod, nil, nil, nil)
239239
twd2 := temporaliov1alpha1.MakeTWD(2, pod, nil, nil, nil)
@@ -246,7 +246,7 @@ func TestGenerateBuildID(t *testing.T) {
246246
{
247247
name: "no containers",
248248
generateInputs: func() (*temporaliov1alpha1.TemporalWorkerDeployment, *temporaliov1alpha1.TemporalWorkerDeployment) {
249-
twd := temporaliov1alpha1.MakeTWD(1, temporaliov1alpha1.MakePodSpec(nil, nil), nil, nil, nil)
249+
twd := temporaliov1alpha1.MakeTWD(1, temporaliov1alpha1.MakePodSpec(nil, nil, ""), nil, nil, nil)
250250
return twd, nil // only check 1 result, no need to compare
251251
},
252252
expectedPrefix: "",

tests/env_helpers.go

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
package tests
2+
3+
import (
4+
"context"
5+
"log/slog"
6+
"os"
7+
"os/exec"
8+
"path/filepath"
9+
"testing"
10+
"time"
11+
12+
temporaliov1alpha1 "github.com/temporalio/temporal-worker-controller/api/v1alpha1"
13+
"github.com/temporalio/temporal-worker-controller/internal/controller"
14+
"github.com/temporalio/temporal-worker-controller/internal/controller/clientpool"
15+
"go.temporal.io/sdk/log"
16+
appsv1 "k8s.io/api/apps/v1"
17+
corev1 "k8s.io/api/core/v1"
18+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
19+
"k8s.io/apimachinery/pkg/types"
20+
"k8s.io/client-go/kubernetes/scheme"
21+
"k8s.io/client-go/rest"
22+
ctrl "sigs.k8s.io/controller-runtime"
23+
"sigs.k8s.io/controller-runtime/pkg/client"
24+
"sigs.k8s.io/controller-runtime/pkg/envtest"
25+
logf "sigs.k8s.io/controller-runtime/pkg/log"
26+
"sigs.k8s.io/controller-runtime/pkg/log/zap"
27+
"sigs.k8s.io/controller-runtime/pkg/manager"
28+
)
29+
30+
// setupKubebuilderAssets sets up the KUBEBUILDER_ASSETS environment variable if not already set
31+
func setupKubebuilderAssets() error {
32+
if os.Getenv("KUBEBUILDER_ASSETS") != "" {
33+
return nil // Already set
34+
}
35+
36+
// Try to find the assets using setup-envtest
37+
cmd := exec.Command("setup-envtest", "use", "1.28.0", "--bin-dir", "bin")
38+
output, err := cmd.Output()
39+
if err != nil {
40+
return err
41+
}
42+
43+
// Parse the output to get the path
44+
// The output format is typically: "export KUBEBUILDER_ASSETS=/path/to/assets"
45+
// We need to extract just the path
46+
assetsPath := string(output)
47+
if len(assetsPath) > 0 {
48+
// Remove any trailing newlines
49+
assetsPath = assetsPath[:len(assetsPath)-1]
50+
os.Setenv("KUBEBUILDER_ASSETS", assetsPath)
51+
}
52+
53+
return nil
54+
}
55+
56+
// setupTestEnvironment sets up the test environment with envtest
57+
func setupTestEnvironment(t *testing.T) (*rest.Config, client.Client, manager.Manager, *clientpool.ClientPool, func()) {
58+
// Setup kubebuilder assets for IDE testing
59+
if err := setupKubebuilderAssets(); err != nil {
60+
t.Logf("Warning: Could not setup kubebuilder assets automatically: %v", err)
61+
t.Logf("You may need to run 'make envtest' first or set KUBEBUILDER_ASSETS manually")
62+
}
63+
64+
logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true)))
65+
66+
t.Log("bootstrapping test environment")
67+
//startDevServer(t)
68+
testEnv := &envtest.Environment{
69+
CRDDirectoryPaths: []string{
70+
filepath.Join("..", "helm", "temporal-worker-controller", "templates", "crds"),
71+
},
72+
ErrorIfCRDPathMissing: true,
73+
}
74+
75+
cfg, err := testEnv.Start()
76+
if err != nil {
77+
t.Fatalf("failed to start test environment: %v", err)
78+
}
79+
80+
err = temporaliov1alpha1.AddToScheme(scheme.Scheme) // is this installing CRDs?
81+
if err != nil {
82+
t.Fatalf("failed to add scheme: %v", err)
83+
}
84+
85+
k8sClient, err := client.New(cfg, client.Options{Scheme: scheme.Scheme})
86+
if err != nil {
87+
t.Fatalf("failed to create k8s client: %v", err)
88+
}
89+
90+
// Create manager
91+
mgr, err := ctrl.NewManager(cfg, ctrl.Options{
92+
Scheme: scheme.Scheme,
93+
})
94+
if err != nil {
95+
t.Fatalf("failed to create manager: %v", err)
96+
}
97+
98+
// Create client pool
99+
clientPool := clientpool.New(log.NewStructuredLogger(slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
100+
AddSource: false,
101+
Level: nil,
102+
ReplaceAttr: nil,
103+
}))), k8sClient)
104+
105+
// Setup controller
106+
reconciler := &controller.TemporalWorkerDeploymentReconciler{
107+
Client: mgr.GetClient(),
108+
Scheme: mgr.GetScheme(),
109+
TemporalClientPool: clientPool,
110+
}
111+
112+
err = reconciler.SetupWithManager(mgr)
113+
if err != nil {
114+
t.Fatalf("failed to setup controller: %v", err)
115+
}
116+
117+
// Start manager
118+
ctx, cancel := context.WithCancel(context.Background())
119+
go func() {
120+
if err := mgr.Start(ctx); err != nil {
121+
t.Errorf("failed to start manager: %v", err)
122+
}
123+
}()
124+
125+
// Return cleanup function
126+
cleanup := func() {
127+
cancel()
128+
if err := testEnv.Stop(); err != nil {
129+
t.Errorf("failed to stop test environment: %v", err)
130+
}
131+
}
132+
133+
return cfg, k8sClient, mgr, clientPool, cleanup
134+
}
135+
136+
func applyDeployment(t *testing.T, ctx context.Context, k8sClient client.Client, deploymentName, namespace string) {
137+
var deployment appsv1.Deployment
138+
if err := k8sClient.Get(ctx, types.NamespacedName{
139+
Name: deploymentName,
140+
Namespace: namespace,
141+
}, &deployment); err != nil {
142+
t.Fatalf("failed to get deployment: %v", err)
143+
}
144+
145+
// Set deployment status to Available to simulate a healthy deployment
146+
// This is necessary because envtest doesn't actually start pods
147+
now := metav1.Now()
148+
deployment.Status = appsv1.DeploymentStatus{ // todo: only do this if workers come up successfully
149+
Replicas: *deployment.Spec.Replicas,
150+
UpdatedReplicas: *deployment.Spec.Replicas,
151+
ReadyReplicas: *deployment.Spec.Replicas,
152+
AvailableReplicas: *deployment.Spec.Replicas,
153+
UnavailableReplicas: 0,
154+
Conditions: []appsv1.DeploymentCondition{
155+
{
156+
Type: appsv1.DeploymentAvailable,
157+
Status: corev1.ConditionTrue,
158+
LastUpdateTime: now,
159+
LastTransitionTime: now,
160+
Reason: "MinimumReplicasAvailable",
161+
Message: "Deployment has minimum availability.",
162+
},
163+
{
164+
Type: appsv1.DeploymentProgressing,
165+
Status: corev1.ConditionTrue,
166+
LastUpdateTime: now,
167+
LastTransitionTime: now,
168+
Reason: "NewReplicaSetAvailable",
169+
Message: "ReplicaSet is available.",
170+
},
171+
},
172+
}
173+
174+
if err := k8sClient.Status().Update(ctx, &deployment); err != nil {
175+
t.Fatalf("failed to update deployment status: %v", err)
176+
}
177+
178+
for i := int32(0); i < *(deployment.Spec.Replicas); i++ {
179+
go runHelloWorldWorker(ctx, deployment.Spec.Template) // todo: cancel these appropriately
180+
}
181+
}
182+
183+
// createTestNamespace creates a test namespace
184+
func createTestNamespace(t *testing.T, k8sClient client.Client) *corev1.Namespace {
185+
testNamespace := &corev1.Namespace{
186+
ObjectMeta: metav1.ObjectMeta{
187+
Name: "test-integration-" + time.Now().Format("20060102150405"),
188+
},
189+
}
190+
191+
if err := k8sClient.Create(context.Background(), testNamespace); err != nil {
192+
t.Fatalf("failed to create test namespace: %v", err)
193+
}
194+
195+
return testNamespace
196+
}
197+
198+
// cleanupTestNamespace cleans up the test namespace
199+
func cleanupTestNamespace(t *testing.T, cfg *rest.Config, k8sClient client.Client, testNamespace *corev1.Namespace) {
200+
if testNamespace != nil {
201+
if err := k8sClient.Delete(context.Background(), testNamespace); err != nil {
202+
t.Errorf("failed to delete test namespace: %v", err)
203+
}
204+
}
205+
}

0 commit comments

Comments
 (0)