Skip to content

Commit e58c01e

Browse files
authored
Merge pull request #64 from sdowell/reconcile-pod-unit-tests
test: add unit tests for reconcilePod
2 parents 8895231 + 9cd382e commit e58c01e

File tree

4 files changed

+150
-21
lines changed

4 files changed

+150
-21
lines changed

cmd/agent-sandbox-controller/main.go

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,30 +22,18 @@ import (
2222
// to ensure that exec-entrypoint and run can make use of them.
2323
_ "k8s.io/client-go/plugin/pkg/client/auth"
2424

25-
"k8s.io/apimachinery/pkg/runtime"
26-
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
27-
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
2825
ctrl "sigs.k8s.io/controller-runtime"
2926
"sigs.k8s.io/controller-runtime/pkg/healthz"
3027
"sigs.k8s.io/controller-runtime/pkg/log/zap"
3128

32-
sandboxv1alpha1 "sigs.k8s.io/agent-sandbox/api/v1alpha1"
3329
"sigs.k8s.io/agent-sandbox/controllers"
3430
//+kubebuilder:scaffold:imports
3531
)
3632

3733
var (
38-
scheme = runtime.NewScheme()
3934
setupLog = ctrl.Log.WithName("setup")
4035
)
4136

42-
func init() {
43-
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
44-
45-
utilruntime.Must(sandboxv1alpha1.AddToScheme(scheme))
46-
//+kubebuilder:scaffold:scheme
47-
}
48-
4937
func main() {
5038
var metricsAddr string
5139
var enableLeaderElection bool
@@ -64,7 +52,7 @@ func main() {
6452
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
6553

6654
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
67-
Scheme: scheme,
55+
Scheme: controllers.Scheme,
6856
HealthProbeBindAddress: probeAddr,
6957
LeaderElection: enableLeaderElection,
7058
LeaderElectionID: "a3317529.x-k8s.io",

controllers/sandbox_controller.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ import (
2727
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2828
"k8s.io/apimachinery/pkg/runtime"
2929
"k8s.io/apimachinery/pkg/types"
30+
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
31+
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
3032
ctrl "sigs.k8s.io/controller-runtime"
3133
"sigs.k8s.io/controller-runtime/pkg/builder"
3234
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -41,6 +43,16 @@ const (
4143
sandboxLabel = "agents.x-k8s.io/sandbox-name-hash"
4244
)
4345

46+
var (
47+
// Scheme for use by sandbox controllers. Registers required types for client.
48+
Scheme = runtime.NewScheme()
49+
)
50+
51+
func init() {
52+
utilruntime.Must(clientgoscheme.AddToScheme(Scheme))
53+
utilruntime.Must(sandboxv1alpha1.AddToScheme(Scheme))
54+
}
55+
4456
// SandboxReconciler reconciles a Sandbox object
4557
type SandboxReconciler struct {
4658
client.Client

controllers/sandbox_controller_test.go

Lines changed: 133 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,17 @@ import (
1818
"errors"
1919
"testing"
2020

21-
. "github.com/onsi/gomega"
21+
"github.com/stretchr/testify/require"
2222
corev1 "k8s.io/api/core/v1"
2323
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
24+
"k8s.io/apimachinery/pkg/runtime"
25+
"k8s.io/apimachinery/pkg/types"
26+
"k8s.io/utils/ptr"
2427
sandboxv1alpha1 "sigs.k8s.io/agent-sandbox/api/v1alpha1"
28+
"sigs.k8s.io/controller-runtime/pkg/client/fake"
2529
)
2630

2731
func TestComputeReadyCondition(t *testing.T) {
28-
g := NewGomegaWithT(t)
2932
r := &SandboxReconciler{}
3033

3134
testCases := []struct {
@@ -143,10 +146,134 @@ func TestComputeReadyCondition(t *testing.T) {
143146
for _, tc := range testCases {
144147
t.Run(tc.name, func(t *testing.T) {
145148
condition := r.computeReadyCondition(tc.generation, tc.err, tc.svc, tc.pod)
146-
g.Expect(condition.Type).To(Equal(string(sandboxv1alpha1.SandboxConditionReady)))
147-
g.Expect(condition.ObservedGeneration).To(Equal(tc.generation))
148-
g.Expect(condition.Status).To(Equal(tc.expectedStatus))
149-
g.Expect(condition.Reason).To(Equal(tc.expectedReason))
149+
require.Equal(t, sandboxv1alpha1.SandboxConditionReady.String(), condition.Type)
150+
require.Equal(t, tc.generation, condition.ObservedGeneration)
151+
require.Equal(t, tc.expectedStatus, condition.Status)
152+
require.Equal(t, tc.expectedReason, condition.Reason)
153+
})
154+
}
155+
}
156+
157+
func TestReconcilePod(t *testing.T) {
158+
sandboxName := "sandbox-name"
159+
sandboxNs := "sandbox-ns"
160+
nameHash := "name-hash"
161+
sandboxObj := &sandboxv1alpha1.Sandbox{
162+
ObjectMeta: metav1.ObjectMeta{
163+
Name: sandboxName,
164+
Namespace: sandboxNs,
165+
},
166+
Spec: sandboxv1alpha1.SandboxSpec{
167+
PodTemplate: sandboxv1alpha1.PodTemplate{
168+
Spec: corev1.PodSpec{
169+
Containers: []corev1.Container{
170+
{
171+
Name: "test-container",
172+
},
173+
},
174+
},
175+
ObjectMeta: sandboxv1alpha1.PodMetadata{
176+
Labels: map[string]string{
177+
"custom-label": "label-val",
178+
},
179+
Annotations: map[string]string{
180+
"custom-annotation": "anno-val",
181+
},
182+
},
183+
},
184+
},
185+
}
186+
testCases := []struct {
187+
name string
188+
initialObjs []runtime.Object
189+
sandbox *sandboxv1alpha1.Sandbox
190+
wantPod *corev1.Pod
191+
}{
192+
{
193+
name: "no-op if Pod already exists",
194+
initialObjs: []runtime.Object{
195+
&corev1.Pod{
196+
ObjectMeta: metav1.ObjectMeta{
197+
Name: sandboxName,
198+
Namespace: sandboxNs,
199+
ResourceVersion: "1",
200+
},
201+
Spec: corev1.PodSpec{
202+
Containers: []corev1.Container{
203+
{
204+
Name: "foo",
205+
},
206+
},
207+
},
208+
},
209+
},
210+
sandbox: sandboxObj,
211+
wantPod: &corev1.Pod{ // Pod is not updated
212+
ObjectMeta: metav1.ObjectMeta{
213+
Name: sandboxName,
214+
Namespace: sandboxNs,
215+
ResourceVersion: "1",
216+
},
217+
Spec: corev1.PodSpec{
218+
Containers: []corev1.Container{
219+
{
220+
Name: "foo",
221+
},
222+
},
223+
},
224+
},
225+
},
226+
{
227+
name: "reconcilePod creates a new Pod",
228+
sandbox: sandboxObj,
229+
wantPod: &corev1.Pod{
230+
ObjectMeta: metav1.ObjectMeta{
231+
Name: sandboxName,
232+
Namespace: sandboxNs,
233+
ResourceVersion: "1",
234+
Labels: map[string]string{
235+
"agents.x-k8s.io/sandbox-name-hash": nameHash,
236+
"custom-label": "label-val",
237+
},
238+
Annotations: map[string]string{
239+
"custom-annotation": "anno-val",
240+
},
241+
OwnerReferences: []metav1.OwnerReference{
242+
{
243+
APIVersion: "agents.x-k8s.io/v1alpha1",
244+
Kind: "Sandbox",
245+
Name: sandboxName,
246+
Controller: ptr.To(true),
247+
BlockOwnerDeletion: ptr.To(true),
248+
},
249+
},
250+
},
251+
Spec: corev1.PodSpec{
252+
Containers: []corev1.Container{
253+
{
254+
Name: "test-container",
255+
},
256+
},
257+
},
258+
},
259+
},
260+
}
261+
262+
for _, tc := range testCases {
263+
t.Run(tc.name, func(t *testing.T) {
264+
r := SandboxReconciler{
265+
Client: fake.NewFakeClient(tc.initialObjs...),
266+
Scheme: Scheme,
267+
}
268+
269+
pod, err := r.reconcilePod(t.Context(), tc.sandbox, nameHash)
270+
require.NoError(t, err)
271+
require.Equal(t, tc.wantPod, pod)
272+
// Validate the Pod from the "cluster" (fake client)
273+
livePod := &corev1.Pod{}
274+
err = r.Get(t.Context(), types.NamespacedName{Name: pod.Name, Namespace: pod.Namespace}, livePod)
275+
require.NoError(t, err)
276+
require.Equal(t, tc.wantPod, livePod)
150277
})
151278
}
152279
}

go.mod

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ module sigs.k8s.io/agent-sandbox
33
go 1.24.4
44

55
require (
6-
github.com/onsi/gomega v1.37.0
6+
github.com/stretchr/testify v1.10.0
77
k8s.io/api v0.34.0
88
k8s.io/apimachinery v0.34.0
99
k8s.io/client-go v0.34.0
10+
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397
1011
sigs.k8s.io/controller-runtime v0.22.1
1112
)
1213

@@ -40,6 +41,8 @@ require (
4041
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
4142
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
4243
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
44+
github.com/onsi/ginkgo/v2 v2.23.3 // indirect
45+
github.com/onsi/gomega v1.37.0 // indirect
4346
github.com/pkg/errors v0.9.1 // indirect
4447
github.com/pmezard/go-difflib v1.0.0 // indirect
4548
github.com/prometheus/client_golang v1.22.0 // indirect
@@ -70,7 +73,6 @@ require (
7073
k8s.io/apiextensions-apiserver v0.34.0 // indirect
7174
k8s.io/klog/v2 v2.130.1 // indirect
7275
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect
73-
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
7476
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
7577
sigs.k8s.io/randfill v1.0.0 // indirect
7678
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect

0 commit comments

Comments
 (0)