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

Generic policy status reconciler #1

Draft
wants to merge 8 commits into
base: rlp-policy-status
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions api/v1beta2/authpolicy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,10 @@ func (ap *AuthPolicy) GetRulesHostnames() (ruleHosts []string) {
return
}

func (ap *AuthPolicy) Kind() string {
return ap.TypeMeta.Kind
}

//+kubebuilder:object:root=true

// AuthPolicyList contains a list of AuthPolicy
Expand Down
4 changes: 4 additions & 0 deletions api/v1beta2/ratelimitpolicy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,10 @@ func (r *RateLimitPolicy) GetRulesHostnames() (ruleHosts []string) {
return
}

func (r *RateLimitPolicy) Kind() string {
return r.TypeMeta.Kind
}

func init() {
SchemeBuilder.Register(&RateLimitPolicy{}, &RateLimitPolicyList{})
}
12 changes: 6 additions & 6 deletions controllers/authpolicy_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,11 @@ func (r *AuthPolicyReconciler) Reconcile(eventCtx context.Context, req ctrl.Requ
func (r *AuthPolicyReconciler) reconcileResources(ctx context.Context, ap *api.AuthPolicy, targetNetworkObject client.Object) error {
// validate
if err := ap.Validate(); err != nil {
return err
return common.ErrInvalid{Kind: ap.Kind(), Err: err}
}

if err := common.ValidateHierarchicalRules(ap, targetNetworkObject); err != nil {
return err
return common.ErrInvalid{Kind: ap.Kind(), Err: err}
}

// reconcile based on gateway diffs
Expand All @@ -159,7 +159,7 @@ func (r *AuthPolicyReconciler) reconcileResources(ctx context.Context, ap *api.A
return err
}

// set annotation of policies afftecting the gateway - should be the last step, only when all the reconciliation steps succeed
// set annotation of policies affecting the gateway - should be the last step, only when all the reconciliation steps succeed
return r.ReconcileGatewayPolicyReferences(ctx, ap, gatewayDiffObj)
}

Expand All @@ -181,13 +181,13 @@ func (r *AuthPolicyReconciler) deleteResources(ctx context.Context, ap *api.Auth
}
}

// update annotation of policies afftecting the gateway
// update annotation of policies affecting the gateway
return r.ReconcileGatewayPolicyReferences(ctx, ap, gatewayDiffObj)
}

// Ensures only one RLP targets the network resource
func (r *AuthPolicyReconciler) reconcileNetworkResourceDirectBackReference(ctx context.Context, ap *api.AuthPolicy, targetNetworkObject client.Object) error {
return r.ReconcileTargetBackReference(ctx, client.ObjectKeyFromObject(ap), targetNetworkObject, common.AuthPolicyBackRefAnnotation)
func (r *AuthPolicyReconciler) reconcileNetworkResourceDirectBackReference(ctx context.Context, ap common.KuadrantPolicy, targetNetworkObject client.Object) error {
return r.ReconcileTargetBackReference(ctx, ap, targetNetworkObject, common.AuthPolicyBackRefAnnotation)
}

func (r *AuthPolicyReconciler) deleteNetworkResourceDirectBackReference(ctx context.Context, targetNetworkObject client.Object) error {
Expand Down
34 changes: 17 additions & 17 deletions controllers/authpolicy_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ var _ = Describe("AuthPolicy controller", func() {
Expect(err).ToNot(HaveOccurred())

// check policy status
Eventually(testPolicyIsReady(policy), 30*time.Second, 5*time.Second).Should(BeTrue())
Eventually(testPolicyIsAccepted(policy), 30*time.Second, 5*time.Second).Should(BeTrue())

// check istio authorizationpolicy
iapKey := types.NamespacedName{Name: istioAuthorizationPolicyName(testGatewayName, policy.Spec.TargetRef), Namespace: testNamespace}
Expand Down Expand Up @@ -161,7 +161,7 @@ var _ = Describe("AuthPolicy controller", func() {
Expect(err).ToNot(HaveOccurred())

// check policy status
Eventually(testPolicyIsReady(policy), 60*time.Second, 5*time.Second).Should(BeTrue())
Eventually(testPolicyIsAccepted(policy), 60*time.Second, 5*time.Second).Should(BeTrue())

// check authorino authconfig hosts
authConfigKey := types.NamespacedName{Name: authConfigName(client.ObjectKeyFromObject(policy)), Namespace: testNamespace}
Expand Down Expand Up @@ -197,7 +197,7 @@ var _ = Describe("AuthPolicy controller", func() {
Expect(err).ToNot(HaveOccurred())

// check policy status
Eventually(testPolicyIsReady(policy), 30*time.Second, 5*time.Second).Should(BeTrue())
Eventually(testPolicyIsAccepted(policy), 30*time.Second, 5*time.Second).Should(BeTrue())

// check istio authorizationpolicy
iapKey := types.NamespacedName{Name: istioAuthorizationPolicyName(testGatewayName, policy.Spec.TargetRef), Namespace: testNamespace}
Expand Down Expand Up @@ -257,7 +257,7 @@ var _ = Describe("AuthPolicy controller", func() {
Expect(err).ToNot(HaveOccurred())

// check policy status
Eventually(testPolicyIsReady(routePolicy), 30*time.Second, 5*time.Second).Should(BeTrue())
Eventually(testPolicyIsAccepted(routePolicy), 30*time.Second, 5*time.Second).Should(BeTrue())

// create second (policyless) httproute
otherRoute := testBuildBasicHttpRoute("policyless-route", testGatewayName, testNamespace, []string{"*.other"})
Expand Down Expand Up @@ -296,7 +296,7 @@ var _ = Describe("AuthPolicy controller", func() {
Expect(err).ToNot(HaveOccurred())

// check policy status
Eventually(testPolicyIsReady(gwPolicy), 30*time.Second, 5*time.Second).Should(BeTrue())
Eventually(testPolicyIsAccepted(gwPolicy), 30*time.Second, 5*time.Second).Should(BeTrue())

// check istio authorizationpolicy
iapKey := types.NamespacedName{Name: istioAuthorizationPolicyName(testGatewayName, gwPolicy.Spec.TargetRef), Namespace: testNamespace}
Expand Down Expand Up @@ -357,7 +357,7 @@ var _ = Describe("AuthPolicy controller", func() {
Expect(err).ToNot(HaveOccurred())

// check policy status
Eventually(testPolicyIsReady(routePolicy), 30*time.Second, 5*time.Second).Should(BeTrue())
Eventually(testPolicyIsAccepted(routePolicy), 30*time.Second, 5*time.Second).Should(BeTrue())

// attach policy to the gatewaay
gwPolicy := &api.AuthPolicy{
Expand Down Expand Up @@ -387,7 +387,7 @@ var _ = Describe("AuthPolicy controller", func() {
if err != nil {
return false
}
condition := meta.FindStatusCondition(existingPolicy.Status.Conditions, APAvailableConditionType)
condition := meta.FindStatusCondition(existingPolicy.Status.Conditions, string(gatewayapiv1alpha2.PolicyConditionAccepted))
return condition != nil && condition.Reason == "AuthSchemeNotReady"
}, 30*time.Second, 5*time.Second).Should(BeTrue())

Expand Down Expand Up @@ -444,7 +444,7 @@ var _ = Describe("AuthPolicy controller", func() {
if err != nil {
return false
}
condition := meta.FindStatusCondition(existingPolicy.Status.Conditions, APAvailableConditionType)
condition := meta.FindStatusCondition(existingPolicy.Status.Conditions, string(gatewayapiv1alpha2.PolicyConditionAccepted))
return condition != nil && condition.Reason == "ReconciliationError" && strings.Contains(condition.Message, "cannot match any route rules, check for invalid route selectors in the policy")
}, 30*time.Second, 5*time.Second).Should(BeTrue())

Expand Down Expand Up @@ -503,7 +503,7 @@ var _ = Describe("AuthPolicy controller", func() {
if err != nil {
return false
}
condition := meta.FindStatusCondition(existingPolicy.Status.Conditions, APAvailableConditionType)
condition := meta.FindStatusCondition(existingPolicy.Status.Conditions, string(gatewayapiv1alpha2.PolicyConditionAccepted))
return condition != nil && condition.Reason == "ReconciliationError" && strings.Contains(condition.Message, "cannot match any route rules, check for invalid route selectors in the policy")
}, 30*time.Second, 5*time.Second).Should(BeTrue())

Expand Down Expand Up @@ -550,7 +550,7 @@ var _ = Describe("AuthPolicy controller", func() {
Expect(err).ToNot(HaveOccurred())

// check policy status
Eventually(testPolicyIsReady(policy), 30*time.Second, 5*time.Second).Should(BeTrue())
Eventually(testPolicyIsAccepted(policy), 30*time.Second, 5*time.Second).Should(BeTrue())

// delete policy
err = k8sClient.Delete(context.Background(), policy)
Expand Down Expand Up @@ -803,7 +803,7 @@ var _ = Describe("AuthPolicy controller", func() {
Expect(err).ToNot(HaveOccurred())

// check policy status
Eventually(testPolicyIsReady(policy), 30*time.Second, 5*time.Second).Should(BeTrue())
Eventually(testPolicyIsAccepted(policy), 30*time.Second, 5*time.Second).Should(BeTrue())

// check authorino authconfig
authConfigKey := types.NamespacedName{Name: authConfigName(client.ObjectKeyFromObject(policy)), Namespace: testNamespace}
Expand Down Expand Up @@ -850,7 +850,7 @@ var _ = Describe("AuthPolicy controller", func() {
Expect(err).ToNot(HaveOccurred())

// check policy status
Eventually(testPolicyIsReady(policy), 30*time.Second, 5*time.Second).Should(BeTrue())
Eventually(testPolicyIsAccepted(policy), 30*time.Second, 5*time.Second).Should(BeTrue())

// check istio authorizationpolicy
iapKey := types.NamespacedName{Name: istioAuthorizationPolicyName(testGatewayName, policy.Spec.TargetRef), Namespace: testNamespace}
Expand Down Expand Up @@ -958,7 +958,7 @@ var _ = Describe("AuthPolicy controller", func() {
Expect(err).ToNot(HaveOccurred())

// check policy status
Eventually(testPolicyIsReady(policy), 30*time.Second, 5*time.Second).Should(BeTrue())
Eventually(testPolicyIsAccepted(policy), 30*time.Second, 5*time.Second).Should(BeTrue())

// check istio authorizationpolicy
iapKey := types.NamespacedName{Name: istioAuthorizationPolicyName(testGatewayName, policy.Spec.TargetRef), Namespace: testNamespace}
Expand Down Expand Up @@ -1067,7 +1067,7 @@ var _ = Describe("AuthPolicy controller", func() {
Expect(err).ToNot(HaveOccurred())

// check policy status
Eventually(testPolicyIsReady(policy), 30*time.Second, 5*time.Second).Should(BeTrue())
Eventually(testPolicyIsAccepted(policy), 30*time.Second, 5*time.Second).Should(BeTrue())

// check istio authorizationpolicy
iapKey := types.NamespacedName{Name: istioAuthorizationPolicyName(testGatewayName, policy.Spec.TargetRef), Namespace: testNamespace}
Expand Down Expand Up @@ -1203,7 +1203,7 @@ var _ = Describe("AuthPolicy controller", func() {
Expect(err).ToNot(HaveOccurred())

// check policy status
Eventually(testPolicyIsReady(policy), 30*time.Second, 5*time.Second).Should(BeTrue())
Eventually(testPolicyIsAccepted(policy), 30*time.Second, 5*time.Second).Should(BeTrue())

// check authorino authconfig
authConfigKey := types.NamespacedName{Name: authConfigName(client.ObjectKeyFromObject(policy)), Namespace: testNamespace}
Expand Down Expand Up @@ -1285,10 +1285,10 @@ func testBasicAuthScheme() api.AuthSchemeSpec {
}
}

func testPolicyIsReady(policy *api.AuthPolicy) func() bool {
func testPolicyIsAccepted(policy *api.AuthPolicy) func() bool {
return func() bool {
existingPolicy := &api.AuthPolicy{}
err := k8sClient.Get(context.Background(), client.ObjectKeyFromObject(policy), existingPolicy)
return err == nil && meta.IsStatusConditionTrue(existingPolicy.Status.Conditions, "Available")
return err == nil && meta.IsStatusConditionTrue(existingPolicy.Status.Conditions, string(gatewayapiv1alpha2.PolicyConditionAccepted))
}
}
26 changes: 21 additions & 5 deletions controllers/authpolicy_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@ package controllers

import (
"context"
"errors"
"fmt"

"github.com/go-logr/logr"
"github.com/kuadrant/kuadrant-operator/pkg/common"
"golang.org/x/exp/slices"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
gatewayapiv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"

authorinoapi "github.com/kuadrant/authorino/api/v1beta2"
api "github.com/kuadrant/kuadrant-operator/api/v1beta2"
Expand Down Expand Up @@ -86,19 +89,32 @@ func (r *AuthPolicyReconciler) calculateStatus(ap *api.AuthPolicy, specErr error
return newStatus
}

func (r *AuthPolicyReconciler) availableCondition(targetNetworkObjectectKind string, specErr error, authConfigReady bool) *metav1.Condition {
func (r *AuthPolicyReconciler) availableCondition(targetNetworkObjectKind string, specErr error, authConfigReady bool) *metav1.Condition {
// Condition if there is no issue
cond := &metav1.Condition{
Type: APAvailableConditionType,
Type: string(gatewayapiv1alpha2.PolicyConditionAccepted),
Status: metav1.ConditionTrue,
Reason: fmt.Sprintf("%sProtected", targetNetworkObjectectKind),
Message: fmt.Sprintf("%s is protected", targetNetworkObjectectKind),
Reason: string(gatewayapiv1alpha2.PolicyReasonAccepted),
Message: fmt.Sprintf("AuthPolicy has been accepted. %s is protected", targetNetworkObjectKind),
}

if specErr != nil {
cond.Status = metav1.ConditionFalse
cond.Reason = "ReconciliationError"
cond.Message = specErr.Error()

switch {
// TargetNotFound
case errors.As(specErr, &common.ErrTargetNotFound{}):
cond.Reason = string(gatewayapiv1alpha2.PolicyReasonTargetNotFound)
// Invalid
case errors.As(specErr, &common.ErrInvalid{}):
cond.Reason = string(gatewayapiv1alpha2.PolicyReasonInvalid)
// Conflicted
case errors.As(specErr, &common.ErrConflict{}):
cond.Reason = string(gatewayapiv1alpha2.PolicyReasonConflicted)
default:
cond.Reason = "ReconciliationError"
}
} else if !authConfigReady {
cond.Status = metav1.ConditionFalse
cond.Reason = "AuthSchemeNotReady"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ var _ = Describe("Limitador Cluster EnvoyFilter controller", func() {
Expect(err).ToNot(HaveOccurred())
// Check RLP status is available
rlpKey := client.ObjectKey{Name: rlpName, Namespace: testNamespace}
Eventually(testRLPIsAvailable(rlpKey), time.Minute, 5*time.Second).Should(BeTrue())
Eventually(testRLPIsAccepted(rlpKey), time.Minute, 5*time.Second).Should(BeTrue())

// Check envoy filter
Eventually(func() bool {
Expand Down
10 changes: 5 additions & 5 deletions controllers/ratelimitpolicy_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ func (r *RateLimitPolicyReconciler) Reconcile(eventCtx context.Context, req ctrl
if delResErr == nil {
delResErr = err
}
return r.reconcileStatus(ctx, rlp, delResErr)
return r.reconcileStatus(ctx, rlp, common.ErrTargetNotFound{Kind: rlp.Kind(), TargetRef: rlp.GetTargetRef(), Err: delResErr})
}
return ctrl.Result{}, err
}
Expand Down Expand Up @@ -153,12 +153,12 @@ func (r *RateLimitPolicyReconciler) reconcileResources(ctx context.Context, rlp
// validate
err := rlp.Validate()
if err != nil {
return err
return common.ErrInvalid{Kind: rlp.Kind(), Err: err}
}

err = common.ValidateHierarchicalRules(rlp, targetNetworkObject)
if err != nil {
return err
return common.ErrInvalid{Kind: rlp.Kind(), Err: err}
}

// reconcile based on gateway diffs
Expand All @@ -180,7 +180,7 @@ func (r *RateLimitPolicyReconciler) reconcileResources(ctx context.Context, rlp
return err
}

// set annotation of policies afftecting the gateway - should be the last step, only when all the reconciliation steps succeed
// set annotation of policies affecting the gateway - should be the last step, only when all the reconciliation steps succeed
return r.ReconcileGatewayPolicyReferences(ctx, rlp, gatewayDiffObj)
}

Expand Down Expand Up @@ -212,7 +212,7 @@ func (r *RateLimitPolicyReconciler) deleteResources(ctx context.Context, rlp *ku

// Ensures only one RLP targets the network resource
func (r *RateLimitPolicyReconciler) reconcileNetworkResourceDirectBackReference(ctx context.Context, policy common.KuadrantPolicy, targetNetworkObject client.Object) error {
return r.ReconcileTargetBackReference(ctx, client.ObjectKeyFromObject(policy), targetNetworkObject, common.RateLimitPolicyBackRefAnnotation)
return r.ReconcileTargetBackReference(ctx, policy, targetNetworkObject, common.RateLimitPolicyBackRefAnnotation)
}

func (r *RateLimitPolicyReconciler) deleteNetworkResourceDirectBackReference(ctx context.Context, targetNetworkObject client.Object) error {
Expand Down
12 changes: 6 additions & 6 deletions controllers/ratelimitpolicy_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ var _ = Describe("RateLimitPolicy controller", func() {

// Check RLP status is available
rlpKey := client.ObjectKeyFromObject(rlp)
Eventually(testRLPIsAvailable(rlpKey), time.Minute, 5*time.Second).Should(BeTrue())
Eventually(testRLPIsAccepted(rlpKey), time.Minute, 5*time.Second).Should(BeTrue())

// Check HTTPRoute direct back reference
routeKey := client.ObjectKey{Name: routeName, Namespace: testNamespace}
Expand Down Expand Up @@ -310,7 +310,7 @@ var _ = Describe("RateLimitPolicy controller", func() {

// Check RLP status is available
rlpKey := client.ObjectKeyFromObject(rlp)
Eventually(testRLPIsAvailable(rlpKey), time.Minute, 5*time.Second).Should(BeTrue())
Eventually(testRLPIsAccepted(rlpKey), time.Minute, 5*time.Second).Should(BeTrue())

// Check wasm plugin
wasmPluginKey := client.ObjectKey{Name: rlptools.WASMPluginName(gateway), Namespace: testNamespace}
Expand Down Expand Up @@ -457,7 +457,7 @@ var _ = Describe("RateLimitPolicy controller", func() {

// Check RLP status is available
rlpKey := client.ObjectKey{Name: rlpName, Namespace: testNamespace}
Eventually(testRLPIsAvailable(rlpKey), time.Minute, 5*time.Second).Should(BeTrue())
Eventually(testRLPIsAccepted(rlpKey), time.Minute, 5*time.Second).Should(BeTrue())

// Check Gateway direct back reference
gwKey := client.ObjectKeyFromObject(gateway)
Expand Down Expand Up @@ -575,7 +575,7 @@ var _ = Describe("RateLimitPolicy controller", func() {

// Check RLP status is available
rlpKey := client.ObjectKey{Name: rlpName, Namespace: testNamespace}
Eventually(testRLPIsAvailable(rlpKey), time.Minute, 5*time.Second).Should(BeTrue())
Eventually(testRLPIsAccepted(rlpKey), time.Minute, 5*time.Second).Should(BeTrue())

// Check Gateway direct back reference
gwKey := client.ObjectKeyFromObject(gateway)
Expand Down Expand Up @@ -622,14 +622,14 @@ var _ = Describe("RateLimitPolicy controller", func() {
})
})

func testRLPIsAvailable(rlpKey client.ObjectKey) func() bool {
func testRLPIsAccepted(rlpKey client.ObjectKey) func() bool {
return func() bool {
existingRLP := &kuadrantv1beta2.RateLimitPolicy{}
err := k8sClient.Get(context.Background(), rlpKey, existingRLP)
if err != nil {
return false
}
if !meta.IsStatusConditionTrue(existingRLP.Status.Conditions, "Available") {
if !meta.IsStatusConditionTrue(existingRLP.Status.Conditions, string(gatewayapiv1alpha2.PolicyConditionAccepted)) {
return false
}

Expand Down
Loading