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  }