Skip to content

Commit 1282345

Browse files
rikatzjsoref
andauthored
Admission warning (#9975)
* Add warning feature in admission code * Apply suggestions from code review Co-authored-by: Josh Soref <[email protected]> * Add deprecation and validation path notice --------- Co-authored-by: Josh Soref <[email protected]>
1 parent 8977835 commit 1282345

File tree

4 files changed

+177
-0
lines changed

4 files changed

+177
-0
lines changed

internal/admission/controller/main.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333
// contains invalid instructions
3434
type Checker interface {
3535
CheckIngress(ing *networking.Ingress) error
36+
CheckWarning(ing *networking.Ingress) ([]string, error)
3637
}
3738

3839
// IngressAdmission implements the AdmissionController interface
@@ -85,6 +86,15 @@ func (ia *IngressAdmission) HandleAdmission(obj runtime.Object) (runtime.Object,
8586
return review, nil
8687
}
8788

89+
// Adds the warnings regardless of operation being allowed or not
90+
warning, err := ia.Checker.CheckWarning(&ingress)
91+
if err != nil {
92+
klog.ErrorS(err, "failed to get ingress warnings")
93+
}
94+
if len(warning) > 0 {
95+
status.Warnings = warning
96+
}
97+
8898
if err := ia.Checker.CheckIngress(&ingress); err != nil {
8999
klog.ErrorS(err, "invalid ingress configuration", "ingress", fmt.Sprintf("%v/%v", review.Request.Namespace, review.Request.Name))
90100
status.Allowed = false

internal/admission/controller/main_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ func (ftc failTestChecker) CheckIngress(ing *networking.Ingress) error {
3838
return nil
3939
}
4040

41+
func (ftc failTestChecker) CheckWarning(ing *networking.Ingress) ([]string, error) {
42+
ftc.t.Error("checker should not be called")
43+
return nil, nil
44+
}
45+
4146
type testChecker struct {
4247
t *testing.T
4348
err error
@@ -50,6 +55,13 @@ func (tc testChecker) CheckIngress(ing *networking.Ingress) error {
5055
return tc.err
5156
}
5257

58+
func (tc testChecker) CheckWarning(ing *networking.Ingress) ([]string, error) {
59+
if ing.ObjectMeta.Name != testIngressName {
60+
tc.t.Errorf("CheckWarning should be called with %v ingress, but got %v", testIngressName, ing.ObjectMeta.Name)
61+
}
62+
return nil, tc.err
63+
}
64+
5365
func TestHandleAdmission(t *testing.T) {
5466
adm := &IngressAdmission{
5567
Checker: failTestChecker{t: t},

internal/ingress/controller/controller.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,54 @@ func (n *NGINXController) syncIngress(interface{}) error {
256256
return nil
257257
}
258258

259+
// GetWarnings returns a list of warnings an Ingress gets when being created.
260+
// The warnings are going to be used in an admission webhook, and they represent
261+
// a list of messages that users need to be aware (like deprecation notices)
262+
// when creating a new ingress object
263+
func (n *NGINXController) CheckWarning(ing *networking.Ingress) ([]string, error) {
264+
warnings := make([]string, 0)
265+
266+
var deprecatedAnnotations = sets.NewString()
267+
deprecatedAnnotations.Insert(
268+
"enable-influxdb",
269+
"influxdb-measurement",
270+
"influxdb-port",
271+
"influxdb-host",
272+
"influxdb-server-name",
273+
"secure-verify-ca-secret",
274+
"fastcgi-params-configmap",
275+
"fastcgi-index",
276+
)
277+
278+
// Skip checks if the ingress is marked as deleted
279+
if !ing.DeletionTimestamp.IsZero() {
280+
return warnings, nil
281+
}
282+
283+
anns := ing.GetAnnotations()
284+
for k := range anns {
285+
trimmedkey := strings.TrimPrefix(k, parser.AnnotationsPrefix+"/")
286+
if deprecatedAnnotations.Has(trimmedkey) {
287+
warnings = append(warnings, fmt.Sprintf("annotation %s is deprecated", k))
288+
}
289+
}
290+
291+
// Add each validation as a single warning
292+
// rikatz: I know this is somehow a duplicated code from CheckIngress, but my goal was to deliver fast warning on this behavior. We
293+
// can and should, tho, simplify this in the near future
294+
if err := inspector.ValidatePathType(ing); err != nil {
295+
if errs, is := err.(interface{ Unwrap() []error }); is {
296+
for _, errW := range errs.Unwrap() {
297+
warnings = append(warnings, errW.Error())
298+
}
299+
} else {
300+
warnings = append(warnings, err.Error())
301+
}
302+
}
303+
304+
return warnings, nil
305+
}
306+
259307
// CheckIngress returns an error in case the provided ingress, when added
260308
// to the current configuration, generates an invalid configuration
261309
func (n *NGINXController) CheckIngress(ing *networking.Ingress) error {

internal/ingress/controller/controller_test.go

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,113 @@ func TestCheckIngress(t *testing.T) {
352352
})
353353
}
354354

355+
func TestCheckWarning(t *testing.T) {
356+
357+
// Ensure no panic with wrong arguments
358+
var nginx = &NGINXController{}
359+
360+
nginx.t = fakeTemplate{}
361+
nginx.store = fakeIngressStore{
362+
ingresses: []*ingress.Ingress{},
363+
}
364+
365+
ing := &networking.Ingress{
366+
ObjectMeta: metav1.ObjectMeta{
367+
Name: "test-ingress-warning",
368+
Namespace: "user-namespace",
369+
Annotations: map[string]string{},
370+
},
371+
Spec: networking.IngressSpec{
372+
Rules: []networking.IngressRule{
373+
{
374+
Host: "example.com",
375+
},
376+
},
377+
},
378+
}
379+
t.Run("when a deprecated annotation is used a warning should be returned", func(t *testing.T) {
380+
ing.ObjectMeta.Annotations[parser.GetAnnotationWithPrefix("enable-influxdb")] = "true"
381+
defer func() {
382+
ing.ObjectMeta.Annotations = map[string]string{}
383+
}()
384+
385+
warnings, err := nginx.CheckWarning(ing)
386+
if err != nil {
387+
t.Errorf("no error should be returned, but %s was returned", err)
388+
}
389+
if len(warnings) != 1 {
390+
t.Errorf("expected 1 warning to occur but %d occured", len(warnings))
391+
} else {
392+
t.Logf("got warning %s correctly", warnings[0])
393+
}
394+
})
395+
396+
t.Run("When an invalid pathType is used, a warning should be returned", func(t *testing.T) {
397+
398+
rules := ing.Spec.DeepCopy().Rules
399+
ing.Spec.Rules = []networking.IngressRule{
400+
{
401+
Host: "example.com",
402+
IngressRuleValue: networking.IngressRuleValue{
403+
HTTP: &networking.HTTPIngressRuleValue{
404+
Paths: []networking.HTTPIngressPath{
405+
{
406+
Path: "/xpto{$2}",
407+
PathType: &pathTypePrefix,
408+
},
409+
{
410+
Path: "/ok",
411+
PathType: &pathTypeExact,
412+
},
413+
},
414+
},
415+
},
416+
},
417+
}
418+
defer func() {
419+
ing.Spec.Rules = rules
420+
}()
421+
422+
warnings, err := nginx.CheckWarning(ing)
423+
if err != nil {
424+
t.Errorf("no error should be returned, but %s was returned", err)
425+
}
426+
if len(warnings) != 1 {
427+
t.Errorf("expected 1 warning to occur but %d occured", len(warnings))
428+
} else {
429+
t.Logf("got warnings %v correctly", warnings)
430+
}
431+
432+
t.Run("adding invalid annotations increases the warning count", func(t *testing.T) {
433+
ing.ObjectMeta.Annotations[parser.GetAnnotationWithPrefix("enable-influxdb")] = "true"
434+
ing.ObjectMeta.Annotations[parser.GetAnnotationWithPrefix("secure-verify-ca-secret")] = "true"
435+
ing.ObjectMeta.Annotations[parser.GetAnnotationWithPrefix("fastcgi-index")] = "blabla"
436+
defer func() {
437+
ing.ObjectMeta.Annotations = map[string]string{}
438+
}()
439+
warnings, err := nginx.CheckWarning(ing)
440+
if err != nil {
441+
t.Errorf("no error should be returned, but %s was returned", err)
442+
}
443+
if len(warnings) != 4 {
444+
t.Errorf("expected 4 warning to occur but %d occured", len(warnings))
445+
} else {
446+
t.Logf("got warnings %v correctly", warnings)
447+
}
448+
})
449+
})
450+
451+
t.Run("When the ingress is marked as deleted", func(t *testing.T) {
452+
ing.DeletionTimestamp = &metav1.Time{
453+
Time: time.Now(),
454+
}
455+
456+
if warnings, err := nginx.CheckWarning(ing); err != nil || len(warnings) != 0 {
457+
t.Errorf("when the ingress is marked as deleted, no warning should be returned")
458+
}
459+
})
460+
}
461+
355462
func TestMergeAlternativeBackends(t *testing.T) {
356463
testCases := map[string]struct {
357464
ingress *ingress.Ingress

0 commit comments

Comments
 (0)