http.HandleFunc not working

3/25/2018

I am working on an application that is hosted on Kubernetes on google cloud, it uses Ingress to manage the routing to each service, then inside each service there's some extra routing using golang.

My problem is: when I try sending an API request to /api/auth/healthz I am expecting to reach the statusHandler() function, instead I always hit the requestHandler() function, even though I have an http.HandleFunc in there: http.HandleFunc("/healthz", statusHandler)

Here's my code in go

func main() {
    flag.Parse()

    // initialize the "database"
    err := store.Initialise()
    if err != nil {
        glog.Error(err)
        glog.Error("Store failed to initialise")
    }

    defer store.Uninitialise()

    // Initialize the default TTL for JWT access tokens to 1 day
    tokenTTL = 24 * time.Hour
    glog.Infof("JWT access tokens time-to-live set to: %s", tokenTTL.String())

    http.HandleFunc("/healthz", statusHandler)
    http.HandleFunc("/", requestHandler)

    glog.Infof("Authentication service started on port 8080...\n")
    http.ListenAndServe(":8080", nil)
}

func statusHandler(w http.ResponseWriter, r *http.Request) {
    glog.Infof("Reached the status handler")
    statusObj := serviceStatus{
        GitSHA:         os.Getenv("GIT_SHA"),
        BuildTimestamp: os.Getenv("BUILD_TIMESTAMP"),
        DeployEnv:      os.Getenv("DEPLOY_ENV"),
    }
    statusJSON, _ := json.Marshal(statusObj)

    w.Header().Set("Content-Type", "application/json")
    fmt.Fprintf(w, "%s", statusJSON)
}

func requestHandler(w http.ResponseWriter, r *http.Request) {
    glog.Infof("Reached the request handler")
    ...
}

And here's my Ingress code:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  # https://github.com/kubernetes/ingress-nginx/blob/master/docs/user-guide/annotations.md
  annotations:
    kubernetes.io/ingress.class: nginx
    certmanager.k8s.io/cluster-issuer: letsencrypt-staging
    nginx.ingress.kubernetes.io/enable-cors: "false"
    nginx.ingress.kubernetes.io/cors-enable-origin: "*"
    nginx.ingress.kubernetes.io/auth-type: "basic"
    nginx.ingress.kubernetes.io/auth-secret: "ci-ingress-auth"
    nginx.ingress.kubernetes.io/proxy-body-size: "4g"
  name: ci-api-ingress
  namespace: ci
spec:
  rules:
  - host: ci.omitted.io
    http:
      paths:
      - backend:
          serviceName: auth-service << This is the service I am working on
          servicePort: 8080
        path: /api/auth
      - backend:
          serviceName: data-import-service
          servicePort: 8080
        path: /api/data-import
      - backend:
          serviceName: log-service
          servicePort: 8080
        path: /api/log
      - backend:
          serviceName: project-service
          servicePort: 8080
        path: /api/project
      - backend:
          serviceName: orchestration-service
          servicePort: 8080
        path: /api/orchestration
      - backend:
          serviceName: public
          servicePort: 80
        path: /

Based on Kubernetes' documentation that should work as what they have in there is something similar to my implementation:

http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
    duration := time.Now().Sub(started)
    if duration.Seconds() > 10 {
        w.WriteHeader(500)
        w.Write([]byte(fmt.Sprintf("error: %v", duration.Seconds())))
    } else {
        w.WriteHeader(200)
        w.Write([]byte("ok"))
    }
})
-- Naguib Ihab
go
kubernetes
kubernetes-ingress

1 Answer

3/25/2018

based on your ingress rules the path for requests should be: /api/auth/healthz not /api/auth-service/healthz

The ingress path /api/auth/ is kept in the request uri passed to the application server.

Adding rewrite-target to the ingress annotations will ensure the path is passed to your underlying server as you'd expect e.g. as /<path> rather than /api/auth/<path>. Inspecting the application server logs should show this is the case.

ingress.kubernetes.io/rewrite-target: /

-- stacksonstacks
Source: StackOverflow