@@ -19,6 +19,7 @@ package initializer
1919import (
2020 "bytes"
2121 "context"
22+ "os"
2223 "testing"
2324
2425 "github.com/google/go-cmp/cmp"
@@ -37,11 +38,136 @@ import (
3738 "github.com/crossplane/crossplane-runtime/v2/pkg/test"
3839)
3940
40- func TestWebhookConfigurations (t * testing.T ) {
41+ func TestSecretCAProvider (t * testing.T ) {
4142 type args struct {
4243 kube client.Client
43- svc admv1.ServiceReference
44- opts []WebhookConfigurationsOption
44+ ref types.NamespacedName
45+ }
46+
47+ type want struct {
48+ ca []byte
49+ err error
50+ }
51+
52+ cases := map [string ]struct {
53+ reason string
54+ args args
55+ want want
56+ }{
57+ "Success" : {
58+ reason : "It should return the CA bundle from the secret." ,
59+ args : args {
60+ ref : types.NamespacedName {Name : "my-secret" , Namespace : "ns" },
61+ kube : & test.MockClient {
62+ MockGet : func (_ context.Context , _ client.ObjectKey , obj client.Object ) error {
63+ s := & corev1.Secret {
64+ Data : map [string ][]byte {"tls.crt" : []byte ("CABUNDLE" )},
65+ }
66+ s .DeepCopyInto (obj .(* corev1.Secret ))
67+ return nil
68+ },
69+ },
70+ },
71+ want : want {
72+ ca : []byte ("CABUNDLE" ),
73+ },
74+ },
75+ "GetError" : {
76+ reason : "It should return an error if the secret cannot be retrieved." ,
77+ args : args {
78+ ref : types.NamespacedName {Name : "my-secret" , Namespace : "ns" },
79+ kube : & test.MockClient {MockGet : test .NewMockGetFn (errBoom )},
80+ },
81+ want : want {
82+ err : errors .Wrap (errBoom , errGetWebhookSecret ),
83+ },
84+ },
85+ "EmptyCert" : {
86+ reason : "It should return an error if the secret has no tls.crt data." ,
87+ args : args {
88+ ref : types.NamespacedName {Name : "my-secret" , Namespace : "ns" },
89+ kube : & test.MockClient {MockGet : test .NewMockGetFn (nil )},
90+ },
91+ want : want {
92+ err : errors .Errorf (errFmtNoTLSCrtInSecret , "ns/my-secret" ),
93+ },
94+ },
95+ }
96+ for name , tc := range cases {
97+ t .Run (name , func (t * testing.T ) {
98+ p := & SecretCAProvider {SecretRef : tc .args .ref }
99+ ca , err := p .GetCABundle (context .TODO (), tc .args .kube )
100+ if diff := cmp .Diff (tc .want .err , err , test .EquateErrors ()); diff != "" {
101+ t .Errorf ("\n %s\n GetCABundle(...): -want err, +got err:\n %s" , tc .reason , diff )
102+ }
103+ if diff := cmp .Diff (tc .want .ca , ca ); diff != "" {
104+ t .Errorf ("\n %s\n GetCABundle(...): -want ca, +got ca:\n %s" , tc .reason , diff )
105+ }
106+ })
107+ }
108+ }
109+
110+ func TestFileCAProvider (t * testing.T ) {
111+ type want struct {
112+ ca []byte
113+ err error
114+ }
115+
116+ validDir := t .TempDir ()
117+ validPath := validDir + "/ca.crt"
118+ _ = os .WriteFile (validPath , []byte ("CABUNDLE" ), 0o644 )
119+
120+ emptyDir := t .TempDir ()
121+ emptyPath := emptyDir + "/ca.crt"
122+ _ = os .WriteFile (emptyPath , []byte {}, 0o644 )
123+
124+ cases := map [string ]struct {
125+ reason string
126+ path string
127+ want want
128+ }{
129+ "Success" : {
130+ reason : "It should return the CA bundle from the file." ,
131+ path : validPath ,
132+ want : want {
133+ ca : []byte ("CABUNDLE" ),
134+ },
135+ },
136+ "FileNotFound" : {
137+ reason : "It should return an error if the file does not exist." ,
138+ path : "/nonexistent/ca.crt" ,
139+ want : want {
140+ err : errors .Wrap (errors .New ("open /nonexistent/ca.crt: no such file or directory" ), errReadCABundleFile ),
141+ },
142+ },
143+ "EmptyFile" : {
144+ reason : "It should return an error if the file is empty." ,
145+ path : emptyPath ,
146+ want : want {
147+ err : errors .New (errEmptyCABundleFile ),
148+ },
149+ },
150+ }
151+ for name , tc := range cases {
152+ t .Run (name , func (t * testing.T ) {
153+ p := & FileCAProvider {Path : tc .path }
154+ ca , err := p .GetCABundle (context .TODO (), nil )
155+ if diff := cmp .Diff (tc .want .err , err , test .EquateErrors ()); diff != "" {
156+ t .Errorf ("\n %s\n GetCABundle(...): -want err, +got err:\n %s" , tc .reason , diff )
157+ }
158+ if diff := cmp .Diff (tc .want .ca , ca ); diff != "" {
159+ t .Errorf ("\n %s\n GetCABundle(...): -want ca, +got ca:\n %s" , tc .reason , diff )
160+ }
161+ })
162+ }
163+ }
164+
165+ func TestWebhookConfigurations (t * testing.T ) {
166+ type args struct {
167+ kube client.Client
168+ caProvider WebhookCAProvider
169+ svc admv1.ServiceReference
170+ opts []WebhookConfigurationsOption
45171 }
46172
47173 type want struct {
@@ -71,14 +197,23 @@ func TestWebhookConfigurations(t *testing.T) {
71197 Port : & p ,
72198 }
73199
200+ caDir := t .TempDir ()
201+ caFilePath := caDir + "/ca.crt"
202+ _ = os .WriteFile (caFilePath , []byte ("CABUNDLE" ), 0o644 )
203+
204+ emptyCADir := t .TempDir ()
205+ emptyCAPath := emptyCADir + "/ca.crt"
206+ _ = os .WriteFile (emptyCAPath , []byte {}, 0o644 )
207+
74208 cases := map [string ]struct {
75209 reason string
76210 args
77211 want
78212 }{
79- "Success " : {
80- reason : "If a proper webhook TLS is given, then webhook configurations should have the configs injected and operations should succeed" ,
213+ "SecretCAProviderSuccess " : {
214+ reason : "If a proper webhook TLS secret is given, then webhook configurations should have the configs injected and operations should succeed" ,
81215 args : args {
216+ caProvider : & SecretCAProvider {SecretRef : types.NamespacedName {}},
82217 opts : []WebhookConfigurationsOption {
83218 WithWebhookConfigurationsFs (fs ),
84219 },
@@ -113,9 +248,10 @@ func TestWebhookConfigurations(t *testing.T) {
113248 },
114249 },
115250 },
116- "CertNotFound " : {
251+ "SecretCAProviderCertNotFound " : {
117252 reason : "If TLS Secret cannot be found, then it should not proceed" ,
118253 args : args {
254+ caProvider : & SecretCAProvider {SecretRef : types.NamespacedName {}},
119255 opts : []WebhookConfigurationsOption {
120256 WithWebhookConfigurationsFs (fs ),
121257 },
@@ -127,9 +263,10 @@ func TestWebhookConfigurations(t *testing.T) {
127263 err : errors .Wrap (errBoom , errGetWebhookSecret ),
128264 },
129265 },
130- "CertKeyEmpty " : {
266+ "SecretCAProviderCertKeyEmpty " : {
131267 reason : "If the TLS Secret does not have a CA bundle, then it should not proceed" ,
132268 args : args {
269+ caProvider : & SecretCAProvider {SecretRef : types.NamespacedName {}},
133270 opts : []WebhookConfigurationsOption {
134271 WithWebhookConfigurationsFs (fs ),
135272 },
@@ -141,9 +278,10 @@ func TestWebhookConfigurations(t *testing.T) {
141278 err : errors .Errorf (errFmtNoTLSCrtInSecret , "/" ),
142279 },
143280 },
144- "ApplyFailed " : {
281+ "SecretCAProviderApplyFailed " : {
145282 reason : "If it cannot apply webhook configurations, then it should not proceed" ,
146283 args : args {
284+ caProvider : & SecretCAProvider {SecretRef : types.NamespacedName {}},
147285 opts : []WebhookConfigurationsOption {
148286 WithWebhookConfigurationsFs (fs ),
149287 },
@@ -165,6 +303,7 @@ func TestWebhookConfigurations(t *testing.T) {
165303 "NonWebhookType" : {
166304 reason : "Only webhook configuration types can be processed" ,
167305 args : args {
306+ caProvider : & SecretCAProvider {SecretRef : types.NamespacedName {}},
168307 opts : []WebhookConfigurationsOption {
169308 WithWebhookConfigurationsFs (fsWithMixedTypes ),
170309 },
@@ -183,13 +322,73 @@ func TestWebhookConfigurations(t *testing.T) {
183322 err : errors .Errorf ("only MutatingWebhookConfiguration and ValidatingWebhookConfiguration kinds are accepted, got %s" , "*v1.CustomResourceDefinition" ),
184323 },
185324 },
325+ "FileCAProviderSuccess" : {
326+ reason : "If a valid CA file is given via FileCAProvider, webhook configurations should be injected successfully" ,
327+ args : args {
328+ caProvider : & FileCAProvider {Path : caFilePath },
329+ opts : []WebhookConfigurationsOption {
330+ WithWebhookConfigurationsFs (fs ),
331+ },
332+ svc : svc ,
333+ kube : & test.MockClient {
334+ MockGet : func (_ context.Context , _ client.ObjectKey , _ client.Object ) error {
335+ return kerrors .NewNotFound (schema.GroupResource {}, "" )
336+ },
337+ MockCreate : func (_ context.Context , obj client.Object , _ ... client.CreateOption ) error {
338+ switch c := obj .(type ) {
339+ case * admv1.ValidatingWebhookConfiguration :
340+ for _ , w := range c .Webhooks {
341+ if ! bytes .Equal (w .ClientConfig .CABundle , []byte ("CABUNDLE" )) {
342+ t .Errorf ("unexpected certificate bundle content: %s" , string (w .ClientConfig .CABundle ))
343+ }
344+ }
345+ case * admv1.MutatingWebhookConfiguration :
346+ for _ , w := range c .Webhooks {
347+ if ! bytes .Equal (w .ClientConfig .CABundle , []byte ("CABUNDLE" )) {
348+ t .Errorf ("unexpected certificate bundle content: %s" , string (w .ClientConfig .CABundle ))
349+ }
350+ }
351+ default :
352+ t .Error ("unexpected type" )
353+ }
354+ return nil
355+ },
356+ },
357+ },
358+ },
359+ "FileCAProviderFileNotFound" : {
360+ reason : "If the CA file does not exist, it should return an error" ,
361+ args : args {
362+ caProvider : & FileCAProvider {Path : "/nonexistent/ca.crt" },
363+ opts : []WebhookConfigurationsOption {
364+ WithWebhookConfigurationsFs (fs ),
365+ },
366+ kube : & test.MockClient {},
367+ },
368+ want : want {
369+ err : errors .Wrap (errors .New ("open /nonexistent/ca.crt: no such file or directory" ), errReadCABundleFile ),
370+ },
371+ },
372+ "FileCAProviderEmptyFile" : {
373+ reason : "If the CA file is empty, it should return an error" ,
374+ args : args {
375+ caProvider : & FileCAProvider {Path : emptyCAPath },
376+ opts : []WebhookConfigurationsOption {
377+ WithWebhookConfigurationsFs (fs ),
378+ },
379+ kube : & test.MockClient {},
380+ },
381+ want : want {
382+ err : errors .New (errEmptyCABundleFile ),
383+ },
384+ },
186385 }
187386 for name , tc := range cases {
188387 t .Run (name , func (t * testing.T ) {
189388 err := NewWebhookConfigurations (
190389 "/webhooks" ,
191390 sch ,
192- & SecretCAProvider { SecretRef : types. NamespacedName {}} ,
391+ tc . args . caProvider ,
193392 tc .args .svc ,
194393 tc .opts ... ).Run (context .TODO (), tc .kube )
195394 if diff := cmp .Diff (tc .err , err , test .EquateErrors ()); diff != "" {
0 commit comments