github.com/nginxinc/kubernetes-ingress@v1.12.5/internal/metrics/listener.go (about) 1 package metrics 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "net/http" 7 "os" 8 "strconv" 9 10 "github.com/golang/glog" 11 "github.com/nginxinc/kubernetes-ingress/internal/nginx" 12 prometheusClient "github.com/nginxinc/nginx-prometheus-exporter/client" 13 nginxCollector "github.com/nginxinc/nginx-prometheus-exporter/collector" 14 "github.com/prometheus/client_golang/prometheus" 15 "github.com/prometheus/client_golang/prometheus/promhttp" 16 api_v1 "k8s.io/api/core/v1" 17 ) 18 19 // metricsEndpoint is the path where prometheus metrics will be exposed 20 const metricsEndpoint = "/metrics" 21 22 // NewNginxMetricsClient creates an NginxClient to fetch stats from NGINX over an unix socket 23 func NewNginxMetricsClient(httpClient *http.Client) (*prometheusClient.NginxClient, error) { 24 return prometheusClient.NewNginxClient(httpClient, "http://config-status/stub_status") 25 } 26 27 // RunPrometheusListenerForNginx runs an http server to expose Prometheus metrics for NGINX 28 func RunPrometheusListenerForNginx(port int, client *prometheusClient.NginxClient, registry *prometheus.Registry, constLabels map[string]string, prometheusSecret *api_v1.Secret) { 29 registry.MustRegister(nginxCollector.NewNginxCollector(client, "nginx_ingress_nginx", constLabels)) 30 runServer(strconv.Itoa(port), registry, prometheusSecret) 31 } 32 33 // RunPrometheusListenerForNginxPlus runs an http server to expose Prometheus metrics for NGINX Plus 34 func RunPrometheusListenerForNginxPlus(port int, nginxPlusCollector prometheus.Collector, registry *prometheus.Registry, prometheusSecret *api_v1.Secret) { 35 registry.MustRegister(nginxPlusCollector) 36 runServer(strconv.Itoa(port), registry, prometheusSecret) 37 } 38 39 func runServer(port string, registry prometheus.Gatherer, prometheusSecret *api_v1.Secret) { 40 http.Handle(metricsEndpoint, promhttp.HandlerFor(registry, promhttp.HandlerOpts{})) 41 42 http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 43 _, err := w.Write([]byte(`<html> 44 <head><title>NGINX Ingress Controller</title></head> 45 <body> 46 <h1>NGINX Ingress Controller</h1> 47 <p><a href='/metrics'>Metrics</a></p> 48 </body> 49 </html>`)) 50 if err != nil { 51 glog.Warningf("Error while sending a response for the '/' path: %v", err) 52 } 53 }) 54 address := fmt.Sprintf(":%v", port) 55 glog.Infof("Starting Prometheus listener on: %v%v", address, metricsEndpoint) 56 if prometheusSecret == nil { 57 glog.Fatal("Error in Prometheus listener server: ", http.ListenAndServe(address, nil)) 58 } else { 59 // Unfortunately, http.ListenAndServeTLS() takes a filename instead of cert/key data, so we 60 // Write the cert and key to a temporary file. We create a unique file name to prevent collisions. 61 certFileName := "nginx-prometheus.cert" 62 keyFileName := "nginx-prometheus.key" 63 certFile, err := writeTempFile(prometheusSecret.Data[api_v1.TLSCertKey], certFileName) 64 if err != nil { 65 glog.Fatal("failed to create cert file for prometheus: %w", err) 66 } 67 68 keyFile, err := writeTempFile(prometheusSecret.Data[api_v1.TLSPrivateKeyKey], keyFileName) 69 if err != nil { 70 glog.Fatal("failed to create key file for prometheus: %w", err) 71 } 72 73 glog.Fatal("Error in Prometheus listener server: ", http.ListenAndServeTLS(address, certFile.Name(), keyFile.Name(), nil)) 74 } 75 } 76 77 func writeTempFile(data []byte, name string) (*os.File, error) { 78 f, err := ioutil.TempFile("", name) 79 if err != nil { 80 return nil, fmt.Errorf("failed to create temp file: %w", err) 81 } 82 83 err = f.Chmod(nginx.TLSSecretFileMode) 84 if err != nil { 85 return nil, fmt.Errorf("couldn't change the mode of the temp file %v: %w", f.Name(), err) 86 } 87 88 _, err = f.Write(data) 89 if err != nil { 90 return f, fmt.Errorf("failed to write to temp file: %w", err) 91 } 92 93 return f, nil 94 }