diff --git a/controllers/common.go b/controllers/common.go new file mode 100644 index 000000000..a11fb358d --- /dev/null +++ b/controllers/common.go @@ -0,0 +1,21 @@ +package controllers + +const ( + KuadrantAppName = "kuadrant" +) + +var ( + AppLabelKey = "app" + AppLabelValue = KuadrantAppName +) + +func CommonLabels() map[string]string { + return map[string]string{ + AppLabelKey: AppLabelValue, + "app.kubernetes.io/component": KuadrantAppName, + "app.kubernetes.io/managed-by": "kuadrant-operator", + "app.kubernetes.io/instance": KuadrantAppName, + "app.kubernetes.io/name": KuadrantAppName, + "app.kubernetes.io/part-of": KuadrantAppName, + } +} diff --git a/controllers/dns_helper.go b/controllers/dns_helper.go index 152194945..fbafe72f2 100644 --- a/controllers/dns_helper.go +++ b/controllers/dns_helper.go @@ -27,7 +27,7 @@ type dnsHelper struct { } func commonDNSRecordLabels(gwKey client.ObjectKey, p *v1alpha1.DNSPolicy) map[string]string { - commonLabels := map[string]string{} + commonLabels := CommonLabels() for k, v := range policyDNSRecordLabels(p) { commonLabels[k] = v } diff --git a/controllers/dns_workflow.go b/controllers/dns_workflow.go index 99787a34c..d0a8205cb 100644 --- a/controllers/dns_workflow.go +++ b/controllers/dns_workflow.go @@ -1,7 +1,63 @@ package controllers -import "github.com/kuadrant/policy-machinery/controller" +import ( + "github.com/samber/lo" -func NewDNSWorkflow() *controller.Workflow { - return &controller.Workflow{} + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/dynamic" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + + "github.com/kuadrant/policy-machinery/controller" + "github.com/kuadrant/policy-machinery/machinery" + + kuadrantdnsv1alpha1 "github.com/kuadrant/dns-operator/api/v1alpha1" +) + +const ( + DNSRecordKind = "DNSRecord" + StateDNSPolicyAcceptedKey = "DNSPolicyValid" +) + +var ( + DNSRecordResource = kuadrantdnsv1alpha1.GroupVersion.WithResource("dnsrecords") + DNSRecordGroupKind = schema.GroupKind{Group: kuadrantdnsv1alpha1.GroupVersion.Group, Kind: "DNSRecord"} +) + +//+kubebuilder:rbac:groups=core,resources=namespaces,verbs=get;list;watch +//+kubebuilder:rbac:groups=kuadrant.io,resources=dnspolicies,verbs=get;list;watch;update;patch;delete +//+kubebuilder:rbac:groups=kuadrant.io,resources=dnspolicies/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=kuadrant.io,resources=dnspolicies/finalizers,verbs=update + +//+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 { + return &controller.Workflow{ + Precondition: NewDNSPoliciesValidator().Subscription().Reconcile, + Tasks: []controller.ReconcileFunc{ + NewEffectiveDNSPoliciesReconciler(client).Subscription().Reconcile, + }, + Postcondition: NewDNSPolicyStatusUpdater(client).Subscription().Reconcile, + } +} + +func LinkListenerToDNSRecord(objs controller.Store) machinery.LinkFunc { + gateways := lo.Map(objs.FilterByGroupKind(machinery.GatewayGroupKind), controller.ObjectAs[*gwapiv1.Gateway]) + listeners := lo.FlatMap(lo.Map(gateways, func(g *gwapiv1.Gateway, _ int) *machinery.Gateway { + return &machinery.Gateway{Gateway: g} + }), machinery.ListenersFromGatewayFunc) + + return machinery.LinkFunc{ + From: machinery.ListenerGroupKind, + To: DNSRecordGroupKind, + Func: func(child machinery.Object) []machinery.Object { + return lo.FilterMap(listeners, func(l *machinery.Listener, _ int) (machinery.Object, bool) { + if dnsRecord, ok := child.(*controller.RuntimeObject).Object.(*kuadrantdnsv1alpha1.DNSRecord); ok { + return l, l.GetNamespace() == dnsRecord.GetNamespace() && + dnsRecord.GetName() == dnsRecordName(l.Gateway.Name, string(l.Name)) + } + return nil, false + }) + }, + } } diff --git a/controllers/dnspolicies_validator.go b/controllers/dnspolicies_validator.go new file mode 100644 index 000000000..80a5f612e --- /dev/null +++ b/controllers/dnspolicies_validator.go @@ -0,0 +1,55 @@ +package controllers + +import ( + "context" + "sync" + + "github.com/samber/lo" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + + "github.com/kuadrant/policy-machinery/controller" + "github.com/kuadrant/policy-machinery/machinery" + + kuadrantv1alpha1 "github.com/kuadrant/kuadrant-operator/api/v1alpha1" + "github.com/kuadrant/kuadrant-operator/pkg/library/kuadrant" +) + +func NewDNSPoliciesValidator() *DNSPoliciesValidator { + return &DNSPoliciesValidator{} +} + +type DNSPoliciesValidator struct{} + +func (r *DNSPoliciesValidator) Subscription() controller.Subscription { + return controller.Subscription{ + ReconcileFunc: r.validate, + Events: []controller.ResourceEventMatcher{ + {Kind: &machinery.GatewayGroupKind}, + {Kind: &kuadrantv1alpha1.DNSPolicyGroupKind}, + }, + } +} + +func (r *DNSPoliciesValidator) validate(ctx context.Context, _ []controller.ResourceEvent, topology *machinery.Topology, _ error, state *sync.Map) error { + logger := controller.LoggerFromContext(ctx).WithName("DNSPoliciesValidator") + + policies := lo.FilterMap(topology.Policies().Items(), func(item machinery.Policy, index int) (*kuadrantv1alpha1.DNSPolicy, bool) { + p, ok := item.(*kuadrantv1alpha1.DNSPolicy) + return p, ok + }) + + logger.V(1).Info("validating dns policies", "policies", len(policies)) + + state.Store(StateDNSPolicyAcceptedKey, lo.SliceToMap(policies, func(policy *kuadrantv1alpha1.DNSPolicy) (string, error) { + if len(policy.GetTargetRefs()) == 0 || len(topology.Targetables().Children(policy)) == 0 { + return policy.GetLocator(), kuadrant.NewErrTargetNotFound(policy.Kind(), policy.GetTargetRef(), + apierrors.NewNotFound(kuadrantv1alpha1.DNSPoliciesResource.GroupResource(), policy.GetName())) + } + return policy.GetLocator(), nil + })) + + logger.V(1).Info("finished validating dns policies") + + return nil +} diff --git a/controllers/dnspolicy_controller.go b/controllers/dnspolicy_controller.go index 749c6efc6..a7d33cf37 100644 --- a/controllers/dnspolicy_controller.go +++ b/controllers/dnspolicy_controller.go @@ -51,14 +51,6 @@ type DNSPolicyReconciler struct { dnsHelper dnsHelper } -//+kubebuilder:rbac:groups=core,resources=namespaces,verbs=get;list;watch -//+kubebuilder:rbac:groups=kuadrant.io,resources=dnspolicies,verbs=get;list;watch;update;patch;delete -//+kubebuilder:rbac:groups=kuadrant.io,resources=dnspolicies/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=kuadrant.io,resources=dnspolicies/finalizers,verbs=update - -//+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 (r *DNSPolicyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log := r.Logger().WithValues("DNSPolicy", req.NamespacedName) log.Info("Reconciling DNSPolicy") diff --git a/controllers/dnspolicy_status_updater.go b/controllers/dnspolicy_status_updater.go new file mode 100644 index 000000000..ea930558e --- /dev/null +++ b/controllers/dnspolicy_status_updater.go @@ -0,0 +1,37 @@ +package controllers + +import ( + "context" + "sync" + + "k8s.io/client-go/dynamic" + + "github.com/kuadrant/policy-machinery/controller" + "github.com/kuadrant/policy-machinery/machinery" + + kuadrantv1alpha1 "github.com/kuadrant/kuadrant-operator/api/v1alpha1" +) + +func NewDNSPolicyStatusUpdater(client *dynamic.DynamicClient) *DNSPolicyStatusUpdater { + return &DNSPolicyStatusUpdater{client: client} +} + +type DNSPolicyStatusUpdater struct { + client *dynamic.DynamicClient +} + +func (r *DNSPolicyStatusUpdater) Subscription() controller.Subscription { + return controller.Subscription{ + ReconcileFunc: r.updateStatus, + Events: []controller.ResourceEventMatcher{ + {Kind: &machinery.GatewayGroupKind}, + {Kind: &kuadrantv1alpha1.DNSPolicyGroupKind}, + {Kind: &DNSRecordGroupKind}, + }, + } +} + +func (r *DNSPolicyStatusUpdater) updateStatus(_ context.Context, _ []controller.ResourceEvent, _ *machinery.Topology, _ error, _ *sync.Map) error { + //ToDo Implement implement me !!! + return nil +} diff --git a/controllers/effective_dnspolicies_reconciler.go b/controllers/effective_dnspolicies_reconciler.go new file mode 100644 index 000000000..33b04d729 --- /dev/null +++ b/controllers/effective_dnspolicies_reconciler.go @@ -0,0 +1,36 @@ +package controllers + +import ( + "context" + "sync" + + "k8s.io/client-go/dynamic" + + "github.com/kuadrant/policy-machinery/controller" + "github.com/kuadrant/policy-machinery/machinery" + + kuadrantv1alpha1 "github.com/kuadrant/kuadrant-operator/api/v1alpha1" +) + +func NewEffectiveDNSPoliciesReconciler(client *dynamic.DynamicClient) *EffectiveDNSPoliciesReconciler { + return &EffectiveDNSPoliciesReconciler{client: client} +} + +type EffectiveDNSPoliciesReconciler struct { + client *dynamic.DynamicClient +} + +func (r *EffectiveDNSPoliciesReconciler) Subscription() controller.Subscription { + return controller.Subscription{ + ReconcileFunc: r.reconcile, + Events: []controller.ResourceEventMatcher{ + {Kind: &machinery.GatewayGroupKind}, + {Kind: &kuadrantv1alpha1.DNSPolicyGroupKind}, + {Kind: &DNSRecordGroupKind}, + }, + } +} + +func (r *EffectiveDNSPoliciesReconciler) reconcile(_ context.Context, _ []controller.ResourceEvent, _ *machinery.Topology, _ error, _ *sync.Map) error { + return nil +} diff --git a/controllers/state_of_the_world.go b/controllers/state_of_the_world.go index 945216ca9..4dd4c4466 100644 --- a/controllers/state_of_the_world.go +++ b/controllers/state_of_the_world.go @@ -9,6 +9,7 @@ import ( egv1alpha1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/go-logr/logr" authorinov1beta1 "github.com/kuadrant/authorino-operator/api/v1beta1" + kuadrantdnsv1alpha1 "github.com/kuadrant/dns-operator/api/v1alpha1" limitadorv1alpha1 "github.com/kuadrant/limitador-operator/api/v1alpha1" "github.com/kuadrant/policy-machinery/controller" "github.com/kuadrant/policy-machinery/machinery" @@ -43,6 +44,8 @@ import ( var ( ConfigMapGroupKind = schema.GroupKind{Group: corev1.GroupName, Kind: "ConfigMap"} operatorNamespace = env.GetString("OPERATOR_NAMESPACE", "kuadrant-system") + + ErrMissingTarget = fmt.Errorf("target not found") ) //+kubebuilder:rbac:groups=gateway.networking.k8s.io,resources=gatewayclasses,verbs=list;watch @@ -158,6 +161,7 @@ func (b *BootOptionsBuilder) getOptions() []controller.ControllerOption { opts = append(opts, b.getEnvoyGatewayOptions()...) opts = append(opts, b.getCertManagerOptions()...) opts = append(opts, b.getConsolePluginOptions()...) + opts = append(opts, b.getDNSOperatorOptions()...) return opts } @@ -294,13 +298,35 @@ func (b *BootOptionsBuilder) getConsolePluginOptions() []controller.ControllerOp return opts } +func (b *BootOptionsBuilder) getDNSOperatorOptions() []controller.ControllerOption { + isDNSRecordOwnedByDNSPolicy := func(c *kuadrantdnsv1alpha1.DNSRecord) bool { + return true + } + + var opts []controller.ControllerOption + opts = append(opts, + controller.WithRunnable("dnsrecord watcher", controller.Watch( + &kuadrantdnsv1alpha1.DNSRecord{}, DNSRecordResource, metav1.NamespaceAll, + controller.FilterResourcesByLabel[*kuadrantdnsv1alpha1.DNSRecord](fmt.Sprintf("%s=%s", AppLabelKey, AppLabelValue)), + controller.WithPredicates(ctrlruntimepredicate.NewTypedPredicateFuncs(isDNSRecordOwnedByDNSPolicy)))), + controller.WithObjectKinds( + DNSRecordGroupKind, + ), + controller.WithObjectLinks( + LinkListenerToDNSRecord, + ), + ) + + return opts +} + func (b *BootOptionsBuilder) Reconciler() controller.ReconcileFunc { mainWorkflow := &controller.Workflow{ Precondition: initWorkflow(b.client).Run, Tasks: []controller.ReconcileFunc{ NewAuthorinoReconciler(b.client).Subscription().Reconcile, NewLimitadorReconciler(b.client).Subscription().Reconcile, - NewDNSWorkflow().Run, + NewDNSWorkflow(b.client).Run, NewTLSWorkflow(b.client, b.isCertManagerInstalled).Run, NewAuthWorkflow().Run, NewRateLimitWorkflow().Run,