Skip to content

Commit

Permalink
Merge pull request #107 from gympass/PE1-1809/function-associations-v…
Browse files Browse the repository at this point in the history
…alidation

[PE1-1809] feat(k8s): throw error for invalid function associations
  • Loading branch information
LCaparelli authored Oct 17, 2023
2 parents 82231dc + 7066150 commit 18a1c79
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 16 deletions.
12 changes: 1 addition & 11 deletions controllers/ingress_v1_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,23 +64,13 @@ func (r *V1Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Re
return reconcile.Result{}, fmt.Errorf("could not fetch Ingress: %+v", err)
}

// TODO in upcoming PRs:
// validate function associations annotation to ensure:
// a. it only references paths present in this Ingress (this currently silently fails)
// b. do not break any existing rules that are already mapped in fa.Validate() (this currently silently fails)

cdnClassName := k8s.CDNClassAnnotationValue(ingress)
cdnClass, err := r.CDNClassFetcher.FetchByName(ctx, cdnClassName)
if err != nil {
return ctrl.Result{}, fmt.Errorf("could not find CDN class (%s): %v", cdnClassName, err)
}

reconcilingCDNIngress, err := k8s.NewCDNIngressFromV1(ctx, ingress, cdnClass)
if err != nil {
return ctrl.Result{}, err
}

err = r.CloudFrontService.Reconcile(ctx, reconcilingCDNIngress, ingress)
err = r.CloudFrontService.Reconcile(ctx, ingress, cdnClass)
if err == nil {
log.Info("Reconciliation successful.")
}
Expand Down
42 changes: 37 additions & 5 deletions internal/cloudfront/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/go-logr/logr"
"github.com/hashicorp/go-multierror"
corev1 "k8s.io/api/core/v1"
networkingv1 "k8s.io/api/networking/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/record"
Expand Down Expand Up @@ -58,7 +59,16 @@ type Service struct {
}

// Reconcile an Ingress resource of any version
func (s *Service) Reconcile(ctx context.Context, reconciling k8s.CDNIngress, ing client.Object) error {
func (s *Service) Reconcile(ctx context.Context, ing *networkingv1.Ingress, class k8s.CDNClass) error {
if err := s.validateIngress(ing); err != nil {
return s.handleFailure(fmt.Errorf("validating Ingress: %v", err), ing)
}

reconciling, err := k8s.NewCDNIngressFromV1(ctx, ing, class)
if err != nil {
return err
}

log, _ := logr.FromContext(ctx)

if k8s.HasFinalizer(ing) && !k8s.HasGroupAnnotation(ing) {
Expand Down Expand Up @@ -105,6 +115,18 @@ func (s *Service) Reconcile(ctx context.Context, reconciling k8s.CDNIngress, ing
return s.handleResult(ing, cdnStatus, errs)
}

func (s *Service) validateIngress(ing *networkingv1.Ingress) error {
if df := k8s.UsedDeprecatedFields(ing); len(df) > 0 {
s.Recorder.Eventf(
ing,
corev1.EventTypeWarning,
"UsingDeprecatedFields",
"Using deprecated fields/annotations: %v", df)
}

return k8s.ValidateIngressFunctionAssociations(ing)
}

func (s *Service) desiredState(ctx context.Context, reconciling k8s.CDNIngress) ([]k8s.CDNIngress, Distribution, error) {
desiredIngresses, err := s.desiredIngresses(ctx, reconciling)
if err != nil {
Expand Down Expand Up @@ -377,14 +399,18 @@ func (s *Service) reconcileFinalizer(obj client.Object, shouldHaveFinalizer bool

func (s *Service) handleResult(obj client.Object, cdnStatus *v1alpha1.CDNStatus, errs *multierror.Error) error {
if errs.Len() > 0 {
return s.handleFailure(errs, obj, cdnStatus)
return s.handleFailureWithStatus(errs, obj, cdnStatus)
}
return s.handleSuccess(obj, cdnStatus)
}

func (s *Service) handleFailure(err error, ingress client.Object, status *v1alpha1.CDNStatus) error {
msg := "Unable to reconcile CDN: " + err.Error()
s.Recorder.Event(ingress, corev1.EventTypeWarning, reasonFailed, msg)
func (s *Service) handleFailure(err error, ingress client.Object) error {
s.recordFailureOnIngress(err, ingress)
return err
}

func (s *Service) handleFailureWithStatus(err error, ingress client.Object, status *v1alpha1.CDNStatus) error {
msg := s.recordFailureOnIngress(err, ingress)

ingRef := v1alpha1.NewIngressRef(ingress.GetNamespace(), ingress.GetName())
msg = fmt.Sprintf("%s: %s", ingRef, msg)
Expand All @@ -393,6 +419,12 @@ func (s *Service) handleFailure(err error, ingress client.Object, status *v1alph
return err
}

func (s *Service) recordFailureOnIngress(err error, ingress client.Object) string {
msg := "Unable to reconcile CDN: " + err.Error()
s.Recorder.Event(ingress, corev1.EventTypeWarning, reasonFailed, msg)
return msg
}

func (s *Service) handleSuccess(ingress client.Object, status *v1alpha1.CDNStatus) error {
msg := "Successfully reconciled CDN"
s.Recorder.Event(ingress, corev1.EventTypeNormal, reasonSuccess, msg)
Expand Down
30 changes: 30 additions & 0 deletions internal/k8s/deprecated.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) 2023 GPBR Participacoes LTDA.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package k8s

import "sigs.k8s.io/controller-runtime/pkg/client"

func UsedDeprecatedFields(ing client.Object) []string {
var deprecations []string
if _, ok := ing.GetAnnotations()[cfViewerFnAnnotation]; ok {
deprecations = append(deprecations, cfViewerFnAnnotation)
}
return deprecations
}
67 changes: 67 additions & 0 deletions internal/k8s/validation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright (c) 2023 GPBR Participacoes LTDA.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package k8s

import (
"fmt"

networkingv1 "k8s.io/api/networking/v1"

"github.com/Gympass/cdn-origin-controller/internal/strhelper"
)

func ValidateIngressFunctionAssociations(ing *networkingv1.Ingress) error {
allFAs, err := functionAssociations(ing)
if err != nil {
return fmt.Errorf("parsing function associations: %v", err)
}

if allFAs == nil {
return nil
}

if len(viewerFnARN(ing)) > 0 {
return fmt.Errorf("can't use %q (deprecated) and %q at the same time, prefer %q",
cfViewerFnAnnotation, cfFunctionAssociationsAnnotation, cfFunctionAssociationsAnnotation)
}

ingPaths := ingressPaths(ing)
for faPath, fa := range allFAs {
if err := fa.Validate(); err != nil {
return fmt.Errorf("invalid function association at path %q: %v", faPath, err)
}

if !strhelper.Contains(ingPaths, faPath) {
return fmt.Errorf("function associations references a path %q that is not part of the Ingress' paths %v", faPath, ingPaths)
}
}

return nil
}

func ingressPaths(ing *networkingv1.Ingress) []string {
var paths []string
for _, r := range ing.Spec.Rules {
for _, p := range r.HTTP.Paths {
paths = append(paths, p.Path)
}
}
return paths
}

0 comments on commit 18a1c79

Please sign in to comment.