From 63c5e5f5c680562df7591fa0928307456c36cdec Mon Sep 17 00:00:00 2001 From: JoshVanL Date: Thu, 21 May 2020 10:47:00 +0100 Subject: [PATCH] Cleans up metrics pkg to not require locks, and split out resources into different files Signed-off-by: JoshVanL --- pkg/metrics/BUILD.bazel | 8 +- pkg/metrics/acme.go | 38 ++++++ pkg/metrics/certificates.go | 106 +++++++++++++++++ .../{metrics_test.go => certificates_test.go} | 0 pkg/metrics/metrics.go | 111 ------------------ 5 files changed, 150 insertions(+), 113 deletions(-) create mode 100644 pkg/metrics/acme.go create mode 100644 pkg/metrics/certificates.go rename pkg/metrics/{metrics_test.go => certificates_test.go} (100%) diff --git a/pkg/metrics/BUILD.bazel b/pkg/metrics/BUILD.bazel index c3657cefe..b0057124c 100644 --- a/pkg/metrics/BUILD.bazel +++ b/pkg/metrics/BUILD.bazel @@ -2,7 +2,11 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "go_default_library", - srcs = ["metrics.go"], + srcs = [ + "acme.go", + "certificates.go", + "metrics.go", + ], importpath = "github.com/jetstack/cert-manager/pkg/metrics", visibility = ["//visibility:public"], deps = [ @@ -33,7 +37,7 @@ filegroup( go_test( name = "go_default_test", - srcs = ["metrics_test.go"], + srcs = ["certificates_test.go"], embed = [":go_default_library"], deps = [ "//pkg/apis/certmanager/v1alpha2:go_default_library", diff --git a/pkg/metrics/acme.go b/pkg/metrics/acme.go new file mode 100644 index 000000000..ae4bf40ca --- /dev/null +++ b/pkg/metrics/acme.go @@ -0,0 +1,38 @@ +/* +Copyright 2020 The Jetstack cert-manager contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package metrics contains global structures related to metrics collection +// cert-manager exposes the following metrics: +// certificate_expiration_timestamp_seconds{name, namespace} +// certificate_ready_status{name, namespace, condition} +// acme_client_request_count{"scheme", "host", "path", "method", "status"} +// acme_client_request_duration_seconds{"scheme", "host", "path", "method", "status"} +// controller_sync_call_count{"controller"} +package metrics + +import ( + "time" +) + +// ObserveACMERequestDuration increases bucket counters for that ACME client duration. +func (m *Metrics) ObserveACMERequestDuration(duration time.Duration, labels ...string) { + m.acmeClientRequestDurationSeconds.WithLabelValues(labels...).Observe(duration.Seconds()) +} + +// IncrementACMERequestCount increases the acme client request counter. +func (m *Metrics) IncrementACMERequestCount(labels ...string) { + m.acmeClientRequestCount.WithLabelValues(labels...).Inc() +} diff --git a/pkg/metrics/certificates.go b/pkg/metrics/certificates.go new file mode 100644 index 000000000..ea77f84c6 --- /dev/null +++ b/pkg/metrics/certificates.go @@ -0,0 +1,106 @@ +/* +Copyright 2020 The Jetstack cert-manager contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package metrics contains global structures related to metrics collection +// cert-manager exposes the following metrics: +// certificate_expiration_timestamp_seconds{name, namespace} +// certificate_ready_status{name, namespace, condition} +// acme_client_request_count{"scheme", "host", "path", "method", "status"} +// acme_client_request_duration_seconds{"scheme", "host", "path", "method", "status"} +// controller_sync_call_count{"controller"} +package metrics + +import ( + "context" + + "github.com/prometheus/client_golang/prometheus" + "k8s.io/client-go/tools/cache" + + cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2" + cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1" + logf "github.com/jetstack/cert-manager/pkg/logs" +) + +// UpdateCertificate will update that Certificate metric with expiry and Ready +// condition. +func (m *Metrics) UpdateCertificate(ctx context.Context, crt *cmapi.Certificate) { + key, err := cache.MetaNamespaceKeyFunc(crt) + if err != nil { + log := logf.WithRelatedResource(m.log, crt) + log.Error(err, "failed to get key from certificate object") + return + } + + m.updateCertificateStatus(key, crt) + m.updateCertificateExpiry(ctx, key, crt) +} + +// updateCertificateExpiry updates the expiry time of a certificate +func (m *Metrics) updateCertificateExpiry(ctx context.Context, key string, crt *cmapi.Certificate) { + expiryTime := 0.0 + + if crt.Status.NotAfter != nil { + expiryTime = float64(crt.Status.NotAfter.Unix()) + } + + m.certificateExpiryTimeSeconds.With(prometheus.Labels{ + "name": crt.Name, + "namespace": crt.Namespace}).Set(expiryTime) +} + +// updateCertificateStatus will update the metric for that Certificate +func (m *Metrics) updateCertificateStatus(key string, crt *cmapi.Certificate) { + for _, c := range crt.Status.Conditions { + if c.Type == cmapi.CertificateConditionReady { + m.updateCertificateReadyStatus(crt, c.Status) + return + } + } + + // If no status condition set yet, set to Unknown + m.updateCertificateReadyStatus(crt, cmmeta.ConditionUnknown) +} + +func (m *Metrics) updateCertificateReadyStatus(crt *cmapi.Certificate, current cmmeta.ConditionStatus) { + for _, condition := range readyConditionStatuses { + value := 0.0 + + if current == condition { + value = 1.0 + } + + m.certificateReadyStatus.With(prometheus.Labels{ + "name": crt.Name, + "namespace": crt.Namespace, + "condition": string(condition), + }).Set(value) + } +} + +// RemoveCertificate will delete the Certificate metrics from continuing to be +// exposed. +func (m *Metrics) RemoveCertificate(key string) { + namespace, name, err := cache.SplitMetaNamespaceKey(key) + if err != nil { + m.log.Error(err, "failed to get namespace and name from key") + return + } + + m.certificateExpiryTimeSeconds.DeleteLabelValues(name, namespace) + for _, condition := range readyConditionStatuses { + m.certificateReadyStatus.DeleteLabelValues(name, namespace, string(condition)) + } +} diff --git a/pkg/metrics/metrics_test.go b/pkg/metrics/certificates_test.go similarity index 100% rename from pkg/metrics/metrics_test.go rename to pkg/metrics/certificates_test.go diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index 4ed757f26..e8977946b 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -26,18 +26,14 @@ package metrics import ( "context" "net/http" - "sync" "time" "github.com/go-logr/logr" "github.com/gorilla/mux" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" - "k8s.io/client-go/tools/cache" - cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2" cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1" - logf "github.com/jetstack/cert-manager/pkg/logs" ) const ( @@ -56,15 +52,11 @@ type Metrics struct { registry *prometheus.Registry server *http.Server - mux sync.Mutex - certificateExpiryTimeSeconds *prometheus.GaugeVec certificateReadyStatus *prometheus.GaugeVec acmeClientRequestDurationSeconds *prometheus.SummaryVec acmeClientRequestCount *prometheus.CounterVec controllerSyncCallCount *prometheus.CounterVec - - registeredCertificates map[string]struct{} } var readyConditionStatuses = [...]cmmeta.ConditionStatus{cmmeta.ConditionTrue, cmmeta.ConditionFalse, cmmeta.ConditionUnknown} @@ -138,8 +130,6 @@ func New(log logr.Logger, listenAddress string) *Metrics { Handler: router, }, - registeredCertificates: make(map[string]struct{}), - certificateExpiryTimeSeconds: certificateExpiryTimeSeconds, certificateReadyStatus: certificateReadyStatus, acmeClientRequestCount: acmeClientRequestCount, @@ -175,112 +165,11 @@ func (m *Metrics) Start(stopCh <-chan struct{}) { m.shutdown() } -// ObserveACMERequestDuration increases bucket counters for that ACME client duration. -func (m *Metrics) ObserveACMERequestDuration(duration time.Duration, labels ...string) { - m.acmeClientRequestDurationSeconds.WithLabelValues(labels...).Observe(duration.Seconds()) -} - -// IncrementACMERequestCount increases the acme client request counter. -func (m *Metrics) IncrementACMERequestCount(labels ...string) { - m.acmeClientRequestCount.WithLabelValues(labels...).Inc() -} - // IncrementSyncCallCount will increase the sync counter for that controller. func (m *Metrics) IncrementSyncCallCount(controllerName string) { m.controllerSyncCallCount.WithLabelValues(controllerName).Inc() } -// UpdateCertificate will update that Certificate metric with expiry and Ready -// condition. -func (m *Metrics) UpdateCertificate(ctx context.Context, crt *cmapi.Certificate) { - key, err := cache.MetaNamespaceKeyFunc(crt) - if err != nil { - log := logf.WithRelatedResource(m.log, crt) - log.Error(err, "failed to get key from certificate object") - return - } - - m.updateCertificateStatus(key, crt) - m.updateCertificateExpiry(ctx, key, crt) -} - -// updateCertificateExpiry updates the expiry time of a certificate -func (m *Metrics) updateCertificateExpiry(ctx context.Context, key string, crt *cmapi.Certificate) { - expiryTime := 0.0 - - if crt.Status.NotAfter != nil { - expiryTime = float64(crt.Status.NotAfter.Unix()) - } - - m.certificateExpiryTimeSeconds.With(prometheus.Labels{ - "name": crt.Name, - "namespace": crt.Namespace}).Set(expiryTime) - - m.mux.Lock() - m.registeredCertificates[key] = struct{}{} - m.mux.Unlock() -} - -// updateCertificateStatus will update the metric for that Certificate -func (m *Metrics) updateCertificateStatus(key string, crt *cmapi.Certificate) { - defer func() { - m.mux.Lock() - m.registeredCertificates[key] = struct{}{} - m.mux.Unlock() - }() - - for _, c := range crt.Status.Conditions { - if c.Type == cmapi.CertificateConditionReady { - m.updateCertificateReadyStatus(crt, c.Status) - return - } - } - - // If no status condition set yet, set to Unknown - m.updateCertificateReadyStatus(crt, cmmeta.ConditionUnknown) -} - -func (m *Metrics) updateCertificateReadyStatus(crt *cmapi.Certificate, current cmmeta.ConditionStatus) { - for _, condition := range readyConditionStatuses { - value := 0.0 - - if current == condition { - value = 1.0 - } - - m.certificateReadyStatus.With(prometheus.Labels{ - "name": crt.Name, - "namespace": crt.Namespace, - "condition": string(condition), - }).Set(value) - } -} - -// RemoveCertificate will delete the Certificate metrics from continuing to be -// exposed. -func (m *Metrics) RemoveCertificate(key string) { - m.mux.Lock() - defer m.mux.Unlock() - - namespace, name, err := cache.SplitMetaNamespaceKey(key) - if err != nil { - m.log.Error(err, "failed to get namespace and name from key") - return - } - - // If the certificate is not registered, exit early - if _, ok := m.registeredCertificates[key]; !ok { - return - } - - m.certificateExpiryTimeSeconds.DeleteLabelValues(name, namespace) - for _, condition := range readyConditionStatuses { - m.certificateReadyStatus.DeleteLabelValues(name, namespace, string(condition)) - } - - delete(m.registeredCertificates, key) -} - func (m *Metrics) shutdown() { m.log.Info("stopping Prometheus metrics server...")