apiVersion: v1
kind: ConfigMap
metadata:
  name: apicurio-envoy-config
  labels:
    app: apicurio
data:
  envoy.yaml: |
    # The administration endpoint uses a Unix socket instead of TCP in order
    # to avoid exposing it outside of the pod. Requests for metrics and
    # probes will go via an HTTP listener that only accepts requests for the
    # /metrics and /ready paths.
    admin:
      access_log_path: /dev/null
      address:
        socket_address:
          address: 0.0.0.0
          port_value: 9901

    static_resources:

      clusters:

      # This backend is used to send metrics and probes requests to the
      # administration endpoint.
      - name: admin
        connect_timeout: 1s
        type: STATIC
        lb_policy: ROUND_ROBIN
        load_assignment:
          cluster_name: admin
          endpoints:
          - lb_endpoints:
            - endpoint:
                address:
                  socket_address:
                    address: 0.0.0.0
                    port_value: 9901

      # This cluster is used to send requests to the backend.
      - name: backend
        connect_timeout: 2s
        type: STATIC
        lb_policy: ROUND_ROBIN
        load_assignment:
          cluster_name: backend
          endpoints:
          - lb_endpoints:
            - endpoint:
                address:
                  socket_address:
                    address: 127.0.0.1
                    port_value: 8080

      # This cluster is used to send request to the rate limiting service.
      - name: limiter
        connect_timeout: 1s
        type: STRICT_DNS
        #consider setting dns_refresh_rate
        # dns_refresh_rate: 30000ms #im not sure if it's configured like this
        lb_policy: ROUND_ROBIN
        http2_protocol_options: {}
        load_assignment:
          cluster_name: limiter
          endpoints:
          - lb_endpoints:
            - endpoint:
                address:
                  socket_address:
                    #HEEEY workaround here because of nameSuffix
                    address: limitador-mt
                    port_value: 8081

      listeners:

      # This listener is used to accept /metrics and /ready requests.
      # Everything else will be rejected.
      - name: admin
        address:
          socket_address:
            address: 0.0.0.0
            port_value: 9000
        filter_chains:
        - filters:
          - name: envoy.filters.network.http_connection_manager
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
              stat_prefix: admin
              route_config:
                name: admin
                virtual_hosts:
                - name: admin
                  domains:
                  - "*"
                  routes:
                  - name: ready
                    match:
                      path: /ready
                    route:
                      cluster: admin
                  - name: metrics
                    match:
                      path: /metrics
                    route:
                      cluster: admin
                      prefix_rewrite: /stats/prometheus
              http_filters:
              - name: envoy.filters.http.router

      # This listener is used to accept inbound API requests.
      - name: ingress
        address:
          socket_address:
            address: 0.0.0.0
            port_value: 38080
        filter_chains:
        - filters:
          - name: envoy.filters.network.http_connection_manager
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
              access_log:
              - name: envoy.access_loggers.file
                typed_config:
                  "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog
                  path: /dev/stdout
              stat_prefix: ingress
              route_config:
                name: backend
                virtual_hosts:
                - name: all
                  domains:
                  - "*"
                  routes:

                  # Apply rate limit to each tenant
                  - name: apicurio-tenants
                    match:
                      safe_regex:
                        regex: "\/t\/.+\/.*"
                        google_re2: {}
                    route:
                      cluster: backend
                      rate_limits:
                      # normal plan rate limit
                      - actions:
                        - generic_key:
                            descriptor_key: component
                            descriptor_value: srs_apicurio_registry
                        - request_headers:
                            descriptor_key: tenant_id
                            header_name: X-Registry-Tenant-Id

                  # This is an example of how to define a rate limit for a
                  # specific path.
                  # - name: my_path
                  #   match:
                  #     path: /my_path
                  #   route:
                  #     cluster: backend
                  #     rate_limits:
                  #     - actions:
                  #       - generic_key:
                  #           descriptor_key: example_descriptor_key
                  #           descriptor_value: example_descriptor_value

                  # Everything else not matching one of the rules above goes
                  # it's rate limited by a local rate limit to avoid over loading the ratelimit service
                  - name: default
                    match:
                      prefix: /
                    route:
                      cluster: backend
                    typed_per_filter_config:
                      envoy.filters.http.local_ratelimit:
                        "@type": type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit
                        stat_prefix: http_local_rate_limiter
                        token_bucket:
                          max_tokens: 60
                          tokens_per_fill: 60
                          fill_interval: 30s
                        filter_enabled:
                          runtime_key: local_rate_limit_enabled
                          default_value:
                            numerator: 100
                            denominator: HUNDRED
                        filter_enforced:
                          runtime_key: local_rate_limit_enforced
                          default_value:
                            numerator: 100
                            denominator: HUNDRED

              http_filters:

              #custom lua filter to parse the path and set the tenantId as a header
              #example from here https://github.com/ibm-cloud-architecture/tutorial-istio-envoy-lua-filters/blob/master/example-1-query/envoy.yaml
              - name: envoy.filters.http.lua
                typed_config:
                  "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
                  inline_code: |
                    function envoy_on_request(request_handle)
                      local path = request_handle:headers():get(":path")
                      local delimiter = "/"
                      local tid_delimiter = false
                      local index = 0
                      for item in (path..delimiter):gmatch("(.-)"..delimiter) do
                        if index == 0 then
                        elseif index == 1 and item == "t" then
                          tid_delimiter = true
                        elseif index == 2 and tid_delimiter and item ~= "" then
                          request_handle:logDebug("Extracted tenantId " .. item)
                          request_handle:headers():add("X-Registry-Tenant-Id", item)
                          return
                        else
                          return
                        end
                        index = index + 1
                      end
                    end

              # config for main safety local rate limit
              - name: envoy.filters.http.local_ratelimit
                typed_config:
                  "@type": type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit
                  stat_prefix: http_local_rate_limiter

              # This is needed to enable the rate limiter:
              - name: envoy.filters.http.ratelimit
                typed_config:
                  "@type": type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit
                  domain: stage:apicurio-registry
                  failure_mode_deny: false
                  timeout: 2s
                  rate_limit_service:
                    grpc_service:
                      envoy_grpc:
                        cluster_name: limiter
                    transport_api_version: V3

              # This is mandatory in order to have the HTTP routes above.
              - name: envoy.filters.http.router

              # We need this in order to generate JSON responses according to
              # our API guidelines, otherwise Envoy will generate plain text
              # responses.
              local_reply_config:
                mappers:
                - filter:
                    status_code_filter:
                      comparison:
                        op: EQ
                        value:
                          default_value: 429
                          runtime_key: none
                  body_format_override:
                    json_format:
                      kind: "Error"
                      id: "429"
                      code: "429"
                      reason: "Too Many Requests"
