@@ -134,6 +134,72 @@ func runTests(admissionReviewVersion string) {
134134 ExpectWithOffset (1 , w .Code ).To (Equal (http .StatusNotFound ))
135135 })
136136
137+ It ("should scaffold a defaulting webhook which recovers from panics" , func () {
138+ By ("creating a controller manager" )
139+ m , err := manager .New (cfg , manager.Options {})
140+ ExpectWithOffset (1 , err ).NotTo (HaveOccurred ())
141+
142+ By ("registering the type in the Scheme" )
143+ builder := scheme.Builder {GroupVersion : testDefaulterGVK .GroupVersion ()}
144+ builder .Register (& TestDefaulter {}, & TestDefaulterList {})
145+ err = builder .AddToScheme (m .GetScheme ())
146+ ExpectWithOffset (1 , err ).NotTo (HaveOccurred ())
147+
148+ err = WebhookManagedBy (m ).
149+ For (& TestDefaulter {Panic : true }).
150+ RecoverPanic ().
151+ Complete ()
152+ ExpectWithOffset (1 , err ).NotTo (HaveOccurred ())
153+ svr := m .GetWebhookServer ()
154+ ExpectWithOffset (1 , svr ).NotTo (BeNil ())
155+
156+ reader := strings .NewReader (`{
157+ "kind":"AdmissionReview",
158+ "apiVersion":"admission.k8s.io/` + admissionReviewVersion + `",
159+ "request":{
160+ "uid":"07e52e8d-4513-11e9-a716-42010a800270",
161+ "kind":{
162+ "group":"",
163+ "version":"v1",
164+ "kind":"TestDefaulter"
165+ },
166+ "resource":{
167+ "group":"",
168+ "version":"v1",
169+ "resource":"testdefaulter"
170+ },
171+ "namespace":"default",
172+ "operation":"CREATE",
173+ "object":{
174+ "replica":1,
175+ "panic":true
176+ },
177+ "oldObject":null
178+ }
179+ }` )
180+
181+ ctx , cancel := context .WithCancel (context .Background ())
182+ cancel ()
183+ // TODO: we may want to improve it to make it be able to inject dependencies,
184+ // but not always try to load certs and return not found error.
185+ err = svr .Start (ctx )
186+ if err != nil && ! os .IsNotExist (err ) {
187+ ExpectWithOffset (1 , err ).NotTo (HaveOccurred ())
188+ }
189+
190+ By ("sending a request to a mutating webhook path" )
191+ path := generateMutatePath (testDefaulterGVK )
192+ req := httptest .NewRequest ("POST" , "http://svc-name.svc-ns.svc" + path , reader )
193+ req .Header .Add ("Content-Type" , "application/json" )
194+ w := httptest .NewRecorder ()
195+ svr .WebhookMux .ServeHTTP (w , req )
196+ ExpectWithOffset (1 , w .Code ).To (Equal (http .StatusOK ))
197+ By ("sanity checking the response contains reasonable fields" )
198+ ExpectWithOffset (1 , w .Body ).To (ContainSubstring (`"allowed":false` ))
199+ ExpectWithOffset (1 , w .Body ).To (ContainSubstring (`"code":500` ))
200+ ExpectWithOffset (1 , w .Body ).To (ContainSubstring (`"message":"panic: injected panic [recovered]` ))
201+ })
202+
137203 It ("should scaffold a defaulting webhook with a custom defaulter" , func () {
138204 By ("creating a controller manager" )
139205 m , err := manager .New (cfg , manager.Options {})
@@ -284,6 +350,73 @@ func runTests(admissionReviewVersion string) {
284350 ExpectWithOffset (1 , w .Body ).To (ContainSubstring (`"code":403` ))
285351 })
286352
353+ It ("should scaffold a validating webhook which recovers from panics" , func () {
354+ By ("creating a controller manager" )
355+ m , err := manager .New (cfg , manager.Options {})
356+ ExpectWithOffset (1 , err ).NotTo (HaveOccurred ())
357+
358+ By ("registering the type in the Scheme" )
359+ builder := scheme.Builder {GroupVersion : testValidatorGVK .GroupVersion ()}
360+ builder .Register (& TestValidator {}, & TestValidatorList {})
361+ err = builder .AddToScheme (m .GetScheme ())
362+ ExpectWithOffset (1 , err ).NotTo (HaveOccurred ())
363+
364+ err = WebhookManagedBy (m ).
365+ For (& TestValidator {Panic : true }).
366+ RecoverPanic ().
367+ Complete ()
368+ ExpectWithOffset (1 , err ).NotTo (HaveOccurred ())
369+ svr := m .GetWebhookServer ()
370+ ExpectWithOffset (1 , svr ).NotTo (BeNil ())
371+
372+ reader := strings .NewReader (`{
373+ "kind":"AdmissionReview",
374+ "apiVersion":"admission.k8s.io/` + admissionReviewVersion + `",
375+ "request":{
376+ "uid":"07e52e8d-4513-11e9-a716-42010a800270",
377+ "kind":{
378+ "group":"",
379+ "version":"v1",
380+ "kind":"TestValidator"
381+ },
382+ "resource":{
383+ "group":"",
384+ "version":"v1",
385+ "resource":"testvalidator"
386+ },
387+ "namespace":"default",
388+ "operation":"CREATE",
389+ "object":{
390+ "replica":2,
391+ "panic":true
392+ }
393+ }
394+ }` )
395+
396+ ctx , cancel := context .WithCancel (context .Background ())
397+ cancel ()
398+ // TODO: we may want to improve it to make it be able to inject dependencies,
399+ // but not always try to load certs and return not found error.
400+ err = svr .Start (ctx )
401+ if err != nil && ! os .IsNotExist (err ) {
402+ ExpectWithOffset (1 , err ).NotTo (HaveOccurred ())
403+ }
404+
405+ By ("sending a request to a validating webhook path" )
406+ path := generateValidatePath (testValidatorGVK )
407+ _ , err = reader .Seek (0 , 0 )
408+ ExpectWithOffset (1 , err ).NotTo (HaveOccurred ())
409+ req := httptest .NewRequest ("POST" , "http://svc-name.svc-ns.svc" + path , reader )
410+ req .Header .Add ("Content-Type" , "application/json" )
411+ w := httptest .NewRecorder ()
412+ svr .WebhookMux .ServeHTTP (w , req )
413+ ExpectWithOffset (1 , w .Code ).To (Equal (http .StatusOK ))
414+ By ("sanity checking the response contains reasonable field" )
415+ ExpectWithOffset (1 , w .Body ).To (ContainSubstring (`"allowed":false` ))
416+ ExpectWithOffset (1 , w .Body ).To (ContainSubstring (`"code":500` ))
417+ ExpectWithOffset (1 , w .Body ).To (ContainSubstring (`"message":"panic: injected panic [recovered]` ))
418+ })
419+
287420 It ("should scaffold a validating webhook with a custom validator" , func () {
288421 By ("creating a controller manager" )
289422 m , err := manager .New (cfg , manager.Options {})
@@ -542,7 +675,8 @@ var _ runtime.Object = &TestDefaulter{}
542675const testDefaulterKind = "TestDefaulter"
543676
544677type TestDefaulter struct {
545- Replica int `json:"replica,omitempty"`
678+ Replica int `json:"replica,omitempty"`
679+ Panic bool `json:"panic,omitempty"`
546680}
547681
548682var testDefaulterGVK = schema.GroupVersionKind {Group : "foo.test.org" , Version : "v1" , Kind : testDefaulterKind }
@@ -568,6 +702,9 @@ func (*TestDefaulterList) GetObjectKind() schema.ObjectKind { return nil }
568702func (* TestDefaulterList ) DeepCopyObject () runtime.Object { return nil }
569703
570704func (d * TestDefaulter ) Default () {
705+ if d .Panic {
706+ panic ("injected panic" )
707+ }
571708 if d .Replica < 2 {
572709 d .Replica = 2
573710 }
@@ -579,7 +716,8 @@ var _ runtime.Object = &TestValidator{}
579716const testValidatorKind = "TestValidator"
580717
581718type TestValidator struct {
582- Replica int `json:"replica,omitempty"`
719+ Replica int `json:"replica,omitempty"`
720+ Panic bool `json:"panic,omitempty"`
583721}
584722
585723var testValidatorGVK = schema.GroupVersionKind {Group : "foo.test.org" , Version : "v1" , Kind : testValidatorKind }
@@ -607,13 +745,19 @@ func (*TestValidatorList) DeepCopyObject() runtime.Object { return nil }
607745var _ admission.Validator = & TestValidator {}
608746
609747func (v * TestValidator ) ValidateCreate () error {
748+ if v .Panic {
749+ panic ("injected panic" )
750+ }
610751 if v .Replica < 0 {
611752 return errors .New ("number of replica should be greater than or equal to 0" )
612753 }
613754 return nil
614755}
615756
616757func (v * TestValidator ) ValidateUpdate (old runtime.Object ) error {
758+ if v .Panic {
759+ panic ("injected panic" )
760+ }
617761 if v .Replica < 0 {
618762 return errors .New ("number of replica should be greater than or equal to 0" )
619763 }
@@ -626,6 +770,9 @@ func (v *TestValidator) ValidateUpdate(old runtime.Object) error {
626770}
627771
628772func (v * TestValidator ) ValidateDelete () error {
773+ if v .Panic {
774+ panic ("injected panic" )
775+ }
629776 if v .Replica > 0 {
630777 return errors .New ("number of replica should be less than or equal to 0 to delete" )
631778 }
0 commit comments