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

feat: tlspolicy enforced condition #635

Merged
merged 1 commit into from
May 13, 2024
Merged
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
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ run: generate fmt vet ## Run a controller from your host.
go run ./main.go

docker-build: ## Build docker image with the manager.
docker build -t $(IMG) . --load
docker build -t $(IMG) .

docker-push: ## Push docker image with the manager.
docker push $(IMG)
Expand Down
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
97 changes: 94 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,88 @@ 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, string(certmanv1.IssuerConditionReady)) {
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
}

if len(gwDiffObj.GatewaysWithValidPolicyRef) == 0 {
return errors.New("no valid gateways found")
}

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

for _, cert := range expectedCertificates {
Comment on lines +144 to +146
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this is what we want but there's no error if len(expectedCerificates) == 0 since this is already allowed at:

so I've kept it the same. In this case a TLSPolicy is still marked as enforced: true even if there are no Certificates created

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that is ok. If there are no expected certificates then we have done everything we can to enforce

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, string(certmanv1.CertificateConditionReady)) {
return fmt.Errorf("certificate %s not ready", cert.Name)
}
}
}

return nil
}
Loading
Loading