Use Capturing groups in k8s ingress

I want to use Kong’s Capturing Groups mode in The Kong Ingress, and I have completed the POC on the virtual machine.But when I migrated the business code to K8S, I found that it didn’t work properly.

On Virtual Machine

,"bot-detection","acme","request-termination"]}[root@dev-node1 ~]# curl http://localhost:8001/plugins/enabled | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   553  100   553    0     0   103k      0 --:--:-- --:--:-- --:--:-- 92166
{
  "enabled_plugins": [
    "grpc-web",
    "correlation-id",
    "pre-function",
    "cors",
    "rate-limiting",
    "verify",
    "loggly",
    "hmac-auth",
    "zipkin",
    "request-size-limiting",
    "azure-functions",
    "request-transformer",
    "oauth2",
    "response-transformer",
    "ip-restriction",
    "statsd",
    "jwt",
    "proxy-cache",
    "basic-auth",
    "key-auth",
    "regular_reference",          # The added plugin.
    "http-log",
    "injection",
    "syslog",
    "session",
    "datadog",
    "tcp-log",
    "prometheus",
    "post-function",
    "ldap-auth",
    "acl",
    "grpc-gateway",
    "file-log",
    "transformer",
    "udp-log",
    "response-ratelimiting",
    "aws-lambda",
    "bot-detection",
    "acme",
    "request-termination"
  ]
}

Through The Admin API of Kong, I can see that it has been enabled, I also see that in konga.

Because I need to use The Mode of Kong’s Capturing Groups, I made the following configuration in Kong.

And the corresponding plugin is configured.

By requesting the corresponding route, you can see that the plugin is working.

[root@dev-node1 ~]# curl http://localhost:8000/test_regular_reference/11/22
{"host":"localhost:8000","x-forwarded-host":"localhost","connection":"keep-alive","x-forwarded-proto":"http","x-forwarded-for":"127.0.0.1","user-agent":"curl\/7.29.0","accept":"*\/*","x-real-ip":"127.0.0.1","x-forwarded-port":"8000"}
nil
{}
nil
/test_v1/22/11

On Kubernetes

You can see from Kong’s administration API that the plugin has been enabled.

[root@k8s-master kong-install]# curl http://10.44.0.7:8001/plugins/enabled | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   541  100   541    0     0   120k      0 --:--:-- --:--:-- --:--:--  132k
{
  "enabled_plugins": [
    "grpc-web",
    "correlation-id",
    "pre-function",
    "cors",
    "rate-limiting",
    "verify",
    "loggly",
    "hmac-auth",
    "zipkin",
    "request-size-limiting",
    "azure-functions",
    "request-transformer",
    "oauth2",
    "response-transformer",
    "ip-restriction",
    "statsd",
    "jwt",
    "proxy-cache",
    "basic-auth",
    "key-auth",
    "http-log",
    "regular_reference",          # The plugin also enabled.
    "syslog",
    "session",
    "datadog",
    "tcp-log",
    "prometheus",
    "post-function",
    "ldap-auth",
    "acl",
    "grpc-gateway",
    "file-log",
    "transformer",
    "udp-log",
    "response-ratelimiting",
    "aws-lambda",
    "bot-detection",
    "acme",
    "request-termination"
  ]
}

Kong ingress

apiVersion: configuration.konghq.com/v1
kind: KongIngress
metadata:
    name: regular-reference
    namespace: demo
spec:
    route:
        methods: 
        - GET
        - POST
        strip_path: true
    proxy:
        path: /test_v1/(?<version>\d+)/(?<release>\d+)
        connect_timeout: 20
        read_timeout: 20
        write_timeout: 20

Ingress

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
    name: regular-reference
    namespace: demo
spec:
    rules:
    - http:
        paths:
        - path: /test_v1
          backend:
              serviceName: external-service
              servicePort: 7979

KongPlugin

apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
    name: kong-plugin-regular-reference
    namespace: demo
config:
    route_raw_path: /test_v1
    route_rep_maps: '{"version": 2, "release": 1}'

I have configed the plugin on the corresponding service.

[root@k8s-master kong-install]# kubectl get svc external-service -n demo -o yaml
apiVersion: v1
kind: Service
metadata:
  annotations:
    konghq.com/plugins: kong-plugin-regular-reference
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"external-service","namespace":"demo"},"spec":{"ports":[{"port":7979,"protocol":"TCP","targetPort":7979}]}}
  creationTimestamp: "2020-09-22T16:51:11Z"
  managedFields:
  - apiVersion: v1
    fieldsType: FieldsV1
    fieldsV1:
      f:metadata:
        f:annotations:
          .: {}
          f:kubectl.kubernetes.io/last-applied-configuration: {}
      f:spec:
        f:ports:
          .: {}
          k:{"port":7979,"protocol":"TCP"}:
            .: {}
            f:port: {}
            f:protocol: {}
            f:targetPort: {}
        f:sessionAffinity: {}
        f:type: {}
    manager: kubectl-client-side-apply
    operation: Update
    time: "2020-09-22T16:51:11Z"
  - apiVersion: v1
    fieldsType: FieldsV1
    fieldsV1:
      f:metadata:
        f:annotations:
          f:konghq.com/plugins: {}
    manager: kubectl-edit
    operation: Update
    time: "2020-10-10T08:36:52Z"
  name: external-service
  namespace: demo
  resourceVersion: "6288960"
  selfLink: /api/v1/namespaces/demo/services/external-service
  uid: 2cdba68f-ebf2-4967-ad86-33fdd8fcccea
spec:
  clusterIP: 10.1.202.181
  ports:
  - port: 7979
    protocol: TCP
    targetPort: 7979
  sessionAffinity: None
  type: ClusterIP
status:
  loadBalancer: {}

And I have cofiged the kong ingress in k8s Ingress.

[root@k8s-master kong-install]# kubectl get ingress regular-reference  -n demo -o yaml
Warning: extensions/v1beta1 Ingress is deprecated in v1.14+, unavailable in v1.22+; use networking.k8s.io/v1 Ingress
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    konghq.com/override: regular-reference
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"extensions/v1beta1","kind":"Ingress","metadata":{"annotations":{},"name":"regular-reference","namespace":"demo"},"spec":{"rules":[{"http":{"paths":[{"backend":{"serviceName":"external-service","servicePort":7979},"path":"/test_v1"}]}}]}}
  creationTimestamp: "2020-10-10T08:50:13Z"
  generation: 1
  managedFields:
  - apiVersion: extensions/v1beta1
    fieldsType: FieldsV1
    fieldsV1:
      f:metadata:
        f:annotations:
          .: {}
          f:kubectl.kubernetes.io/last-applied-configuration: {}
      f:spec:
        f:rules: {}
    manager: kubectl-client-side-apply
    operation: Update
    time: "2020-10-10T08:50:13Z"
  - apiVersion: extensions/v1beta1
    fieldsType: FieldsV1
    fieldsV1:
      f:metadata:
        f:annotations:
          f:konghq.com/override: {}
    manager: kubectl-edit
    operation: Update
    time: "2020-10-10T09:05:54Z"
  name: regular-reference
  namespace: demo
  resourceVersion: "6289477"
  selfLink: /apis/extensions/v1beta1/namespaces/demo/ingresses/regular-reference
  uid: 8b30ec16-7cae-4907-ac1f-abe08dd9fda8
spec:
  rules:
  - http:
      paths:
      - backend:
          serviceName: external-service
          servicePort: 7979
        path: /test_v1
        pathType: ImplementationSpecific
status:
  loadBalancer: {}

But the following error occurred when I was testing:

2020/10/10 09:04:45 [error] 22#0: *71405 lua coroutine: runtime error: /opt/kong/plugins/regular_reference/handler.lua:55: attempt to index field 'uri_captures' (a nil value)
stack traceback:
coroutine 0:
        /opt/kong/plugins/regular_reference/handler.lua: in function </opt/kong/plugins/regular_reference/handler.lua:23>
coroutine 1:
        [C]: in function 'resume'
        coroutine.wrap:21: in function <coroutine.wrap:21>
        /usr/local/share/lua/5.1/kong/init.lua:757: in function 'access'
        access_by_lua(nginx-kong.conf:91):2: in main chunk, client: 10.32.0.1, server: kong, request: "GET /test_v1/11/22 HTTP/1.1", host: "10.1.221.255"

Maybe the kong ingress don’t work. But i don’t know how to do?

That KongIngress path corresponds to the Kong service (upstream) path. Capture groups will come from the Ingress/Kong route path, i.e. spec.rules.http.paths.path in the Ingress resource.

You may run into issues there due to Kubernetes limitations, however: the K8S API Server validation for Ingress was overly-strict in the regular expression dialects it would accept. IIRC that limitation may have affected capture groups, but I’m unsure–try and see if you can place that expression in your Ingress rule path.

That limitation should now be fixed, but per https://github.com/kubernetes/kubernetes/issues/90210 it likely requires Ingress v1, or at least definitely requires Kubernetes 1.19.

Recent versions of the Kong Ingress Controller (0.10 and 1.0) do support Ingress v1, so if you have a Kubernetes 1.19 environment available (as of yet, it looks like the major hosted providers don’t have it available, though Azure AKS appears to now offer 1.19 in preview), you should be able to set an Ingress rule with capture groups.