github.com/fafucoder/cilium@v1.6.11/operator/api.go (about) 1 // Copyright 2019 Authors of Cilium 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package main 16 17 import ( 18 "context" 19 "fmt" 20 "net/http" 21 22 "github.com/cilium/cilium/pkg/k8s" 23 "github.com/cilium/cilium/pkg/kvstore" 24 ) 25 26 // startServer starts an api server listening on the given address. 27 func startServer(shutdownSignal <-chan struct{}, allSystemsGo <-chan struct{}, addrs ...string) { 28 http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { 29 select { 30 // only start serving the real health check once all systems all up and running 31 case <-allSystemsGo: 32 healthHandler(w, r) 33 default: 34 healthHandlerOK(w, r) 35 } 36 }) 37 38 errs := make(chan error, 1) 39 nServers := 0 40 41 // Since we are opening this on localhost only, we need to make sure 42 // we can open for both v4 and v6 localhost. In case the user is running 43 // v4-only or v6-only. 44 for _, addr := range addrs { 45 if addr == "" { 46 continue 47 } 48 nServers++ 49 srv := &http.Server{Addr: addr} 50 errCh := make(chan error, 1) 51 52 go func() { 53 err := srv.ListenAndServe() 54 if err != nil { 55 errCh <- err 56 errs <- err 57 } 58 }() 59 go func() { 60 select { 61 case <-shutdownSignal: 62 if err := srv.Shutdown(context.Background()); err != nil { 63 log.WithError(err).Error("apiserver shutdown") 64 } 65 case err := <-errCh: 66 log.Warnf("Unable to start status api: %s", err) 67 } 68 }() 69 log.Infof("Starting apiserver on address %s", addr) 70 } 71 72 for err := range errs { 73 nServers-- 74 if nServers == 0 { 75 log.Fatalf("Unable to start status api: %s", err) 76 } 77 } 78 } 79 80 func healthHandlerOK(w http.ResponseWriter, r *http.Request) { 81 w.WriteHeader(http.StatusOK) 82 if _, err := w.Write([]byte("ok")); err != nil { 83 log.WithError(err).Error("Failed to write liveness-probe response") 84 } 85 } 86 87 func healthHandler(w http.ResponseWriter, r *http.Request) { 88 statusCode := http.StatusOK 89 reply := "ok" 90 91 if err := checkStatus(); err != nil { 92 statusCode = http.StatusInternalServerError 93 reply = err.Error() 94 log.WithError(err).Warn("Health check status") 95 } 96 97 w.WriteHeader(statusCode) 98 if _, err := w.Write([]byte(reply)); err != nil { 99 log.WithError(err).Error("Failed to write liveness-probe response") 100 } 101 } 102 103 // checkStatus checks the connection status to the kvstore and 104 // k8s apiserver and returns an error if any of them is unhealthy 105 func checkStatus() error { 106 if kvstoreEnabled() { 107 if client := kvstore.Client(); client == nil { 108 return fmt.Errorf("kvstore client not configured") 109 } else if _, err := client.Status(); err != nil { 110 return err 111 } 112 } 113 114 if _, err := k8s.Client().Discovery().ServerVersion(); err != nil { 115 return err 116 } 117 118 return nil 119 }