Getting an error I don't understand with k8s

11/26/2019

My yaml file.

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-service
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
  rules:
    -http:
      paths:
        - path: /?(.*)
          backend:
            serviceName: nginx-service
            servicePort: 80

kubectl apply -f file.yaml

error: error validating "ingress.yaml": error validating data: ValidationError(Ingress.spec.rules): invalid type for io.k8s.api.networking.v1beta1.IngressSpec.rules: got "map", expected "array"; if you choose to ignore these errors, turn validation off with --validate=false

-- tink3r
kubernetes

2 Answers

11/26/2019

The issue is with typo in "-http" (no whitespace char between the hyphen and the 'h' letter).

spec:
  rules:
    -http:   <--- this doesn't create an array, and array is expected instead of map
      paths:

How to troubleshoot such issues (and what Kubernetes does "under_the_hood"):

  • yamllint . It is possible to put the yaml code to any online YAML syntax validator . Let's check the line_11. There is ? symbol that states that yamllint had to guess what is wrong with that line. Please note that yamllint still considers this YAML as Valid.

enter image description here

  • convert yaml to JSON. That's exactly what kubectl does before sending data to kube-apiserver. it is possible to do that with json2yaml tool for example. You'll immidiately see that the tool parses it as a map and not as an array.

Tha is how it converted to JSON with incorrect YAML. incorrect YAML

And with the corrected YAML it parsed as array correct YAML . <code>rules</code> contain an array

How it is supposed to work. As with all other Kubernetes resources, an Ingress needs apiVersion, kind, and metadata fields. Additionally, Ingress frequently uses annotations to configure some options depending on the Ingress controller, an example of which is the rewrite-target annotation. Different Ingress controllers support different annotations.

If anyone needs comprehensive info on rewrite-target, that can be found here

IngressRule represents the rules mapping the paths under a specified host to the related backend services. Incoming requests are first evaluated for a host match, then routed to the backend associated with the matching IngressRuleValue.

host have to be of a string type and contain fully qualified domain name (FQDN) of a network host. Current Limitations (client GitVersion:"v1.16.3", Server GitVersion:"v1.14.8-gke.12"):

  • IPs aren't allowed. IngressRuleValue can only apply to the IP in the Spec of the parent Ingress.
  • The : delimiter is not respected because ports are not allowed. As of now, the port of an Ingress is implicitly :80 for http and :443 for https.

Both these may change in the future.

Incoming requests are matched against the host before the IngressRuleValue. If the host is unspecified, the Ingress routes all traffic based on the specified IngressRuleValue.

So, the structure shall be as the following:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: dual-ingress
  annotations:
    <your-annotations-go-here>
spec:
  rules:
  - host: <your-hostname-1-goes-here>
    http:
      paths:
      - path: <host-1-path-1>
        backend:
          serviceName: <service1>
          servicePort: <service1-port>
      - path: <host-1-path-2>
        backend:
          serviceName: <service2>
          servicePort: <service2-port>
  - host: <your-hostname-2-goes-here>
    http:
      paths:
      - path: <host-2-path-1>
        backend:
          serviceName: <service3>
          servicePort: <service3-port>
      - path: <host-2-path-2>
        backend:
          serviceName: <service4>
          servicePort: <service4-port>

the above YAML creates the following Ingress :

kubectl get ingress 

NAME           HOSTS                                     ADDRESS   PORTS   AGE
dual-ingress   your-hostname-1,your-hostname-2,bar.com             80      14s

In case you don't specify host in YAML all traffic will be routed according to IngressRuleValue. YAML example:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: mono-ingress
  annotations:
    <your-annotations-go-here>
spec:
  rules:
     - http:
        paths:
          - path: <path-1>
            backend:
              serviceName: <service1>
              servicePort: <service1-port>
          - path: <path-2>
            backend:
              serviceName: <service2>
              servicePort: <service2-port>

The above YAML creates the following Ingress :

kubectl get ingress -o wide 
NAME           HOSTS                ADDRESS   PORTS   AGE
mono-ingress   *                              80      10s

So, to sum up, rules: field shall contain an array (and topic starter missed single space char between "-" and "http", so it wasn't parsed as an array, but map)

Hope that helps.

-- Nick
Source: StackOverflow

11/26/2019

this is yaml template error only, use

kubectl explain ingress.spec.rules

to get idea.

The hostname is also missing in the ingress rule, should have been something like

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-service
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
  rules:
    -host:{replace-me-with-hostname}
     http:
       paths:
        - path: /?(.*)
          backend:
            serviceName: nginx-service
            servicePort: 80 

-- Tushar Mahajan
Source: StackOverflow