Skip to content

Commit

Permalink
feat: tlspolicy enforced condition
Browse files Browse the repository at this point in the history
  • Loading branch information
KevFan committed May 9, 2024
1 parent b1c1f4e commit 150bfc2
Show file tree
Hide file tree
Showing 9 changed files with 164 additions and 20 deletions.
3 changes: 2 additions & 1 deletion api/v1alpha1/dnspolicy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,8 @@ var _ kuadrant.Referrer = &DNSPolicy{}
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:metadata:labels="gateway.networking.k8s.io/policy=direct"
// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[0].reason`,description="DNSPolicy Status",priority=2
// +kubebuilder:printcolumn:name="Accepted",type=string,JSONPath=`.status.conditions[?(@.type=="Accepted")].status`,description="DNSPolicy Accepted",priority=2
// +kubebuilder:printcolumn:name="Enforced",type=string,JSONPath=`.status.conditions[?(@.type=="Enforced")].status`,description="DNSPolicy Enforced",priority=2
// +kubebuilder:printcolumn:name="TargetRefKind",type="string",JSONPath=".spec.targetRef.kind",description="Type of the referenced Gateway API resource",priority=2
// +kubebuilder:printcolumn:name="TargetRefName",type="string",JSONPath=".spec.targetRef.name",description="Name of the referenced Gateway API resource",priority=2
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
Expand Down
3 changes: 2 additions & 1 deletion api/v1alpha1/tlspolicy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,8 @@ var _ kuadrant.Referrer = &TLSPolicy{}
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:metadata:labels="gateway.networking.k8s.io/policy=direct"
// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[0].reason`,description="TLSPolicy Status",priority=2
// +kubebuilder:printcolumn:name="Accepted",type=string,JSONPath=`.status.conditions[?(@.type=="Accepted")].status`,description="TLSPolicy Accepted",priority=2
// +kubebuilder:printcolumn:name="Enforced",type=string,JSONPath=`.status.conditions[?(@.type=="Enforced")].status`,description="TLSPolicy Enforced",priority=2
// +kubebuilder:printcolumn:name="TargetRefKind",type="string",JSONPath=".spec.targetRef.kind",description="Type of the referenced Gateway API resource",priority=2
// +kubebuilder:printcolumn:name="TargetRefName",type="string",JSONPath=".spec.targetRef.name",description="Name of the referenced Gateway API resource",priority=2
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
Expand Down
11 changes: 8 additions & 3 deletions bundle/manifests/kuadrant.io_dnspolicies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,14 @@ spec:
scope: Namespaced
versions:
- additionalPrinterColumns:
- description: DNSPolicy Status
jsonPath: .status.conditions[0].reason
name: Status
- description: DNSPolicy Accepted
jsonPath: .status.conditions[?(@.type=="Accepted")].status
name: Accepted
priority: 2
type: string
- description: DNSPolicy Enforced
jsonPath: .status.conditions[?(@.type=="Enforced")].status
name: Enforced
priority: 2
type: string
- description: Type of the referenced Gateway API resource
Expand Down
11 changes: 8 additions & 3 deletions bundle/manifests/kuadrant.io_tlspolicies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,14 @@ spec:
scope: Namespaced
versions:
- additionalPrinterColumns:
- description: TLSPolicy Status
jsonPath: .status.conditions[0].reason
name: Status
- description: TLSPolicy Accepted
jsonPath: .status.conditions[?(@.type=="Accepted")].status
name: Accepted
priority: 2
type: string
- description: TLSPolicy Enforced
jsonPath: .status.conditions[?(@.type=="Enforced")].status
name: Enforced
priority: 2
type: string
- description: Type of the referenced Gateway API resource
Expand Down
11 changes: 8 additions & 3 deletions config/crd/bases/kuadrant.io_dnspolicies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,14 @@ spec:
scope: Namespaced
versions:
- additionalPrinterColumns:
- description: DNSPolicy Status
jsonPath: .status.conditions[0].reason
name: Status
- description: DNSPolicy Accepted
jsonPath: .status.conditions[?(@.type=="Accepted")].status
name: Accepted
priority: 2
type: string
- description: DNSPolicy Enforced
jsonPath: .status.conditions[?(@.type=="Enforced")].status
name: Enforced
priority: 2
type: string
- description: Type of the referenced Gateway API resource
Expand Down
11 changes: 8 additions & 3 deletions config/crd/bases/kuadrant.io_tlspolicies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,14 @@ spec:
scope: Namespaced
versions:
- additionalPrinterColumns:
- description: TLSPolicy Status
jsonPath: .status.conditions[0].reason
name: Status
- description: TLSPolicy Accepted
jsonPath: .status.conditions[?(@.type=="Accepted")].status
name: Accepted
priority: 2
type: string
- description: TLSPolicy Enforced
jsonPath: .status.conditions[?(@.type=="Enforced")].status
name: Enforced
priority: 2
type: string
- description: Type of the referenced Gateway API resource
Expand Down
4 changes: 2 additions & 2 deletions controllers/tlspolicy_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func (r *TLSPolicyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
if delResErr == nil {
delResErr = err
}
return r.reconcileStatus(ctx, tlsPolicy, kuadrant.NewErrTargetNotFound(tlsPolicy.Kind(), tlsPolicy.GetTargetRef(), delResErr))
return r.reconcileStatus(ctx, tlsPolicy, targetReferenceObject, kuadrant.NewErrTargetNotFound(tlsPolicy.Kind(), tlsPolicy.GetTargetRef(), delResErr))
}
return ctrl.Result{}, err
}
Expand Down Expand Up @@ -109,7 +109,7 @@ func (r *TLSPolicyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (

specErr := r.reconcileResources(ctx, tlsPolicy, targetReferenceObject)

statusResult, statusErr := r.reconcileStatus(ctx, tlsPolicy, specErr)
statusResult, statusErr := r.reconcileStatus(ctx, tlsPolicy, targetReferenceObject, specErr)

if specErr != nil {
return ctrl.Result{}, specErr
Expand Down
37 changes: 36 additions & 1 deletion controllers/tlspolicy_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
gatewayapiv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"

"github.com/kuadrant/kuadrant-operator/api/v1alpha1"
"github.com/kuadrant/kuadrant-operator/pkg/library/kuadrant"
)

var _ = Describe("TLSPolicy controller", func() {
Expand Down Expand Up @@ -85,6 +86,11 @@ var _ = Describe("TLSPolicy controller", func() {
"Message": Equal("TLSPolicy target test-gateway was not found"),
})),
)
g.Expect(tlsPolicy.Status.Conditions).ToNot(
ContainElement(MatchFields(IgnoreExtras, Fields{
"Type": Equal(string(kuadrant.PolicyConditionEnforced)),
})),
)
}, TestTimeoutMedium, time.Second).Should(Succeed())
})

Expand All @@ -100,6 +106,11 @@ var _ = Describe("TLSPolicy controller", func() {
"Message": Equal("TLSPolicy target test-gateway was not found"),
})),
)
g.Expect(tlsPolicy.Status.Conditions).ToNot(
ContainElement(MatchFields(IgnoreExtras, Fields{
"Type": Equal(string(kuadrant.PolicyConditionEnforced)),
})),
)
}, TestTimeoutMedium, time.Second).Should(Succeed())

By("creating a valid Gateway")
Expand All @@ -118,6 +129,14 @@ var _ = Describe("TLSPolicy controller", func() {
"Message": Equal("TLSPolicy has been accepted"),
})),
)
g.Expect(tlsPolicy.Status.Conditions).To(
ContainElement(MatchFields(IgnoreExtras, Fields{
"Type": Equal(string(kuadrant.PolicyConditionEnforced)),
"Status": Equal(metav1.ConditionTrue),
"Reason": Equal(string(kuadrant.PolicyConditionEnforced)),
"Message": Equal("TLSPolicy has been successfully enforced"),
})),
)
}, TestTimeoutMedium, time.Second).Should(Succeed())
})

Expand Down Expand Up @@ -146,6 +165,14 @@ var _ = Describe("TLSPolicy controller", func() {
"Message": Equal("TLSPolicy has been accepted"),
})),
)
g.Expect(tlsPolicy.Status.Conditions).To(
ContainElement(MatchFields(IgnoreExtras, Fields{
"Type": Equal(string(kuadrant.PolicyConditionEnforced)),
"Status": Equal(metav1.ConditionTrue),
"Reason": Equal(string(kuadrant.PolicyConditionEnforced)),
"Message": Equal("TLSPolicy has been successfully enforced"),
})),
)
}, TestTimeoutMedium, time.Second).Should(Succeed())
})

Expand Down Expand Up @@ -186,7 +213,7 @@ var _ = Describe("TLSPolicy controller", func() {
Expect(client.IgnoreNotFound(err)).ToNot(HaveOccurred())
})

It("should have accepted condition with status true", func() {
It("should have accepted and enforced condition with status true", func() {
Eventually(func(g Gomega) {
err := k8sClient.Get(ctx, client.ObjectKeyFromObject(tlsPolicy), tlsPolicy)
g.Expect(err).NotTo(HaveOccurred())
Expand All @@ -198,6 +225,14 @@ var _ = Describe("TLSPolicy controller", func() {
"Message": Equal("TLSPolicy has been accepted"),
})),
)
g.Expect(tlsPolicy.Status.Conditions).To(
ContainElement(MatchFields(IgnoreExtras, Fields{
"Type": Equal(string(kuadrant.PolicyConditionEnforced)),
"Status": Equal(metav1.ConditionTrue),
"Reason": Equal(string(kuadrant.PolicyConditionEnforced)),
"Message": Equal("TLSPolicy has been successfully enforced"),
})),
)
}, TestTimeoutMedium, time.Second).Should(Succeed())
})
})
Expand Down
93 changes: 90 additions & 3 deletions controllers/tlspolicy_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,28 @@ package controllers

import (
"context"
"errors"
"fmt"
"slices"

certmanv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
"k8s.io/apimachinery/pkg/api/equality"
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"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
gatewayapiv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"

"github.com/kuadrant/kuadrant-operator/api/v1alpha1"
"github.com/kuadrant/kuadrant-operator/pkg/library/kuadrant"
"github.com/kuadrant/kuadrant-operator/pkg/library/reconcilers"
"github.com/kuadrant/kuadrant-operator/pkg/library/utils"
)

func (r *TLSPolicyReconciler) reconcileStatus(ctx context.Context, tlsPolicy *v1alpha1.TLSPolicy, specErr error) (ctrl.Result, error) {
newStatus := r.calculateStatus(tlsPolicy, specErr)
func (r *TLSPolicyReconciler) reconcileStatus(ctx context.Context, tlsPolicy *v1alpha1.TLSPolicy, targetNetworkObject client.Object, specErr error) (ctrl.Result, error) {
newStatus := r.calculateStatus(ctx, tlsPolicy, targetNetworkObject, specErr)

equalStatus := equality.Semantic.DeepEqual(newStatus, tlsPolicy.Status)
if equalStatus && tlsPolicy.Generation == tlsPolicy.Status.ObservedGeneration {
Expand All @@ -57,7 +65,7 @@ func (r *TLSPolicyReconciler) reconcileStatus(ctx context.Context, tlsPolicy *v1
return ctrl.Result{}, nil
}

func (r *TLSPolicyReconciler) calculateStatus(tlsPolicy *v1alpha1.TLSPolicy, specErr error) *v1alpha1.TLSPolicyStatus {
func (r *TLSPolicyReconciler) calculateStatus(ctx context.Context, tlsPolicy *v1alpha1.TLSPolicy, targetNetworkObject client.Object, specErr error) *v1alpha1.TLSPolicyStatus {
newStatus := &v1alpha1.TLSPolicyStatus{
// Copy initial conditions. Otherwise, status will always be updated
Conditions: slices.Clone(tlsPolicy.Status.Conditions),
Expand All @@ -67,5 +75,84 @@ func (r *TLSPolicyReconciler) calculateStatus(tlsPolicy *v1alpha1.TLSPolicy, spe
acceptedCond := kuadrant.AcceptedCondition(tlsPolicy, specErr)
meta.SetStatusCondition(&newStatus.Conditions, *acceptedCond)

// Do not set enforced condition if Accepted condition is false
if meta.IsStatusConditionFalse(newStatus.Conditions, string(gatewayapiv1alpha2.PolicyReasonAccepted)) {
meta.RemoveStatusCondition(&newStatus.Conditions, string(kuadrant.PolicyConditionEnforced))
return newStatus
}

enforcedCond := r.enforcedCondition(ctx, tlsPolicy, targetNetworkObject)
meta.SetStatusCondition(&newStatus.Conditions, *enforcedCond)

return newStatus
}

func (r *TLSPolicyReconciler) enforcedCondition(ctx context.Context, tlsPolicy *v1alpha1.TLSPolicy, targetNetworkObject client.Object) *metav1.Condition {
if err := r.isIssuerReady(ctx, tlsPolicy); err != nil {
return kuadrant.EnforcedCondition(tlsPolicy, kuadrant.NewErrUnknown(tlsPolicy.Kind(), err), false)
}

if err := r.isCertificatesReady(ctx, tlsPolicy, targetNetworkObject); err != nil {
return kuadrant.EnforcedCondition(tlsPolicy, kuadrant.NewErrUnknown(tlsPolicy.Kind(), err), false)
}

return kuadrant.EnforcedCondition(tlsPolicy, nil, true)
}

func (r *TLSPolicyReconciler) isIssuerReady(ctx context.Context, tlsPolicy *v1alpha1.TLSPolicy) error {
var conditions []certmanv1.IssuerCondition

switch tlsPolicy.Spec.IssuerRef.Kind {
case "", certmanv1.IssuerKind:
issuer := &certmanv1.Issuer{}
if err := r.Client().Get(ctx, client.ObjectKey{Name: tlsPolicy.Spec.IssuerRef.Name, Namespace: tlsPolicy.Namespace}, issuer); err != nil {
return err
}
conditions = issuer.Status.Conditions
case certmanv1.ClusterIssuerKind:
issuer := &certmanv1.ClusterIssuer{}
if err := r.Client().Get(ctx, client.ObjectKey{Name: tlsPolicy.Spec.IssuerRef.Name}, issuer); err != nil {
return err
}
conditions = issuer.Status.Conditions
default:
return fmt.Errorf(`invalid value %q for issuerRef.kind. Must be empty, %q or %q`, tlsPolicy.Spec.IssuerRef.Kind, certmanv1.IssuerKind, certmanv1.ClusterIssuerKind)
}

transformedCond := utils.Map(conditions, func(c certmanv1.IssuerCondition) metav1.Condition {
return metav1.Condition{Reason: c.Reason, Status: metav1.ConditionStatus(c.Status), Type: string(c.Type), Message: c.Message}
})

if meta.IsStatusConditionFalse(transformedCond, "Ready") {
return errors.New("issuer not ready")
}

return nil
}

func (r *TLSPolicyReconciler) isCertificatesReady(ctx context.Context, tlsPolicy *v1alpha1.TLSPolicy, targetNetworkObject client.Object) error {
gwDiffObj, err := reconcilers.ComputeGatewayDiffs(ctx, r.Client(), tlsPolicy, targetNetworkObject)
if err != nil {
return err
}

for _, gw := range gwDiffObj.GatewaysWithValidPolicyRef {
expectedCertificates := r.expectedCertificatesForGateway(ctx, gw.Gateway, tlsPolicy)

for _, cert := range expectedCertificates {
c := &certmanv1.Certificate{}
if err := r.Client().Get(ctx, client.ObjectKeyFromObject(cert), c); err != nil {
return err
}
conditions := utils.Map(c.Status.Conditions, func(c certmanv1.CertificateCondition) metav1.Condition {
return metav1.Condition{Reason: c.Reason, Status: metav1.ConditionStatus(c.Status), Type: string(c.Type), Message: c.Message}
})

if meta.IsStatusConditionFalse(conditions, "Ready") {
return fmt.Errorf("certificate %s not ready", cert.Name)
}
}
}

return nil
}

0 comments on commit 150bfc2

Please sign in to comment.