@@ -18,67 +18,282 @@ package controller
1818
1919import (
2020 "context"
21+ "time"
2122
2223 . "github.com/onsi/ginkgo/v2"
2324 . "github.com/onsi/gomega"
2425 "k8s.io/apimachinery/pkg/api/errors"
26+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2527 "k8s.io/apimachinery/pkg/types"
2628 "sigs.k8s.io/controller-runtime/pkg/reconcile"
2729
28- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29-
3030 orchestratev1alpha1 "http-operator/api/v1alpha1"
3131)
3232
3333var _ = Describe ("HttpBin Controller" , func () {
34+ const (
35+ timeout = time .Second * 10
36+ interval = time .Millisecond * 250
37+ )
38+
3439 Context ("When reconciling a resource" , func () {
35- const resourceName = "test-resource "
40+ const resourceName = "test-httpbin "
3641
3742 ctx := context .Background ()
3843
3944 typeNamespacedName := types.NamespacedName {
4045 Name : resourceName ,
41- Namespace : "default" , // TODO(user):Modify as needed
46+ Namespace : "default" ,
4247 }
43- httpbin := & orchestratev1alpha1.HttpBin {}
4448
4549 BeforeEach (func () {
4650 By ("creating the custom resource for the Kind HttpBin" )
51+ httpbin := & orchestratev1alpha1.HttpBin {}
4752 err := k8sClient .Get (ctx , typeNamespacedName , httpbin )
4853 if err != nil && errors .IsNotFound (err ) {
4954 resource := & orchestratev1alpha1.HttpBin {
5055 ObjectMeta : metav1.ObjectMeta {
5156 Name : resourceName ,
5257 Namespace : "default" ,
5358 },
54- // TODO(user): Specify other spec details if needed.
59+ Spec : orchestratev1alpha1.HttpBinSpec {
60+ EnableHTTPS : false ,
61+ Region : "us-east-1" ,
62+ },
5563 }
5664 Expect (k8sClient .Create (ctx , resource )).To (Succeed ())
5765 }
5866 })
5967
6068 AfterEach (func () {
61- // TODO(user): Cleanup logic after each test, like removing the resource instance.
6269 resource := & orchestratev1alpha1.HttpBin {}
6370 err := k8sClient .Get (ctx , typeNamespacedName , resource )
71+ if err == nil {
72+ By ("Cleanup the specific resource instance HttpBin" )
73+ Expect (k8sClient .Delete (ctx , resource )).To (Succeed ())
74+ }
75+ // Also cleanup any HttpBinDeployment that was created
76+ deployment := & orchestratev1alpha1.HttpBinDeployment {}
77+ err = k8sClient .Get (ctx , typeNamespacedName , deployment )
78+ if err == nil {
79+ Expect (k8sClient .Delete (ctx , deployment )).To (Succeed ())
80+ }
81+ })
82+
83+ It ("should successfully reconcile and create HttpBinDeployment" , func () {
84+ By ("Reconciling the created resource" )
85+ controllerReconciler := & HttpBinReconciler {
86+ RemoteClient : k8sClient ,
87+ Scheme : k8sClient .Scheme (),
88+ }
89+
90+ result , err := controllerReconciler .Reconcile (ctx , reconcile.Request {
91+ NamespacedName : typeNamespacedName ,
92+ })
6493 Expect (err ).NotTo (HaveOccurred ())
94+ // Check that reconciliation requests a requeue
95+ Expect (result ).NotTo (Equal (reconcile.Result {}))
96+
97+ By ("Verifying HttpBinDeployment was created" )
98+ httpBinDeployment := & orchestratev1alpha1.HttpBinDeployment {}
99+ Eventually (func () error {
100+ return k8sClient .Get (ctx , typeNamespacedName , httpBinDeployment )
101+ }, timeout , interval ).Should (Succeed ())
65102
66- By ("Cleanup the specific resource instance HttpBin" )
67- Expect (k8sClient .Delete (ctx , resource )).To (Succeed ())
103+ Expect (httpBinDeployment .Spec .Service .Port ).To (Equal (int32 (443 )))
68104 })
69- It ("should successfully reconcile the resource" , func () {
70- By ("Reconciling the created resource" )
105+
106+ It ("should set correct port when EnableHTTPS is true" , func () {
107+ By ("Creating HttpBin with EnableHTTPS=true" )
108+ httpsResourceName := "test-httpbin-https"
109+ httpsNamespacedName := types.NamespacedName {
110+ Name : httpsResourceName ,
111+ Namespace : "default" ,
112+ }
113+
114+ resource := & orchestratev1alpha1.HttpBin {
115+ ObjectMeta : metav1.ObjectMeta {
116+ Name : httpsResourceName ,
117+ Namespace : "default" ,
118+ },
119+ Spec : orchestratev1alpha1.HttpBinSpec {
120+ EnableHTTPS : true ,
121+ },
122+ }
123+ Expect (k8sClient .Create (ctx , resource )).To (Succeed ())
124+
125+ defer func () {
126+ // Cleanup
127+ _ = k8sClient .Delete (ctx , resource )
128+ deployment := & orchestratev1alpha1.HttpBinDeployment {}
129+ _ = k8sClient .Get (ctx , httpsNamespacedName , deployment )
130+ _ = k8sClient .Delete (ctx , deployment )
131+ }()
132+
133+ By ("Reconciling the resource" )
134+ controllerReconciler := & HttpBinReconciler {
135+ RemoteClient : k8sClient ,
136+ Scheme : k8sClient .Scheme (),
137+ }
138+
139+ _ , err := controllerReconciler .Reconcile (ctx , reconcile.Request {
140+ NamespacedName : httpsNamespacedName ,
141+ })
142+ Expect (err ).NotTo (HaveOccurred ())
143+
144+ By ("Verifying HttpBinDeployment has HTTPS port" )
145+ httpBinDeployment := & orchestratev1alpha1.HttpBinDeployment {}
146+ Eventually (func () error {
147+ return k8sClient .Get (ctx , httpsNamespacedName , httpBinDeployment )
148+ }, timeout , interval ).Should (Succeed ())
149+
150+ Expect (httpBinDeployment .Spec .Service .Port ).To (Equal (int32 (8443 )))
151+ })
152+
153+ It ("should handle not found resource gracefully" , func () {
154+ By ("Reconciling a non-existent resource" )
155+ controllerReconciler := & HttpBinReconciler {
156+ RemoteClient : k8sClient ,
157+ Scheme : k8sClient .Scheme (),
158+ }
159+
160+ result , err := controllerReconciler .Reconcile (ctx , reconcile.Request {
161+ NamespacedName : types.NamespacedName {
162+ Name : "non-existent" ,
163+ Namespace : "default" ,
164+ },
165+ })
166+ Expect (err ).NotTo (HaveOccurred ())
167+ Expect (result ).To (Equal (reconcile.Result {}))
168+ })
169+
170+ It ("should propagate status from HttpBinDeployment to HttpBin" , func () {
171+ By ("Creating HttpBin and HttpBinDeployment with status" )
71172 controllerReconciler := & HttpBinReconciler {
72173 RemoteClient : k8sClient ,
73174 Scheme : k8sClient .Scheme (),
74175 }
75176
177+ // First reconcile to create HttpBinDeployment
76178 _ , err := controllerReconciler .Reconcile (ctx , reconcile.Request {
77179 NamespacedName : typeNamespacedName ,
78180 })
79181 Expect (err ).NotTo (HaveOccurred ())
80- // TODO(user): Add more specific assertions depending on your controller's reconciliation logic.
81- // Example: If you expect a certain status condition after reconciliation, verify it here.
182+
183+ By ("Updating HttpBinDeployment status" )
184+ httpBinDeployment := & orchestratev1alpha1.HttpBinDeployment {}
185+ Eventually (func () error {
186+ return k8sClient .Get (ctx , typeNamespacedName , httpBinDeployment )
187+ }, timeout , interval ).Should (Succeed ())
188+
189+ httpBinDeployment .Status .URL = "https://test.example.com"
190+ httpBinDeployment .Status .IsDeploymentReady = true
191+ Expect (k8sClient .Status ().Update (ctx , httpBinDeployment )).To (Succeed ())
192+
193+ By ("Reconciling again to propagate status" )
194+ _ , err = controllerReconciler .Reconcile (ctx , reconcile.Request {
195+ NamespacedName : typeNamespacedName ,
196+ })
197+ Expect (err ).NotTo (HaveOccurred ())
198+
199+ By ("Verifying HttpBin status was updated" )
200+ httpBin := & orchestratev1alpha1.HttpBin {}
201+ Expect (k8sClient .Get (ctx , typeNamespacedName , httpBin )).To (Succeed ())
202+ Expect (httpBin .Status .URL ).To (Equal ("https://test.example.com" ))
203+ Expect (httpBin .Status .Ready ).To (BeTrue ())
204+ })
205+
206+ It ("should copy labels from HttpBin to HttpBinDeployment" , func () {
207+ By ("Creating HttpBin with labels" )
208+ labeledResourceName := "test-httpbin-labels"
209+ labeledNamespacedName := types.NamespacedName {
210+ Name : labeledResourceName ,
211+ Namespace : "default" ,
212+ }
213+
214+ resource := & orchestratev1alpha1.HttpBin {
215+ ObjectMeta : metav1.ObjectMeta {
216+ Name : labeledResourceName ,
217+ Namespace : "default" ,
218+ Labels : map [string ]string {
219+ "custom-label" : "custom-value" ,
220+ "environment" : "test" ,
221+ },
222+ },
223+ }
224+ Expect (k8sClient .Create (ctx , resource )).To (Succeed ())
225+
226+ defer func () {
227+ _ = k8sClient .Delete (ctx , resource )
228+ deployment := & orchestratev1alpha1.HttpBinDeployment {}
229+ _ = k8sClient .Get (ctx , labeledNamespacedName , deployment )
230+ _ = k8sClient .Delete (ctx , deployment )
231+ }()
232+
233+ By ("Reconciling the resource" )
234+ controllerReconciler := & HttpBinReconciler {
235+ RemoteClient : k8sClient ,
236+ Scheme : k8sClient .Scheme (),
237+ }
238+
239+ _ , err := controllerReconciler .Reconcile (ctx , reconcile.Request {
240+ NamespacedName : labeledNamespacedName ,
241+ })
242+ Expect (err ).NotTo (HaveOccurred ())
243+
244+ By ("Verifying HttpBinDeployment has the labels" )
245+ httpBinDeployment := & orchestratev1alpha1.HttpBinDeployment {}
246+ Eventually (func () error {
247+ return k8sClient .Get (ctx , labeledNamespacedName , httpBinDeployment )
248+ }, timeout , interval ).Should (Succeed ())
249+
250+ Expect (httpBinDeployment .Labels ).To (HaveKeyWithValue ("custom-label" , "custom-value" ))
251+ Expect (httpBinDeployment .Labels ).To (HaveKeyWithValue ("environment" , "test" ))
252+ })
253+
254+ It ("should set condition when deployment is not ready" , func () {
255+ By ("Reconciling to create HttpBinDeployment" )
256+ controllerReconciler := & HttpBinReconciler {
257+ RemoteClient : k8sClient ,
258+ Scheme : k8sClient .Scheme (),
259+ }
260+
261+ _ , err := controllerReconciler .Reconcile (ctx , reconcile.Request {
262+ NamespacedName : typeNamespacedName ,
263+ })
264+ Expect (err ).NotTo (HaveOccurred ())
265+
266+ By ("Setting HttpBinDeployment status to not ready" )
267+ httpBinDeployment := & orchestratev1alpha1.HttpBinDeployment {}
268+ Eventually (func () error {
269+ return k8sClient .Get (ctx , typeNamespacedName , httpBinDeployment )
270+ }, timeout , interval ).Should (Succeed ())
271+
272+ httpBinDeployment .Status .URL = "https://test.example.com"
273+ httpBinDeployment .Status .IsDeploymentReady = false
274+ Expect (k8sClient .Status ().Update (ctx , httpBinDeployment )).To (Succeed ())
275+
276+ By ("Reconciling again" )
277+ _ , err = controllerReconciler .Reconcile (ctx , reconcile.Request {
278+ NamespacedName : typeNamespacedName ,
279+ })
280+ Expect (err ).NotTo (HaveOccurred ())
281+
282+ By ("Verifying HttpBin has progressing condition" )
283+ httpBin := & orchestratev1alpha1.HttpBin {}
284+ Expect (k8sClient .Get (ctx , typeNamespacedName , httpBin )).To (Succeed ())
285+ Expect (httpBin .Status .Ready ).To (BeFalse ())
286+
287+ // Check condition exists
288+ found := false
289+ for _ , condition := range httpBin .Status .Conditions {
290+ if condition .Type == orchestratev1alpha1 .HttpBinConditionTypeReady {
291+ found = true
292+ Expect (condition .Status ).To (Equal (metav1 .ConditionFalse ))
293+ Expect (condition .Reason ).To (Equal (orchestratev1alpha1 .HttpBinConditionReasonDeploymentProgressing ))
294+ }
295+ }
296+ Expect (found ).To (BeTrue ())
82297 })
83298 })
84299})
0 commit comments