diff --git a/controllers/authpolicy_auth_config.go b/controllers/authpolicy_auth_config.go index 56476d6ed..35ec2f627 100644 --- a/controllers/authpolicy_auth_config.go +++ b/controllers/authpolicy_auth_config.go @@ -91,14 +91,14 @@ func (r *AuthPolicyReconciler) desiredAuthConfig(ap *api.AuthPolicy, targetNetwo func (r *AuthPolicyReconciler) policyHosts(ap *api.AuthPolicy, targetNetworkObject client.Object) ([]string, error) { if len(ap.Spec.AuthRules) == 0 { - return common.TargetHostnames(targetNetworkObject) + return common.TargetHostnames(targetNetworkObject), nil } uniqueHostnamesMap := make(map[string]any) for idx := range ap.Spec.AuthRules { if len(ap.Spec.AuthRules[idx].Hosts) == 0 { // When one of the rules does not have hosts, just return target hostnames - return common.TargetHostnames(targetNetworkObject) + return common.TargetHostnames(targetNetworkObject), nil } for _, hostname := range ap.Spec.AuthRules[idx].Hosts { diff --git a/controllers/authpolicy_istio_authorization_policy.go b/controllers/authpolicy_istio_authorization_policy.go index 3706b30f2..f71f7984f 100644 --- a/controllers/authpolicy_istio_authorization_policy.go +++ b/controllers/authpolicy_istio_authorization_policy.go @@ -36,10 +36,7 @@ func (r *AuthPolicyReconciler) reconcileIstioAuthorizationPolicies(ctx context.C return err } - targetHostnames, err := common.TargetHostnames(targetNetworkObject) - if err != nil { - return err - } + targetHostnames := common.TargetHostnames(targetNetworkObject) // TODO(guicassolato): should the rules filter only the hostnames valid for each gateway? toRules := istioAuthorizationPolicyRules(ap.Spec.AuthRules, targetHostnames, targetNetworkObject) diff --git a/controllers/rate_limiting_wasmplugin_controller.go b/controllers/rate_limiting_wasmplugin_controller.go index 6bdc4f17f..9b2787a61 100644 --- a/controllers/rate_limiting_wasmplugin_controller.go +++ b/controllers/rate_limiting_wasmplugin_controller.go @@ -187,29 +187,103 @@ func (r *RateLimitingWASMPluginReconciler) wasmPluginConfig(ctx context.Context, return nil, err } - rateLimitPolicies := gatewayAPITopology.GatewayPolicies() + rateLimitPolicies := gatewayAPITopology.PoliciesFromGateway(gw) logger.V(1).Info("wasmPluginConfig", "#RLPS", len(rateLimitPolicies)) // TODO(eastizle): Sort RLPs by name for consistent comparison with existing objects?? for _, rlp := range rateLimitPolicies { - route := gatewayAPITopology.GetPolicyHTTPRoute(rlp) + wasmRLP := r.WASMRateLimitPolicy(gatewayAPITopology, rlp, gw) + if wasmRLP == nil { + // no need to add the policy if there are no rules; + // a rlp can return no rules if all its limits fail to match any route rule + continue + } + + wasmPlugin.RateLimitPolicies = append(wasmPlugin.RateLimitPolicies, *wasmRLP) + } + + return wasmPlugin, nil +} + +func (r *RateLimitingWASMPluginReconciler) WASMRateLimitPolicy(t *common.KuadrantTopology, rlp *kuadrantv1beta2.RateLimitPolicy, gw *gatewayapiv1beta1.Gateway) []rlptools.Rule { + gwHostnames := common.TargetHostnames(gw) + + route := r.RouteFromRLP(t, rlp, gw) + + // narrow the list of hostnames specified in the route so we don't generate wasm rules that only apply to other gateways + // this is a no-op for the gateway rlp + hostnames := common.FilterValidSubdomains(gwHostnames, route.Spec.Hostnames) + if len(hostnames) == 0 { // it should only happen when the route specifies no hostnames + hostnames = gwHostnames + } + + // gwHostnames := common.Map(gwHostnamesTmp, func(str string) gatewayapiv1beta1.Hostname { return gatewayapiv1beta1.Hostname(str) }) + + wasmRLP := &wasm.RateLimitPolicy{ + Name: client.ObjectKeyFromObject(rlp).String(), + Domain: rlptools.LimitsNamespaceFromRLP(rlp), + Hostnames: common.HostnamesToStrings(hostnames), // we might be listing more hostnames than needed due to route selectors hostnames possibly being more restrictive + Service: common.KuadrantRateLimitClusterName, + Rules: nil, + } + + +} - // For gateway targeted policies, if no httproutes attached to the gateway, skip wasm config +func (r *RateLimitingWASMPluginReconciler) RouteFromRLP(t *common.KuadrantTopology, rlp *kuadrantv1beta2.RateLimitPolicy, gw *gatewayapiv1beta1.Gateway) *gatewayapiv1beta1.HTTPRoute { + route := t.GetPolicyHTTPRoute(rlp) + + if route == nil { + // The policy is targeting a gateway + // This gateway policy will be enforced into all HTTPRoutes that do not have a policy attached to it + + // Build imaginary route with all the routes not having a RLP targeting it + freeRoutes := t.GetFreeRoutes(gw) + + // For policies targeting a gateway, when no httproutes is attached to the gateway, skip wasm config // test wasm config when no http routes attached to the gateway //logger.V(1).Info("no httproutes attached to the targeted gateway, skipping wasm config for the gateway rlp", "ratelimitpolicy", gwRLPKey) + freeRules := make([]gatewayapiv1beta1.HTTPRouteRule, 0) + for idx := range freeRoutes { + freeroute := freeRoutes[idx] + freeRules = append(freeRules, route.Spec.Rules...) + } + route = &gatewayapiv1beta1.HTTPRoute{ + Spec: gatewayapiv1beta1.HTTPRouteSpec{ + Hostnames: gwHostnames, + Rules: freeRules, + }, + } + } + + return route +} + - if route == nil { - // policy targeting a gateway - // All the routes not having a RLP targeting it will be targeted by the - // Build imaginary route with all the routes not having a RLP targeting it + + wasmRules := rlptools.WasmRules(rlp, route) + if len(wasmRules) == 0 { + // no need to add the policy if there are no rules; + // a rlp can return no rules if all its limits fail to match any route rule + continue } + + wasmPlugin.RateLimitPolicies = append(wasmPlugin.RateLimitPolicies, wasm.RateLimitPolicy{ + Name: client.ObjectKeyFromObject(rlp).String(), + Domain: rlptools.LimitsNamespaceFromRLP(rlp), + Rules: wasmRules, + Hostnames: common.HostnamesToStrings(hostnames), // we might be listing more hostnames than needed due to route selectors hostnames possibly being more restrictive + Service: common.KuadrantRateLimitClusterName, + }) + } return wasmPlugin, nil } + // SHOULD BE DELETED func (r *RateLimitingWASMPluginReconciler) wasmPluginConfigTOBEDELETED(ctx context.Context, gw common.GatewayWrapper, rlpRefs []client.ObjectKey) (*wasm.Plugin, error) { logger, _ := logr.FromContext(ctx) diff --git a/pkg/common/gatewayapi_utils.go b/pkg/common/gatewayapi_utils.go index cc66daecd..eed66d91e 100644 --- a/pkg/common/gatewayapi_utils.go +++ b/pkg/common/gatewayapi_utils.go @@ -527,7 +527,7 @@ func (g GatewayWrapperList) Swap(i, j int) { } // TargetHostnames returns an array of hostnames coming from the network object (HTTPRoute, Gateway) -func TargetHostnames(targetNetworkObject client.Object) ([]string, error) { +func TargetHostnames(targetNetworkObject client.Object) []string { hosts := make([]string, 0) switch obj := targetNetworkObject.(type) { case *gatewayapiv1beta1.HTTPRoute: @@ -546,15 +546,12 @@ func TargetHostnames(targetNetworkObject client.Object) ([]string, error) { hosts = append(hosts, "*") } - return hosts, nil + return hosts } // ValidateHierarchicalRules returns error if the policy rules hostnames fail to match the target network hosts func ValidateHierarchicalRules(policy KuadrantPolicy, targetNetworkObject client.Object) error { - targetHostnames, err := TargetHostnames(targetNetworkObject) - if err != nil { - return err - } + targetHostnames := TargetHostnames(targetNetworkObject) if valid, invalidHost := ValidSubdomains(targetHostnames, policy.GetRulesHostnames()); !valid { return fmt.Errorf( diff --git a/pkg/common/kuadrant_topology.go b/pkg/common/kuadrant_topology.go index fe46f9212..8ae8f1b76 100644 --- a/pkg/common/kuadrant_topology.go +++ b/pkg/common/kuadrant_topology.go @@ -12,7 +12,7 @@ func NewKuadrantTopology(gateways []*gatewayapiv1beta1.Gateway, routes []*gatewa return &KuadrantTopology{} } -func (k *KuadrantTopology) GatewayPolicies() []KuadrantPolicy { +func (k *KuadrantTopology) PoliciesFromGateway(gateway *gatewayapiv1beta1.Gateway) []KuadrantPolicy { // TODO return nil } @@ -21,3 +21,8 @@ func (k *KuadrantTopology) GetPolicyHTTPRoute(policy KuadrantPolicy) *gatewayapi // TODO return nil } + +func (k *KuadrantTopology) GetFreeRoutes(gateway *gatewayapiv1beta1.Gateway) []*gatewayapiv1beta1.HTTPRoute { + // TODO + return nil +}