Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RLP atomic override #523

Merged
merged 14 commits into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions api/v1beta2/ratelimitpolicy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@ func (l Limit) CountersAsStringList() []string {
// RateLimitPolicySpec defines the desired state of RateLimitPolicy
// +kubebuilder:validation:XValidation:rule="self.targetRef.kind != 'Gateway' || !has(self.limits) || !self.limits.exists(x, has(self.limits[x].routeSelectors))",message="route selectors not supported when targeting a Gateway"
// +kubebuilder:validation:XValidation:rule="!(has(self.defaults) && has(self.limits))",message="Implicit and explicit defaults are mutually exclusive"
// +kubebuilder:validation:XValidation:rule="!(has(self.defaults) && has(self.overrides))",message="Overrides and explicit defaults are mutually exclusive"
// +kubebuilder:validation:XValidation:rule="!(has(self.overrides) && has(self.limits))",message="Overrides and implicit defaults are mutually exclusive"
// +kubebuilder:validation:XValidation:rule="!(has(self.overrides) && self.targetRef.kind != 'Gateway')",message="Overrides are only allowed for policies targeting a Gateway resource"
type RateLimitPolicySpec struct {
// TargetRef identifies an API object to apply policy to.
// +kubebuilder:validation:XValidation:rule="self.group == 'gateway.networking.k8s.io'",message="Invalid targetRef.group. The only supported value is 'gateway.networking.k8s.io'"
Expand All @@ -133,6 +136,11 @@ type RateLimitPolicySpec struct {
// +optional
Defaults *RateLimitPolicyCommonSpec `json:"defaults,omitempty"`

// Overrides define override values for this policy and for policies inheriting this policy.
// Overrides are mutually exclusive with implicit defaults and explicit Defaults defined by RateLimitPolicyCommonSpec.
// +optional
Overrides *RateLimitPolicyCommonSpec `json:"overrides,omitempty"`

// RateLimitPolicyCommonSpec defines implicit default values for this policy and for policies inheriting this policy.
// RateLimitPolicyCommonSpec is mutually exclusive with explicit defaults defined by Defaults.
RateLimitPolicyCommonSpec `json:""`
Expand Down Expand Up @@ -286,6 +294,10 @@ func (r *RateLimitPolicySpec) CommonSpec() *RateLimitPolicyCommonSpec {
return r.Defaults
}

if r.Overrides != nil {
return r.Overrides
}

return &r.RateLimitPolicyCommonSpec
}

Expand Down
5 changes: 5 additions & 0 deletions api/v1beta2/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

401 changes: 401 additions & 0 deletions bundle/manifests/kuadrant.io_ratelimitpolicies.yaml

Large diffs are not rendered by default.

401 changes: 401 additions & 0 deletions config/crd/bases/kuadrant.io_ratelimitpolicies.yaml

Large diffs are not rendered by default.

77 changes: 49 additions & 28 deletions controllers/authpolicy_authconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@ import (
"errors"
"fmt"
"reflect"
"slices"
"strings"

"k8s.io/utils/ptr"

"github.com/go-logr/logr"
authorinoapi "github.com/kuadrant/authorino/api/v1beta2"
apierrors "k8s.io/apimachinery/pkg/api/errors"
Expand All @@ -18,6 +17,7 @@ import (

api "github.com/kuadrant/kuadrant-operator/api/v1beta2"
"github.com/kuadrant/kuadrant-operator/pkg/common"
kuadrantgatewayapi "github.com/kuadrant/kuadrant-operator/pkg/library/gatewayapi"
"github.com/kuadrant/kuadrant-operator/pkg/library/kuadrant"
"github.com/kuadrant/kuadrant-operator/pkg/library/utils"
)
Expand Down Expand Up @@ -67,14 +67,17 @@ func (r *AuthPolicyReconciler) desiredAuthConfig(ctx context.Context, ap *api.Au

switch obj := targetNetworkObject.(type) {
case *gatewayapiv1.HTTPRoute:
ok, err := routeGatewayHasAuthOverrides(ctx, obj, r.Client())
t, err := r.generateTopology(ctx)
if err != nil {
logger.V(1).Info("Failed to generate topology", "error", err)
return nil, err
}
if ok {

overrides := routeGatewayAuthOverrides(t, ap)
if len(overrides) != 0 {
logger.V(1).Info("targeted gateway has authpolicy with atomic overrides, skipping authorino authconfig for the HTTPRoute authpolicy")
utils.TagObjectToDelete(authConfig)
r.OverriddenPolicyMap.SetOverriddenPolicy(ap)
r.AffectedPolicyMap.SetAffectedPolicy(ap, overrides)
return authConfig, nil
}
route = obj
Expand Down Expand Up @@ -105,7 +108,14 @@ func (r *AuthPolicyReconciler) desiredAuthConfig(ctx context.Context, ap *api.Au
if len(rules) == 0 {
logger.V(1).Info("no httproutes attached to the targeted gateway, skipping authorino authconfig for the gateway authpolicy")
utils.TagObjectToDelete(authConfig)
r.OverriddenPolicyMap.SetOverriddenPolicy(ap)
obj := targetNetworkObject.(*gatewayapiv1.Gateway)
gatewayWrapper := kuadrant.GatewayWrapper{Gateway: obj, Referrer: ap}
refs := gatewayWrapper.PolicyRefs()
filteredRef := utils.Filter(refs, func(key client.ObjectKey) bool {
return key != client.ObjectKeyFromObject(ap)
})

r.AffectedPolicyMap.SetAffectedPolicy(ap, filteredRef)
return authConfig, nil
}
route = &gatewayapiv1.HTTPRoute{
Expand All @@ -116,8 +126,8 @@ func (r *AuthPolicyReconciler) desiredAuthConfig(ctx context.Context, ap *api.Au
}
}

// AuthPolicy is not overridden if we still need to create an AuthConfig for it
r.OverriddenPolicyMap.RemoveOverriddenPolicy(ap)
// AuthPolicy is not Affected if we still need to create an AuthConfig for it
r.AffectedPolicyMap.RemoveAffectedPolicy(ap)

// hosts
authConfig.Spec.Hosts = hosts
Expand Down Expand Up @@ -185,32 +195,43 @@ func (r *AuthPolicyReconciler) desiredAuthConfig(ctx context.Context, ap *api.Au
return mergeConditionsFromRouteSelectorsIntoConfigs(ap, route, authConfig)
}

// routeGatewayHasAuthOverrides return true when the gateway which a route is attached to has an attached authPolicy that defines atomic overrides
func routeGatewayHasAuthOverrides(ctx context.Context, route *gatewayapiv1.HTTPRoute, c client.Client) (bool, error) {
for idx := range route.Spec.ParentRefs {
parentRef := route.Spec.ParentRefs[idx]
gw := &gatewayapiv1.Gateway{}
namespace := ptr.Deref(parentRef.Namespace, gatewayapiv1.Namespace(route.GetNamespace()))
err := c.Get(ctx, client.ObjectKey{Name: string(parentRef.Name), Namespace: string(namespace)}, gw)
if err != nil {
return false, err
}
// routeGatewayAuthOverrides returns the GW auth policies that has a
func routeGatewayAuthOverrides(t *kuadrantgatewayapi.Topology, ap *api.AuthPolicy) []client.ObjectKey {
affectedPolicies := getAffectedPolicies(t, ap)

annotation, ok := gw.GetAnnotations()[common.AuthPolicyBackRefAnnotation]
// Filter the policies where:
// 1. targets a gateway
// 2. is not the current AP that is being assessed
// 3. is an overriding policy
affectedPolicies = utils.Filter(affectedPolicies, func(policy kuadrantgatewayapi.Policy) bool {
p, ok := policy.(*api.AuthPolicy)
if !ok {
continue
}
otherAP := &api.AuthPolicy{}
err = c.Get(ctx, utils.NamespacedNameToObjectKey(annotation, gw.Namespace), otherAP)
if err != nil {
return false, err
return false
}
return kuadrantgatewayapi.IsTargetRefGateway(policy.GetTargetRef()) &&
ap.GetUID() != policy.GetUID() && p.IsAtomicOverride()
})

if otherAP.IsAtomicOverride() {
return true, nil
return utils.Map(affectedPolicies, func(policy kuadrantgatewayapi.Policy) client.ObjectKey {
return client.ObjectKeyFromObject(policy)
})
}

func getAffectedPolicies(t *kuadrantgatewayapi.Topology, ap *api.AuthPolicy) []kuadrantgatewayapi.Policy {
topologyIndexes := kuadrantgatewayapi.NewTopologyIndexes(t)
var affectedPolicies []kuadrantgatewayapi.Policy

// If AP is listed within the policies from gateway, it potentially can be overridden by it
for _, gw := range t.Gateways() {
policyList := topologyIndexes.PoliciesFromGateway(gw.Gateway)
if slices.Contains(utils.Map(policyList, func(p kuadrantgatewayapi.Policy) client.ObjectKey {
return client.ObjectKeyFromObject(p)
}), client.ObjectKeyFromObject(ap)) {
affectedPolicies = append(affectedPolicies, policyList...)
}
}
return false, nil

return affectedPolicies
}

// authConfigName returns the name of Authorino AuthConfig CR.
Expand Down
8 changes: 4 additions & 4 deletions controllers/authpolicy_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ const authPolicyFinalizer = "authpolicy.kuadrant.io/finalizer"
type AuthPolicyReconciler struct {
*reconcilers.BaseReconciler
TargetRefReconciler reconcilers.TargetRefReconciler
// OverriddenPolicyMap tracks the overridden policies to report their status.
OverriddenPolicyMap *kuadrant.OverriddenPolicyMap
// AffectedPolicyMap tracks the affected policies to report their status.
AffectedPolicyMap *kuadrant.AffectedPolicyMap
}

//+kubebuilder:rbac:groups=kuadrant.io,resources=authpolicies,verbs=get;list;watch;create;update;patch;delete
Expand Down Expand Up @@ -72,7 +72,7 @@ func (r *AuthPolicyReconciler) Reconcile(eventCtx context.Context, req ctrl.Requ
if delResErr == nil {
delResErr = err
}
return r.reconcileStatus(ctx, ap, targetNetworkObject, kuadrant.NewErrTargetNotFound(ap.Kind(), ap.GetTargetRef(), delResErr))
return r.reconcileStatus(ctx, ap, kuadrant.NewErrTargetNotFound(ap.Kind(), ap.GetTargetRef(), delResErr))
}
return ctrl.Result{}, err
}
Expand Down Expand Up @@ -108,7 +108,7 @@ func (r *AuthPolicyReconciler) Reconcile(eventCtx context.Context, req ctrl.Requ
specErr := r.reconcileResources(ctx, ap, targetNetworkObject)

// reconcile authpolicy status
statusResult, statusErr := r.reconcileStatus(ctx, ap, targetNetworkObject, specErr)
statusResult, statusErr := r.reconcileStatus(ctx, ap, specErr)

if specErr != nil {
return ctrl.Result{}, specErr
Expand Down
Loading
Loading