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

RLP v2 controller #199

Merged
merged 9 commits into from
Jun 20, 2023
Merged

RLP v2 controller #199

merged 9 commits into from
Jun 20, 2023

Conversation

eguzki
Copy link
Contributor

@eguzki eguzki commented Jun 8, 2023

What

First implementation of the RLP v2 controller

Many issues to address and TODO in the code. But this is a working controller with limited functionality (yet)

No doc, no tests

Verification steps

  • Setup env
make local-setup
  • Apply Kuadrant CR
kubectl -n kuadrant-system apply -f - <<EOF
---
apiVersion: kuadrant.io/v1beta1
kind: Kuadrant
metadata:
  name: kuadrant-sample
spec: {}
EOF
  • Deploy toystore example deployment
kubectl apply -f examples/toystore/toystore.yaml
  • Deploy HTTPRoute
kubectl apply -f - <<EOF
---
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: HTTPRoute
metadata:
  name: toystore
spec:
  parentRefs:
  - name: istio-ingressgateway
    namespace: istio-system
  hostnames:
  - "*.toystore.acme.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: "/toys"
      method: GET
    - path:
        type: PathPrefix
        value: "/toys"
      method: POST
    backendRefs:
    - name: toystore
      port: 80
  - matches:
    - path:
        type: PathPrefix
        value: "/assets/"
    backendRefs:
    - name: toystore
      port: 80
EOF
  • Apply RLP v2
kubectl apply -f - <<EOF
---
apiVersion: kuadrant.io/v1beta2
kind: RateLimitPolicy
metadata:
  name: toystore-per-endpoint
spec:
  targetRef:
    group: gateway.networking.k8s.io
    kind: HTTPRoute
    name: toystore
  limits:
    toys:
      rates:
      - limit: 50
        duration: 1
        unit: minute
      counters:
      - auth.identity.username
      routeSelectors:
      - matches: # matches the 1st HTTPRouteRule (i.e. GET or POST to /toys*)
        - path:
            type: PathPrefix
            value: "/toys"
      when:
      - selector: auth.identity.group
        operator: neq
        value: admin
    assets:
      rates:
      - limit: 5
        duration: 1
        unit: minute
      - limit: 100
        duration: 12
        unit: hour
      routeSelectors:
      - matches: # matches the 2nd HTTPRouteRule (i.e. /assets/*)
        - path:
            type: PathPrefix
            value: "/assets/"
EOF

Check Limitador's configuration

❯ k get limitador limitador -n kuadrant-system -o yaml
apiVersion: limitador.kuadrant.io/v1alpha1
kind: Limitador
metadata:
  creationTimestamp: "2023-06-13T15:23:22Z"
  generation: 2
  name: limitador
  namespace: kuadrant-system
  ownerReferences:
  - apiVersion: kuadrant.io/v1beta1
    blockOwnerDeletion: true
    controller: true
    kind: Kuadrant
    name: kuadrant-sample
    uid: 115edcb9-620b-4394-b922-a44ae57f047f
  resourceVersion: "2425"
  uid: b3bf6ccd-faf9-4a77-a502-52bd9d609e3d
spec:
  limits:
  - conditions:
    - default/toystore-per-endpoint/assets == "1"
    max_value: 5
    namespace: istio-system/istio-ingressgateway#*.toystore.acme.com
    seconds: 60
    variables: []
  - conditions:
    - default/toystore-per-endpoint/assets == "1"
    max_value: 100
    namespace: istio-system/istio-ingressgateway#*.toystore.acme.com
    seconds: 43200
    variables: []
  - conditions:
    - default/toystore-per-endpoint/toys == "1"
    max_value: 50
    namespace: istio-system/istio-ingressgateway#*.toystore.acme.com
    seconds: 60
    variables:
    - auth.identity.username
status:
  conditions:
  - lastTransitionTime: "2023-06-13T15:23:32Z"
    message: Limitador is ready
    reason: Ready
    status: "True"
    type: Ready
  observedGeneration: 2
  service:
    host: limitador-limitador.kuadrant-system.svc.cluster.local
    ports:
      grpc: 8081
      http: 8080

Check WASM plugin config

❯ k get wasmplugin kuadrant-istio-ingressgateway -n istio-system  -o yaml
apiVersion: extensions.istio.io/v1alpha1
kind: WasmPlugin
metadata:
  creationTimestamp: "2023-06-16T14:47:38Z"
  generation: 1
  name: kuadrant-istio-ingressgateway
  namespace: istio-system
  resourceVersion: "2631"
  uid: a13a3a91-2b15-4cdb-9936-965c01501a55
spec:
  phase: STATS
  pluginConfig:
    failureMode: deny
    rateLimitPolicies:
    - domain: istio-system/istio-ingressgateway#*.toystore.acme.com
      hostnames:
      - '*.toystore.acme.com'
      name: '*.toystore.acme.com'
      rules:
      - conditions:
        - allOf:
          - operator: startswith
            selector: request.url_path
            value: /assets/
        data:
        - static:
            key: default/toystore-per-endpoint/assets
            value: "1"
      - conditions:
        - allOf:
          - operator: startswith
            selector: request.url_path
            value: /toys
          - operator: neq
            selector: auth.identity.group
            value: admin
        data:
        - static:
            key: default/toystore-per-endpoint/toys
            value: "1"
        - selector:
            selector: auth.identity.username
      service: kuadrant-rate-limiting-service
  selector:
    matchLabels:
      app: istio-ingressgateway
      istio: ingressgateway
  url: oci://quay.io/kuadrant/wasm-shim:latest

Note that e2e rate limiting does not work. WASM shim work Kuadrant/wasm-shim#35 is needed.

@eguzki eguzki changed the title RLP v2 controller RLP v2 controller (try 01) Jun 13, 2023
@eguzki eguzki changed the title RLP v2 controller (try 01) RLP v2 controller Jun 13, 2023
@eguzki eguzki marked this pull request as ready for review June 13, 2023 15:26
@eguzki eguzki requested a review from a team as a code owner June 13, 2023 15:26
@eguzki
Copy link
Contributor Author

eguzki commented Jun 13, 2023

ready for initial review @guicassolato

Still lot of work to be done, but sets a baseline we can merge in rlp-v2-base branch

Comment on lines +34 to +36
// ContextSelector defines one item from the well known attributes
// Attributes: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/advanced/attributes
// Well-known selectors: https://github.com/Kuadrant/architecture/blob/main/rfcs/0001-rlp-v2.md#well-known-selectors
Copy link
Contributor

Choose a reason for hiding this comment

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

if rlp == nil {
return flattenActions
}
// TODO implement one of constraint
Copy link
Contributor

Choose a reason for hiding this comment

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

I see at least a couple ways to implement this. E.g. https://github.com/Kuadrant/authorino/blob/5db6fb9153d5229188b03292249cbf04bfb5fafb/install/crd/kustomization.yaml#L15-L21.

Is that what you had in mind? Or perhaps a validating webhook instead? Or yet, "late" in the process, in the controller, during reconciliation?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is the OpenAPIv3 validation that prevents objects to be created and/or updated without needing any validation webhook.

)

type GatewayAction struct {
Configurations []kuadrantv1beta1.Configuration `json:"configurations"`
type SelectorSpec struct {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we move this or part of it to a common package?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good idea. What do you propose?

  • github.com/kuadrant/kuadrant-operator/api/v1beta2 ?
  • github.com/kuadrant/kuadrant-operator/pkg/types ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done, moved to github.com/kuadrant/kuadrant-operator/pkg/rlptools/wasm

// They are named by a dot-separated path (e.g. request.path)
// Examples:
// "request.path" -> The path portion of the URL
Selector string `json:"selector"`
Copy link
Contributor

Choose a reason for hiding this comment

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

What about defining a type WellknownSelector string?

Suggested change
Selector string `json:"selector"`
Selector WellknownSelector `json:"selector"`

It would also serve as the type for PatternExpression.Selector:

Selector string `json:"selector"`

Copy link
Contributor Author

Choose a reason for hiding this comment

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

in

type ContextSelector string
we use ContextSelector. Shall we change that as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

using ContextSelector in WASM plugin Configuration object

// +optional
GatewayActions []GatewayAction `json:"gateway_actions,omitempty"`
Rules []Rule `json:"rules,omitempty"`
Copy link
Contributor

Choose a reason for hiding this comment

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

Are we also renaming failure_mode_deny and rate_limit_policies as in Kuadrant/wasm-shim#35 (comment)?

@eguzki eguzki force-pushed the rlp-v2-controller branch from 5c06205 to 3966aa6 Compare June 16, 2023 14:18
Copy link
Contributor

@guicassolato guicassolato left a comment

Choose a reason for hiding this comment

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

Excellent, @eguzki! 🚀

I've left a few reminders for when continuing the work in the next PRs.

Comment on lines +181 to +184
for domain, rules := range wasmRulesByDomain {
rateLimitPolicy := wasm.RateLimitPolicy{
Name: domain,
Domain: common.MarshallNamespace(gw.Key(), domain),
Copy link
Contributor

Choose a reason for hiding this comment

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

conditions := make([]wasm.Condition, 0)

for routeSelectorIdx := range limit.RouteSelectors {
// TODO(eastizle): what if there are only Hostnames (i.e. empty "matches" list)
Copy link
Contributor

Choose a reason for hiding this comment

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

That means it matches all HTTPRouteRules.

Comment on lines +65 to +68
for matchIdx := range limit.RouteSelectors[routeSelectorIdx].Matches {
condition := wasm.Condition{
AllOf: patternExpresionsFromMatch(&limit.RouteSelectors[routeSelectorIdx].Matches[matchIdx]),
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Actually, the Wasm conditions should not come directly from the limit, but use the limit's routeSelectors to select the rules from within the HTTPRoutes: #200

Comment on lines +83 to +90
// merge when expression in the same condition
// must be done after adding route level conditions when no route selector are available
// prevent conditions only filled with "when" definitions
for whenIdx := range limit.When {
for idx := range conditions {
conditions[idx].AllOf = append(conditions[idx].AllOf, patternExpresionFromWhen(limit.When[whenIdx]))
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

We're drifting a bit from the proposed "soft conditions" implemented in Limitador (see last bullet point at https://github.com/Kuadrant/architecture/blob/main/rfcs/0001-rlp-v2.md#highlights), by going one step ahead to improve performance.

By evaluating the RLP's when condition as a normal condition in the Wasm shim (just like the conditions built from routeSelectors), the RL request will only be sent to Limitador when there's truly at least one limit whose counters to increment.

@eguzki eguzki merged commit 93baa9a into rlp-v2-base Jun 20, 2023
@eguzki eguzki deleted the rlp-v2-controller branch June 20, 2023 11:37
@eguzki
Copy link
Contributor Author

eguzki commented Jun 20, 2023

@guicassolato consider rebasing rlp-v2-base on top of current main to bring all new stuff since it was created. I think no one is currently working in this rlp-v2 so we are safe rebasing it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants