Skip to content

Commit

Permalink
sotw dnspolicy: dnspolicies reconciliation
Browse files Browse the repository at this point in the history
Signed-off-by: Michael Nairn <mnairn@redhat.com>
  • Loading branch information
mikenairn committed Oct 22, 2024
1 parent 9f851b8 commit b55dbdf
Show file tree
Hide file tree
Showing 11 changed files with 241 additions and 446 deletions.
77 changes: 1 addition & 76 deletions controllers/dns_helper.go
Original file line number Diff line number Diff line change
@@ -1,101 +1,26 @@
package controllers

import (
"context"
"fmt"
"net"
"strings"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1"

kuadrantdnsv1alpha1 "github.com/kuadrant/dns-operator/api/v1alpha1"
"github.com/kuadrant/dns-operator/pkg/builder"

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

const (
LabelGatewayReference = "kuadrant.io/gateway"
LabelGatewayNSRef = "kuadrant.io/gateway-namespace"
LabelListenerReference = "kuadrant.io/listener-name"
)

type dnsHelper struct {
client.Client
}

func commonDNSRecordLabels(gwKey client.ObjectKey, p *v1alpha1.DNSPolicy) map[string]string {
commonLabels := CommonLabels()
for k, v := range policyDNSRecordLabels(p) {
commonLabels[k] = v
}
for k, v := range gatewayDNSRecordLabels(gwKey) {
commonLabels[k] = v
}
return commonLabels
}

func policyDNSRecordLabels(p *v1alpha1.DNSPolicy) map[string]string {
return map[string]string{
p.DirectReferenceAnnotationName(): p.Name,
fmt.Sprintf("%s-namespace", p.DirectReferenceAnnotationName()): p.Namespace,
}
}

func gatewayDNSRecordLabels(gwKey client.ObjectKey) map[string]string {
return map[string]string{
LabelGatewayNSRef: gwKey.Namespace,
LabelGatewayReference: gwKey.Name,
}
}

// removeDNSForDeletedListeners remove any DNSRecords that are associated with listeners that no longer exist in this gateway
func (dh *dnsHelper) removeDNSForDeletedListeners(ctx context.Context, upstreamGateway *gatewayapiv1.Gateway) error {
dnsList := &kuadrantdnsv1alpha1.DNSRecordList{}
//List all dns records that belong to this gateway
labelSelector := &client.MatchingLabels{
LabelGatewayReference: upstreamGateway.Name,
}
if err := dh.List(ctx, dnsList, labelSelector, &client.ListOptions{Namespace: upstreamGateway.Namespace}); err != nil {
return err
}

for i, dnsRecord := range dnsList.Items {
listenerExists := false
rootHostMatches := false
for _, listener := range upstreamGateway.Spec.Listeners {
if listener.Name == gatewayapiv1.SectionName(dnsRecord.Labels[LabelListenerReference]) {
listenerExists = true
rootHostMatches = string(*listener.Hostname) == dnsRecord.Spec.RootHost
break
}
}
if !listenerExists || !rootHostMatches {
if err := dh.Delete(ctx, &dnsList.Items[i], &client.DeleteOptions{}); client.IgnoreNotFound(err) != nil {
return err
}
}
}
return nil
}

func dnsRecordName(gatewayName, listenerName string) string {
return fmt.Sprintf("%s-%s", gatewayName, listenerName)
}

func (dh *dnsHelper) deleteDNSRecordForListener(ctx context.Context, owner metav1.Object, listener gatewayapiv1.Listener) error {
recordName := dnsRecordName(owner.GetName(), string(listener.Name))
dnsRecord := kuadrantdnsv1alpha1.DNSRecord{
ObjectMeta: metav1.ObjectMeta{
Name: recordName,
Namespace: owner.GetNamespace(),
},
}
return dh.Delete(ctx, &dnsRecord, &client.DeleteOptions{})
}

// GatewayWrapper is a wrapper for gateway to implement interface form the builder
type GatewayWrapper struct {
*gatewayapiv1.Gateway
Expand All @@ -106,7 +31,7 @@ func NewGatewayWrapper(gateway *gatewayapiv1.Gateway) *GatewayWrapper {
return &GatewayWrapper{Gateway: gateway}
}

func (g GatewayWrapper) GetAddresses() []builder.TargetAddress {
func (g *GatewayWrapper) GetAddresses() []builder.TargetAddress {
addresses := make([]builder.TargetAddress, len(g.Status.Addresses))
for i, address := range g.Status.Addresses {
addresses[i] = builder.TargetAddress{
Expand Down
5 changes: 3 additions & 2 deletions controllers/dns_workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
Expand Down Expand Up @@ -40,11 +41,11 @@ var (
//+kubebuilder:rbac:groups=kuadrant.io,resources=dnsrecords,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=kuadrant.io,resources=dnsrecords/status,verbs=get

func NewDNSWorkflow(client *dynamic.DynamicClient) *controller.Workflow {
func NewDNSWorkflow(client *dynamic.DynamicClient, scheme *runtime.Scheme) *controller.Workflow {
return &controller.Workflow{
Precondition: NewDNSPoliciesValidator().Subscription().Reconcile,
Tasks: []controller.ReconcileFunc{
NewEffectiveDNSPoliciesReconciler(client).Subscription().Reconcile,
NewEffectiveDNSPoliciesReconciler(client, scheme).Subscription().Reconcile,
},
Postcondition: NewDNSPolicyStatusUpdater(client).Subscription().Reconcile,
}
Expand Down
156 changes: 1 addition & 155 deletions controllers/dnspolicy_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,165 +17,11 @@ limitations under the License.
package controllers

import (
"context"
"fmt"

"github.com/prometheus/client_golang/prometheus"
"sigs.k8s.io/controller-runtime/pkg/metrics"

apierrors "k8s.io/apimachinery/pkg/api/errors"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/handler"
crlog "sigs.k8s.io/controller-runtime/pkg/log"
gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1"

kuadrantdnsv1alpha1 "github.com/kuadrant/dns-operator/api/v1alpha1"

"github.com/kuadrant/kuadrant-operator/api/v1alpha1"
kuadrantgatewayapi "github.com/kuadrant/kuadrant-operator/pkg/library/gatewayapi"
"github.com/kuadrant/kuadrant-operator/pkg/library/kuadrant"
"github.com/kuadrant/kuadrant-operator/pkg/library/mappers"
"github.com/kuadrant/kuadrant-operator/pkg/library/reconcilers"
"sigs.k8s.io/controller-runtime/pkg/metrics"
)

const DNSPolicyFinalizer = "kuadrant.io/dns-policy"

type DNSPolicyRefsConfig struct{}

// DNSPolicyReconciler reconciles a DNSPolicy object
type DNSPolicyReconciler struct {
*reconcilers.BaseReconciler
TargetRefReconciler reconcilers.TargetRefReconciler
dnsHelper dnsHelper
}

func (r *DNSPolicyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := r.Logger().WithValues("DNSPolicy", req.NamespacedName)
log.Info("Reconciling DNSPolicy")
ctx = crlog.IntoContext(ctx, log)

previous := &v1alpha1.DNSPolicy{}
if err := r.Client().Get(ctx, req.NamespacedName, previous); err != nil {
log.Info("error getting dns policy", "error", err)
return ctrl.Result{}, client.IgnoreNotFound(err)
}

dnsPolicy := previous.DeepCopy()
log.V(3).Info("DNSPolicyReconciler Reconcile", "dnsPolicy", dnsPolicy)

markedForDeletion := dnsPolicy.GetDeletionTimestamp() != nil

targetNetworkObject, err := reconcilers.FetchTargetRefObject(ctx, r.Client(), dnsPolicy.GetTargetRef(), dnsPolicy.Namespace, dnsPolicy.TargetProgrammedGatewaysOnly())
if err != nil {
if !markedForDeletion {
if apierrors.IsNotFound(err) {
log.V(3).Info("Network object not found. Cleaning up")
delResErr := r.deleteResources(ctx, dnsPolicy, nil)
if delResErr == nil {
delResErr = err
}
return ctrl.Result{}, kuadrant.NewErrTargetNotFound(dnsPolicy.Kind(), dnsPolicy.GetTargetRef(), delResErr)
}
return ctrl.Result{}, err
}
targetNetworkObject = nil // we need the object set to nil when there's an error, otherwise deleting the resources (when marked for deletion) will panic
}

if markedForDeletion {
log.V(3).Info("cleaning up dns policy")
if controllerutil.ContainsFinalizer(dnsPolicy, DNSPolicyFinalizer) {
if err := r.deleteResources(ctx, dnsPolicy, targetNetworkObject); err != nil {
return ctrl.Result{}, err
}
if err := r.RemoveFinalizer(ctx, dnsPolicy, DNSPolicyFinalizer); err != nil {
return ctrl.Result{}, err
}
}

return ctrl.Result{}, nil
}

// add finalizer to the dnsPolicy
if !controllerutil.ContainsFinalizer(dnsPolicy, DNSPolicyFinalizer) {
if err := r.AddFinalizer(ctx, dnsPolicy, DNSPolicyFinalizer); client.IgnoreNotFound(err) != nil {
return ctrl.Result{Requeue: true}, err
} else if apierrors.IsNotFound(err) {
return ctrl.Result{}, err
}
}

specErr := r.reconcileResources(ctx, dnsPolicy, targetNetworkObject)

return ctrl.Result{}, specErr
}

func (r *DNSPolicyReconciler) reconcileResources(ctx context.Context, dnsPolicy *v1alpha1.DNSPolicy, targetNetworkObject client.Object) error {
// reconcile based on gateway diffs
gatewayDiffObj, err := reconcilers.ComputeGatewayDiffs(ctx, r.Client(), dnsPolicy, targetNetworkObject)
if err != nil {
return err
}

if err = r.reconcileDNSRecords(ctx, dnsPolicy, gatewayDiffObj); err != nil {
return fmt.Errorf("error reconciling DNSRecords %w", err)
}

// set direct back ref - i.e. claim the target network object as taken asap
if err = r.TargetRefReconciler.ReconcileTargetBackReference(ctx, dnsPolicy, targetNetworkObject, dnsPolicy.DirectReferenceAnnotationName()); err != nil {
return fmt.Errorf("reconcile TargetBackReference error %w", err)
}

// set annotation of policies affecting the gateway
if err := r.TargetRefReconciler.ReconcileGatewayPolicyReferences(ctx, dnsPolicy, gatewayDiffObj); err != nil {
return fmt.Errorf("ReconcileGatewayPolicyReferences error %w", err)
}

return nil
}

func (r *DNSPolicyReconciler) deleteResources(ctx context.Context, dnsPolicy *v1alpha1.DNSPolicy, targetNetworkObject client.Object) error {
// remove direct back ref
if targetNetworkObject != nil {
if err := r.TargetRefReconciler.DeleteTargetBackReference(ctx, targetNetworkObject, dnsPolicy.DirectReferenceAnnotationName()); err != nil {
return err
}
}

gatewayDiffObj, err := reconcilers.ComputeGatewayDiffs(ctx, r.Client(), dnsPolicy, targetNetworkObject)
if err != nil {
return err
}

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

func (r *DNSPolicyReconciler) SetupWithManager(mgr ctrl.Manager) error {
ok, err := kuadrantgatewayapi.IsGatewayAPIInstalled(mgr.GetRESTMapper())
if err != nil {
return err
}
if !ok {
r.Logger().Info("DNSPolicy controller disabled. GatewayAPI was not found")
return nil
}

gatewayEventMapper := mappers.NewGatewayEventMapper(
v1alpha1.NewDNSPolicyType(),
mappers.WithLogger(r.Logger().WithName("gateway.mapper")),
mappers.WithClient(mgr.GetClient()),
)

r.dnsHelper = dnsHelper{Client: r.Client()}
ctrlr := ctrl.NewControllerManagedBy(mgr).
For(&v1alpha1.DNSPolicy{}).
Owns(&kuadrantdnsv1alpha1.DNSRecord{}).
Watches(&gatewayapiv1.Gateway{}, handler.EnqueueRequestsFromMapFunc(gatewayEventMapper.Map))
return ctrlr.Complete(r)
}

const (
dnsPolicyNameLabel = "dns_policy_name"
dnsPolicyNamespaceLabel = "dns_policy_namespace"
Expand Down
Loading

0 comments on commit b55dbdf

Please sign in to comment.