@@ -21,64 +21,230 @@ import (
2121
2222 . "github.com/onsi/ginkgo/v2"
2323 . "github.com/onsi/gomega"
24+ corev1 "k8s.io/api/core/v1"
25+ rbacv1 "k8s.io/api/rbac/v1"
2426 "k8s.io/apimachinery/pkg/api/errors"
27+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2528 "k8s.io/apimachinery/pkg/types"
29+ "k8s.io/client-go/kubernetes"
2630 "sigs.k8s.io/controller-runtime/pkg/reconcile"
2731
28- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29-
3032 assistantv1beta1 "github.com/openstack-k8s-operators/openstack-operator/api/assistant/v1beta1"
3133)
3234
3335var _ = Describe ("OpenStackAssistant Controller" , func () {
34- Context ("When reconciling a resource" , func () {
35- const resourceName = "test-resource"
36+ const resourceName = "test-assistant"
37+ const namespace = "default"
38+ const providerSecretName = "test-provider-secret"
39+
40+ ctx := context .Background ()
3641
37- ctx := context .Background ()
42+ typeNamespacedName := types.NamespacedName {
43+ Name : resourceName ,
44+ Namespace : namespace ,
45+ }
3846
39- typeNamespacedName := types.NamespacedName {
40- Name : resourceName ,
41- Namespace : "default" , // TODO(user):Modify as needed
47+ BeforeEach (func () {
48+ By ("creating the provider secret" )
49+ secret := & corev1.Secret {
50+ ObjectMeta : metav1.ObjectMeta {
51+ Name : providerSecretName ,
52+ Namespace : namespace ,
53+ },
54+ Data : map [string ][]byte {
55+ "lightspeed.json" : []byte (`{"name":"lightspeed"}` ),
56+ },
4257 }
43- openstackassistant := & assistantv1beta1.OpenStackAssistant {}
58+ err := k8sClient .Get (ctx , types.NamespacedName {Name : providerSecretName , Namespace : namespace }, & corev1.Secret {})
59+ if errors .IsNotFound (err ) {
60+ Expect (k8sClient .Create (ctx , secret )).To (Succeed ())
61+ }
62+ })
4463
64+ Context ("When creating an OpenStackAssistant resource" , func () {
4565 BeforeEach (func () {
46- By ("creating the custom resource for the Kind OpenStackAssistant" )
47- err := k8sClient .Get (ctx , typeNamespacedName , openstackassistant )
48- if err != nil && errors .IsNotFound (err ) {
49- resource := & assistantv1beta1.OpenStackAssistant {
50- ObjectMeta : metav1.ObjectMeta {
51- Name : resourceName ,
52- Namespace : "default" ,
66+ By ("creating the OpenStackAssistant resource" )
67+ resource := & assistantv1beta1.OpenStackAssistant {
68+ ObjectMeta : metav1.ObjectMeta {
69+ Name : resourceName ,
70+ Namespace : namespace ,
71+ },
72+ Spec : assistantv1beta1.OpenStackAssistantSpec {
73+ ContainerImage : "quay.io/dprince/goose:oc-fedora" ,
74+ Provider : assistantv1beta1 .ProviderGoose ,
75+ LightspeedStack : assistantv1beta1.LightspeedStackSpec {
76+ ProviderSecret : providerSecretName ,
5377 },
54- // TODO(user): Specify other spec details if needed.
55- }
78+ },
79+ }
80+ err := k8sClient .Get (ctx , typeNamespacedName , & assistantv1beta1.OpenStackAssistant {})
81+ if errors .IsNotFound (err ) {
5682 Expect (k8sClient .Create (ctx , resource )).To (Succeed ())
5783 }
5884 })
5985
6086 AfterEach (func () {
61- // TODO(user): Cleanup logic after each test, like removing the resource instance.
6287 resource := & assistantv1beta1.OpenStackAssistant {}
6388 err := k8sClient .Get (ctx , typeNamespacedName , resource )
89+ if err == nil {
90+ resource .Finalizers = nil
91+ Expect (k8sClient .Update (ctx , resource )).To (Succeed ())
92+ Expect (k8sClient .Delete (ctx , resource )).To (Succeed ())
93+ }
94+ // Clean up cluster-scoped resources
95+ clusterRoleName := "openstackassistant-" + namespace + "-" + resourceName
96+ cr := & rbacv1.ClusterRole {}
97+ if err := k8sClient .Get (ctx , types.NamespacedName {Name : clusterRoleName }, cr ); err == nil {
98+ _ = k8sClient .Delete (ctx , cr )
99+ }
100+ crb := & rbacv1.ClusterRoleBinding {}
101+ if err := k8sClient .Get (ctx , types.NamespacedName {Name : clusterRoleName }, crb ); err == nil {
102+ _ = k8sClient .Delete (ctx , crb )
103+ }
104+ })
105+
106+ It ("should add a finalizer on first reconcile" , func () {
107+ reconciler := & OpenStackAssistantReconciler {
108+ Client : k8sClient ,
109+ Scheme : k8sClient .Scheme (),
110+ Kclient : kubernetes .NewForConfigOrDie (cfg ),
111+ }
112+
113+ result , err := reconciler .Reconcile (ctx , reconcile.Request {
114+ NamespacedName : typeNamespacedName ,
115+ })
64116 Expect (err ).NotTo (HaveOccurred ())
117+ Expect (result .Requeue ).To (BeTrue ())
65118
66- By ("Cleanup the specific resource instance OpenStackAssistant" )
67- Expect (k8sClient .Delete (ctx , resource )).To (Succeed ())
119+ updated := & assistantv1beta1.OpenStackAssistant {}
120+ Expect (k8sClient .Get (ctx , typeNamespacedName , updated )).To (Succeed ())
121+ Expect (updated .Finalizers ).To (ContainElement (assistantFinalizer ))
68122 })
69- It ("should successfully reconcile the resource" , func () {
70- By ("Reconciling the created resource" )
71- controllerReconciler := & OpenStackAssistantReconciler {
72- Client : k8sClient ,
73- Scheme : k8sClient .Scheme (),
123+
124+ It ("should create an entrypoint ConfigMap after reconciliation" , func () {
125+ reconciler := & OpenStackAssistantReconciler {
126+ Client : k8sClient ,
127+ Scheme : k8sClient .Scheme (),
128+ Kclient : kubernetes .NewForConfigOrDie (cfg ),
74129 }
75130
76- _ , err := controllerReconciler .Reconcile (ctx , reconcile.Request {
131+ // First reconcile adds finalizer
132+ _ , err := reconciler .Reconcile (ctx , reconcile.Request {
77133 NamespacedName : typeNamespacedName ,
78134 })
79135 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.
136+
137+ // Second reconcile does the actual work
138+ _ , err = reconciler .Reconcile (ctx , reconcile.Request {
139+ NamespacedName : typeNamespacedName ,
140+ })
141+ Expect (err ).NotTo (HaveOccurred ())
142+
143+ cm := & corev1.ConfigMap {}
144+ Expect (k8sClient .Get (ctx , types.NamespacedName {
145+ Name : resourceName + "-entrypoint" ,
146+ Namespace : namespace ,
147+ }, cm )).To (Succeed ())
148+ Expect (cm .Data ).To (HaveKey ("entrypoint.sh" ))
149+ Expect (cm .Data ["entrypoint.sh" ]).To (ContainSubstring ("sleep infinity" ))
150+ })
151+
152+ It ("should create a ClusterRole and ClusterRoleBinding" , func () {
153+ reconciler := & OpenStackAssistantReconciler {
154+ Client : k8sClient ,
155+ Scheme : k8sClient .Scheme (),
156+ Kclient : kubernetes .NewForConfigOrDie (cfg ),
157+ }
158+
159+ // First reconcile adds finalizer
160+ _ , _ = reconciler .Reconcile (ctx , reconcile.Request {NamespacedName : typeNamespacedName })
161+ // Second reconcile creates resources
162+ _ , err := reconciler .Reconcile (ctx , reconcile.Request {NamespacedName : typeNamespacedName })
163+ Expect (err ).NotTo (HaveOccurred ())
164+
165+ clusterRoleName := "openstackassistant-" + namespace + "-" + resourceName
166+
167+ cr := & rbacv1.ClusterRole {}
168+ Expect (k8sClient .Get (ctx , types.NamespacedName {Name : clusterRoleName }, cr )).To (Succeed ())
169+ Expect (cr .Rules ).NotTo (BeEmpty ())
170+
171+ hasNodesRule := false
172+ for _ , rule := range cr .Rules {
173+ for _ , resource := range rule .Resources {
174+ if resource == "nodes" {
175+ hasNodesRule = true
176+ break
177+ }
178+ }
179+ }
180+ Expect (hasNodesRule ).To (BeTrue (), "ClusterRole should include nodes resource" )
181+
182+ crb := & rbacv1.ClusterRoleBinding {}
183+ Expect (k8sClient .Get (ctx , types.NamespacedName {Name : clusterRoleName }, crb )).To (Succeed ())
184+ Expect (crb .RoleRef .Name ).To (Equal (clusterRoleName ))
185+ Expect (crb .Subjects ).To (HaveLen (1 ))
186+ Expect (crb .Subjects [0 ].Name ).To (Equal ("openstackassistant-" + resourceName ))
187+ Expect (crb .Subjects [0 ].Namespace ).To (Equal (namespace ))
188+ })
189+
190+ It ("should create a Pod" , func () {
191+ reconciler := & OpenStackAssistantReconciler {
192+ Client : k8sClient ,
193+ Scheme : k8sClient .Scheme (),
194+ Kclient : kubernetes .NewForConfigOrDie (cfg ),
195+ }
196+
197+ // First reconcile adds finalizer
198+ _ , _ = reconciler .Reconcile (ctx , reconcile.Request {NamespacedName : typeNamespacedName })
199+ // Second reconcile creates resources
200+ _ , err := reconciler .Reconcile (ctx , reconcile.Request {NamespacedName : typeNamespacedName })
201+ Expect (err ).NotTo (HaveOccurred ())
202+
203+ pod := & corev1.Pod {}
204+ Expect (k8sClient .Get (ctx , typeNamespacedName , pod )).To (Succeed ())
205+ Expect (pod .Spec .Containers ).To (HaveLen (1 ))
206+ Expect (pod .Spec .Containers [0 ].Name ).To (Equal ("goose" ))
207+ Expect (pod .Spec .Containers [0 ].Image ).To (Equal ("quay.io/dprince/goose:oc-fedora" ))
208+ Expect (pod .Labels ).To (HaveKeyWithValue ("service" , "openstackassistant" ))
209+ })
210+
211+ It ("should set status conditions after reconciliation" , func () {
212+ reconciler := & OpenStackAssistantReconciler {
213+ Client : k8sClient ,
214+ Scheme : k8sClient .Scheme (),
215+ Kclient : kubernetes .NewForConfigOrDie (cfg ),
216+ }
217+
218+ // First reconcile adds finalizer
219+ _ , _ = reconciler .Reconcile (ctx , reconcile.Request {NamespacedName : typeNamespacedName })
220+ // Second reconcile creates resources
221+ _ , err := reconciler .Reconcile (ctx , reconcile.Request {NamespacedName : typeNamespacedName })
222+ Expect (err ).NotTo (HaveOccurred ())
223+
224+ instance := & assistantv1beta1.OpenStackAssistant {}
225+ Expect (k8sClient .Get (ctx , typeNamespacedName , instance )).To (Succeed ())
226+ Expect (instance .Status .Conditions ).NotTo (BeEmpty ())
227+ Expect (instance .Status .PodName ).To (Equal (resourceName ))
228+ Expect (instance .Status .Hash ).To (HaveKey ("podSpec" ))
229+ })
230+ })
231+
232+ Context ("When the CR does not exist" , func () {
233+ It ("should return no error" , func () {
234+ reconciler := & OpenStackAssistantReconciler {
235+ Client : k8sClient ,
236+ Scheme : k8sClient .Scheme (),
237+ Kclient : kubernetes .NewForConfigOrDie (cfg ),
238+ }
239+
240+ result , err := reconciler .Reconcile (ctx , reconcile.Request {
241+ NamespacedName : types.NamespacedName {
242+ Name : "nonexistent" ,
243+ Namespace : namespace ,
244+ },
245+ })
246+ Expect (err ).NotTo (HaveOccurred ())
247+ Expect (result ).To (Equal (reconcile.Result {}))
82248 })
83249 })
84250})
0 commit comments