How to patch a deployed Ingress resource on Kubernetes?

6/8/2018

I have the following Ingress resource:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: main-ingress
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
    nginx.ingress.kubernetes.io/force-ssl-redirect: "false"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "86400"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "86400"

spec:
  tls:
  - secretName: the-secret
    hosts:
      - sample.domain.com
      - sample2.domain.com
      - rabbit.domain.com
      - hub.domain.com
      - grafana.domain.com

  rules:

  - host: sample.domain.com
    http:
      paths:
      - path: /
        backend:
          serviceName: fe-srvc
          servicePort: 80
      - path: /api
        backend:
          serviceName: be-srvc
          servicePort: 80

  - host: sample2.domain.com
    http:
      paths:
      - path: /
        backend:
          serviceName: fe2-srvc
          servicePort: 80
      - path: /api
        backend:
          serviceName: be-srvc
          servicePort: 80

## The Extra Services ###
  - host: rabbit.domain.com
    http:
      paths:
      - path: /
        backend:
          serviceName: rabbitmq-srvc
          servicePort: 80

and I want to patch it after it is deployed.

So I use this, to try and replace the be-srvc value with some-srvc :

kubectl patch ing/main-ingress --patch '{ "spec" : { "rules": [{"http":{"paths":[ {"- path":"/"},{"backend":{"serviceName":"other-srvc"}},{"servicePort":"80"} ] }}]}}'

and I get this error:

The Ingress "main-ingress" is invalid:
* spec.rules[0].http.backend.serviceName: Required value
* spec.rules[0].http.backend.servicePort: Invalid value: 0: must be between 1 and 65535, inclusive

Any insight would be appreciated!

-- Kostas Demiris
kubectl
kubernetes
kubernetes-ingress

1 Answer

6/8/2018

Your patch has a number of problems; for example "- path" instead of "path" but also incorrect referencing of object levels. However, even if you fixed the mistakes this would not work as intended. Let's see why.

kubectl patch is a request for a strategic merge patch. When patching arrays, like the .spec.rules and .spec.rules.http.paths in this case, a strategic merge patch can use the defined patch type and merge patch merge key for the object to do The Right Thing. However, in case of the Ingress object no one bothered to define these. This means that any patch will overwrite the entire object; it will not be a nice merge that one is hoping for.

To accomplish the particular change referred to in the question you can do:

kubectl get ing/main-ingress -o json \ 
  | jq '(.spec.rules[].http.paths[].backend.serviceName | select(. == "be-srvc")) |= "some-srvc"' \
  | kubectl apply -f -

The above will change all occurrences of the be-srvc Service to some-srvc. Keep in mind that there is a short race condition here: if the Ingress is modified after kubectl get ran the change will fail with the error Operation cannot be fulfilled on ingresses.extensions "xx": the object has been modified; to handle that case you need implement a retry logic.

If the indexes are known in the arrays mentioned above you can accomplish the patch directly:

kubectl patch ing/main-ingress --type=json \
  -p='[{"op": "replace", "path": "/spec/rules/0/http/paths/1/backend/serviceName", "value":"some-srvc"}]'
kubectl patch ing/main-ingress --type=json \
  -p='[{"op": "replace", "path": "/spec/rules/1/http/paths/1/backend/serviceName", "value":"some-srvc"}]'

The two commands above will change the backends for sample.domain.com/api and sample2.domain.com/api to some-srvc.

The two commands can also be combined like this:

kubectl patch ing/main-ingress --type=json \
  -p='[{"op": "replace", "path": "/spec/rules/0/http/paths/1/backend/serviceName", "value":"some-srvc"}, {"op": "replace", "path": "/spec/rules/1/http/paths/1/backend/serviceName", "value":"some-srvc"}]'

This has the same effect and as an added bonus there is no race condition here; the patch guaranteed to be atomic.

-- Janos Lenart
Source: StackOverflow