|
1 | 1 | package cvo |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "context" |
| 5 | + "net" |
| 6 | + "net/http" |
4 | 7 | "time" |
5 | 8 |
|
6 | 9 | "github.com/prometheus/client_golang/prometheus" |
| 10 | + "github.com/prometheus/client_golang/prometheus/promhttp" |
7 | 11 | corev1 "k8s.io/api/core/v1" |
8 | 12 | apierrors "k8s.io/apimachinery/pkg/api/errors" |
9 | 13 | "k8s.io/apimachinery/pkg/labels" |
10 | 14 | "k8s.io/apimachinery/pkg/util/sets" |
11 | 15 | "k8s.io/client-go/tools/cache" |
| 16 | + "k8s.io/klog" |
12 | 17 |
|
13 | 18 | configv1 "github.com/openshift/api/config/v1" |
14 | 19 | "github.com/openshift/cluster-version-operator/lib/resourcemerge" |
15 | 20 | "github.com/openshift/cluster-version-operator/pkg/internal" |
16 | 21 | ) |
17 | 22 |
|
18 | | -func (optr *Operator) registerMetrics(coInformer cache.SharedInformer) error { |
| 23 | +// RegisterMetrics initializes metrics and registers them with the |
| 24 | +// Prometheus implementation. |
| 25 | +func (optr *Operator) RegisterMetrics(coInformer cache.SharedInformer) error { |
19 | 26 | m := newOperatorMetrics(optr) |
20 | 27 | coInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{ |
21 | 28 | UpdateFunc: m.clusterOperatorChanged, |
@@ -86,6 +93,67 @@ version for 'cluster', or empty for 'initial'. |
86 | 93 | } |
87 | 94 | } |
88 | 95 |
|
| 96 | +// RunMetrics launches an server bound to listenAddress serving |
| 97 | +// Prometheus metrics at /metrics over HTTP. Continues serving until |
| 98 | +// runContext.Done() and then attempts a clean shutdown limited by |
| 99 | +// shutdownContext.Done(). Assumes runContext.Done() occurs before or |
| 100 | +// simultaneously with shutdownContext.Done(). |
| 101 | +func RunMetrics(runContext context.Context, shutdownContext context.Context, listenAddress string) error { |
| 102 | + handler := http.NewServeMux() |
| 103 | + handler.Handle("/metrics", promhttp.Handler()) |
| 104 | + server := &http.Server{ |
| 105 | + Handler: handler, |
| 106 | + } |
| 107 | + |
| 108 | + errorChannel := make(chan error, 1) |
| 109 | + errorChannelCount := 1 |
| 110 | + go func() { |
| 111 | + tcpListener, err := net.Listen("tcp", listenAddress) |
| 112 | + if err != nil { |
| 113 | + errorChannel <- err |
| 114 | + return |
| 115 | + } |
| 116 | + |
| 117 | + klog.Infof("Metrics port listening for HTTP on %v", listenAddress) |
| 118 | + |
| 119 | + errorChannel <- server.Serve(tcpListener) |
| 120 | + }() |
| 121 | + |
| 122 | + shutdown := false |
| 123 | + var loopError error |
| 124 | + for errorChannelCount > 0 { |
| 125 | + if shutdown { |
| 126 | + err := <-errorChannel |
| 127 | + errorChannelCount-- |
| 128 | + if err != nil && err != http.ErrServerClosed { |
| 129 | + if loopError == nil { |
| 130 | + loopError = err |
| 131 | + } else if err != nil { // log the error we are discarding |
| 132 | + klog.Errorf("Failed to gracefully shut down metrics server: %s", err) |
| 133 | + } |
| 134 | + } |
| 135 | + } else { |
| 136 | + select { |
| 137 | + case <-runContext.Done(): // clean shutdown |
| 138 | + case err := <-errorChannel: // crashed before a shutdown was requested |
| 139 | + errorChannelCount-- |
| 140 | + if err != nil && err != http.ErrServerClosed { |
| 141 | + loopError = err |
| 142 | + } |
| 143 | + } |
| 144 | + shutdown = true |
| 145 | + shutdownError := server.Shutdown(shutdownContext) |
| 146 | + if loopError == nil { |
| 147 | + loopError = shutdownError |
| 148 | + } else if shutdownError != nil { // log the error we are discarding |
| 149 | + klog.Errorf("Failed to gracefully shut down metrics server: %s", shutdownError) |
| 150 | + } |
| 151 | + } |
| 152 | + } |
| 153 | + |
| 154 | + return loopError |
| 155 | +} |
| 156 | + |
89 | 157 | type conditionKey struct { |
90 | 158 | Name string |
91 | 159 | Type string |
|
0 commit comments