diff --git a/cmd/app/app.go b/cmd/app/app.go index f5b2b0b2..a3309afb 100644 --- a/cmd/app/app.go +++ b/cmd/app/app.go @@ -23,16 +23,20 @@ import ( "crypto/x509" "encoding/pem" "fmt" + "time" + "github.com/cert-manager/cert-manager/pkg/server" "github.com/cert-manager/csi-lib/driver" "github.com/cert-manager/csi-lib/manager" "github.com/cert-manager/csi-lib/manager/util" "github.com/cert-manager/csi-lib/metadata" "github.com/cert-manager/csi-lib/storage" "github.com/spf13/cobra" + "golang.org/x/sync/errgroup" "k8s.io/utils/clock" "github.com/cert-manager/csi-driver/cmd/app/options" + "github.com/cert-manager/csi-driver/internal/metrics" "github.com/cert-manager/csi-driver/internal/version" csiapi "github.com/cert-manager/csi-driver/pkg/apis/v1alpha1" "github.com/cert-manager/csi-driver/pkg/filestore" @@ -96,18 +100,46 @@ func NewCommand(ctx context.Context) *cobra.Command { return fmt.Errorf("failed to setup driver: " + err.Error()) } - go func() { + const metricsListenAddress = ":8080" + // Start metrics server + metricsLn, err := server.Listen("tcp", metricsListenAddress) + if err != nil { + return fmt.Errorf("failed to listen on prometheus address %s: %v", metricsListenAddress, err) + } + metricsServer := metrics.NewServer(metricsLn) + + g, _ := errgroup.WithContext(ctx) + g.Go(func() error { <-ctx.Done() log.Info("shutting down driver", "context", ctx.Err()) d.Stop() - }() + return nil + }) - log.Info("running driver") - if err := d.Run(); err != nil { - return fmt.Errorf("failed running driver: " + err.Error()) - } + g.Go(func() error { + log.Info("running driver") + if err := d.Run(); err != nil { + return fmt.Errorf("failed running driver: " + err.Error()) + } + return nil + }) + + g.Go(func() error { + <-ctx.Done() + // allow a timeout for graceful shutdown + shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + // nolint: contextcheck + return metricsServer.Shutdown(shutdownCtx) + }) + + g.Go(func() error { + log.V(3).Info("starting metrics server", "address", metricsLn.Addr()) + return metricsServer.Serve(metricsLn) + }) - return nil + return g.Wait() }, } diff --git a/go.mod b/go.mod index 896b1cbf..f903df6b 100644 --- a/go.mod +++ b/go.mod @@ -8,9 +8,11 @@ require ( github.com/go-logr/logr v1.4.2 github.com/onsi/ginkgo/v2 v2.19.0 github.com/onsi/gomega v1.33.1 + github.com/prometheus/client_golang v1.18.0 github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.9.0 + golang.org/x/sync v0.7.0 k8s.io/api v0.30.1 k8s.io/apimachinery v0.30.1 k8s.io/cli-runtime v0.30.1 @@ -70,7 +72,6 @@ require ( github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.18.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.46.0 // indirect github.com/prometheus/procfs v0.15.0 // indirect @@ -81,7 +82,6 @@ require ( golang.org/x/crypto v0.23.0 // indirect golang.org/x/net v0.25.0 // indirect golang.org/x/oauth2 v0.20.0 // indirect - golang.org/x/sync v0.7.0 // indirect golang.org/x/sys v0.20.0 // indirect golang.org/x/term v0.20.0 // indirect golang.org/x/text v0.15.0 // indirect diff --git a/internal/metrics/metrics.go b/internal/metrics/metrics.go new file mode 100644 index 00000000..0ee35224 --- /dev/null +++ b/internal/metrics/metrics.go @@ -0,0 +1,37 @@ +package metrics + +import ( + "net" + "net/http" + "time" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/collectors" + "github.com/prometheus/client_golang/prometheus/promhttp" +) + +const ( + prometheusMetricsServerReadTimeout = 8 * time.Second + prometheusMetricsServerWriteTimeout = 8 * time.Second + prometheusMetricsServerMaxHeaderBytes = 1 << 20 // 1 MiB +) + +func NewServer(ln net.Listener) *http.Server { + registry := prometheus.NewRegistry() + registry.MustRegister( + collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}), + collectors.NewGoCollector(), + ) + + mux := http.NewServeMux() + mux.Handle("/metrics", promhttp.HandlerFor(registry, promhttp.HandlerOpts{})) + + server := &http.Server{ + Addr: ln.Addr().String(), + ReadTimeout: prometheusMetricsServerReadTimeout, + WriteTimeout: prometheusMetricsServerWriteTimeout, + MaxHeaderBytes: prometheusMetricsServerMaxHeaderBytes, + Handler: mux, + } + return server +}