mirror of
https://github.com/external-secrets/external-secrets.git
synced 2024-12-14 11:57:59 +00:00
Merge pull request #110 from external-secrets/metrics-first-iteration
Add first iteration of custom metrics
This commit is contained in:
commit
b8fda4387d
8 changed files with 223 additions and 31 deletions
5
docs/guides-metrics.md
Normal file
5
docs/guides-metrics.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
# Metrics
|
||||
|
||||
The External Secrets Operator exposes its Prometheus metrics in the `/metrics` path. To enable it, set the `prometheus.enabled` Helm flag to `true`.
|
||||
|
||||
The Operator has the metrics inherited from Kubebuilder plus some custom metrics with the `external_secret` prefix.
|
5
go.mod
5
go.mod
|
@ -53,14 +53,13 @@ require (
|
|||
github.com/onsi/ginkgo v1.16.1
|
||||
github.com/onsi/gomega v1.11.0
|
||||
github.com/pierrec/lz4 v2.5.2+incompatible // indirect
|
||||
github.com/prometheus/client_golang v1.9.0 // indirect
|
||||
github.com/prometheus/procfs v0.4.0 // indirect
|
||||
github.com/prometheus/client_golang v1.10.0
|
||||
github.com/prometheus/client_model v0.2.0
|
||||
github.com/stretchr/testify v1.6.1
|
||||
github.com/tidwall/gjson v1.7.4
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20210201163806-010130855d6c // indirect
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c // indirect
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect
|
||||
golang.org/x/text v0.3.5 // indirect
|
||||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect
|
||||
|
|
19
go.sum
19
go.sum
|
@ -497,8 +497,8 @@ github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDf
|
|||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_golang v1.9.0 h1:Rrch9mh17XcxvEu9D9DEpb4isxjGBtcevQjKvxPRQIU=
|
||||
github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66IdsO+O441Eve7ptJDU=
|
||||
github.com/prometheus/client_golang v1.10.0 h1:/o0BDeWzLWXNZ+4q5gXltUvaMpJqckTa+jTNoB+z4cg=
|
||||
github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
|
@ -513,8 +513,8 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
|
|||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.15.0 h1:4fgOnadei3EZvgRwxJ7RMpG1k1pOZth5Pc13tyspaKM=
|
||||
github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
|
||||
github.com/prometheus/common v0.18.0 h1:WCVKW7aL6LEe1uryfI9dnEc2ZqNB1Fn0ok930v0iL1Y=
|
||||
github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
|
@ -523,13 +523,15 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT
|
|||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.4.0 h1:2XVTOS3f4uP3n077heNHPNQ0a9LHAO7uPZgDFZP77sg=
|
||||
github.com/prometheus/procfs v0.4.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
|
@ -537,6 +539,7 @@ github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkB
|
|||
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
|
@ -727,6 +730,7 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
@ -779,10 +783,11 @@ golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 h1:46ULzRKLh1CwgRq2dC5SlBzEqqNCi8rreOZnNrbqcIY=
|
||||
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M=
|
||||
|
|
|
@ -28,6 +28,7 @@ nav:
|
|||
- Getting started: guides-getting-started.md
|
||||
- Advanced Templating: guides-templating.md
|
||||
- Multi Tenancy: guides-multi-tenancy.md
|
||||
- Metrics: guides-metrics.md
|
||||
- Provider:
|
||||
- AWS:
|
||||
- Secrets Manager: provider-aws-secrets-manager.md
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
@ -52,11 +53,14 @@ type Reconciler struct {
|
|||
func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
log := r.Log.WithValues("ExternalSecret", req.NamespacedName)
|
||||
|
||||
syncCallsMetricLabels := prometheus.Labels{"name": req.Name, "namespace": req.Namespace}
|
||||
|
||||
var externalSecret esv1alpha1.ExternalSecret
|
||||
|
||||
err := r.Get(ctx, req.NamespacedName, &externalSecret)
|
||||
if err != nil {
|
||||
log.Error(err, "could not get ExternalSecret")
|
||||
syncCallsError.With(syncCallsMetricLabels).Inc()
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
|
||||
|
@ -71,8 +75,10 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
|||
if err != nil {
|
||||
log.Error(err, "could not get store reference")
|
||||
conditionSynced := NewExternalSecretCondition(esv1alpha1.ExternalSecretReady, corev1.ConditionFalse, esv1alpha1.ConditionReasonSecretSyncedError, err.Error())
|
||||
SetExternalSecretCondition(&externalSecret.Status, *conditionSynced)
|
||||
SetExternalSecretCondition(&externalSecret, *conditionSynced)
|
||||
|
||||
err = r.Status().Update(ctx, &externalSecret)
|
||||
syncCallsError.With(syncCallsMetricLabels).Inc()
|
||||
return ctrl.Result{RequeueAfter: requeueAfter}, nil
|
||||
}
|
||||
|
||||
|
@ -87,6 +93,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
|||
storeProvider, err := schema.GetProvider(store)
|
||||
if err != nil {
|
||||
log.Error(err, "could not get store provider")
|
||||
syncCallsError.With(syncCallsMetricLabels).Inc()
|
||||
return ctrl.Result{RequeueAfter: requeueAfter}, nil
|
||||
}
|
||||
|
||||
|
@ -94,8 +101,9 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
|||
if err != nil {
|
||||
log.Error(err, "could not get provider client")
|
||||
conditionSynced := NewExternalSecretCondition(esv1alpha1.ExternalSecretReady, corev1.ConditionFalse, esv1alpha1.ConditionReasonSecretSyncedError, err.Error())
|
||||
SetExternalSecretCondition(&externalSecret.Status, *conditionSynced)
|
||||
SetExternalSecretCondition(&externalSecret, *conditionSynced)
|
||||
err = r.Status().Update(ctx, &externalSecret)
|
||||
syncCallsError.With(syncCallsMetricLabels).Inc()
|
||||
return ctrl.Result{RequeueAfter: requeueAfter}, nil
|
||||
}
|
||||
|
||||
|
@ -119,11 +127,12 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
|||
if err != nil {
|
||||
log.Error(err, "could not reconcile ExternalSecret")
|
||||
conditionSynced := NewExternalSecretCondition(esv1alpha1.ExternalSecretReady, corev1.ConditionFalse, esv1alpha1.ConditionReasonSecretSyncedError, err.Error())
|
||||
SetExternalSecretCondition(&externalSecret.Status, *conditionSynced)
|
||||
SetExternalSecretCondition(&externalSecret, *conditionSynced)
|
||||
err = r.Status().Update(ctx, &externalSecret)
|
||||
if err != nil {
|
||||
log.Error(err, "unable to update status")
|
||||
}
|
||||
syncCallsError.With(syncCallsMetricLabels).Inc()
|
||||
return ctrl.Result{RequeueAfter: requeueAfter}, nil
|
||||
}
|
||||
|
||||
|
@ -133,13 +142,15 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
|||
}
|
||||
|
||||
conditionSynced := NewExternalSecretCondition(esv1alpha1.ExternalSecretReady, corev1.ConditionTrue, esv1alpha1.ConditionReasonSecretSynced, "Secret was synced")
|
||||
SetExternalSecretCondition(&externalSecret.Status, *conditionSynced)
|
||||
SetExternalSecretCondition(&externalSecret, *conditionSynced)
|
||||
externalSecret.Status.RefreshTime = metav1.NewTime(time.Now())
|
||||
err = r.Status().Update(ctx, &externalSecret)
|
||||
if err != nil {
|
||||
log.Error(err, "unable to update status")
|
||||
}
|
||||
|
||||
syncCallsTotal.With(syncCallsMetricLabels).Inc()
|
||||
|
||||
return ctrl.Result{
|
||||
RequeueAfter: dur,
|
||||
}, nil
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
@ -32,15 +33,18 @@ import (
|
|||
"github.com/external-secrets/external-secrets/pkg/provider/schema"
|
||||
)
|
||||
|
||||
var fakeProvider *fake.Client
|
||||
var (
|
||||
fakeProvider *fake.Client
|
||||
metric dto.Metric
|
||||
timeout = time.Second * 5
|
||||
interval = time.Millisecond * 250
|
||||
)
|
||||
|
||||
var _ = Describe("ExternalSecret controller", func() {
|
||||
const (
|
||||
ExternalSecretName = "test-es"
|
||||
ExternalSecretStore = "test-store"
|
||||
ExternalSecretTargetSecretName = "test-secret"
|
||||
timeout = time.Second * 5
|
||||
interval = time.Millisecond * 250
|
||||
)
|
||||
|
||||
var ExternalSecretNamespace string
|
||||
|
@ -63,7 +67,12 @@ var _ = Describe("ExternalSecret controller", func() {
|
|||
},
|
||||
})).To(Succeed())
|
||||
|
||||
metric.Reset()
|
||||
syncCallsTotal.Reset()
|
||||
syncCallsError.Reset()
|
||||
externalSecretCondition.Reset()
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
Expect(k8sClient.Delete(context.Background(), &v1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
|
@ -78,9 +87,8 @@ var _ = Describe("ExternalSecret controller", func() {
|
|||
}, client.PropagationPolicy(metav1.DeletePropagationBackground)), client.GracePeriodSeconds(0)).To(Succeed())
|
||||
})
|
||||
|
||||
Context("When updating ExternalSecret Status", func() {
|
||||
Context("When creating an ExternalSecret", func() {
|
||||
It("should set the condition eventually", func() {
|
||||
By("creating an ExternalSecret")
|
||||
ctx := context.Background()
|
||||
es := &esv1alpha1.ExternalSecret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
|
@ -110,13 +118,66 @@ var _ = Describe("ExternalSecret controller", func() {
|
|||
}
|
||||
return true
|
||||
}, timeout, interval).Should(BeTrue())
|
||||
})
|
||||
|
||||
Expect(externalSecretConditionShouldBe(ExternalSecretName, ExternalSecretNamespace, esv1alpha1.ExternalSecretReady, v1.ConditionFalse, 0.0)).To(BeTrue())
|
||||
Expect(externalSecretConditionShouldBe(ExternalSecretName, ExternalSecretNamespace, esv1alpha1.ExternalSecretReady, v1.ConditionTrue, 1.0)).To(BeTrue())
|
||||
|
||||
Eventually(func() float64 {
|
||||
Expect(syncCallsTotal.WithLabelValues(ExternalSecretName, ExternalSecretNamespace).Write(&metric)).To(Succeed())
|
||||
return metric.GetCounter().GetValue()
|
||||
}, timeout, interval).Should(Equal(2.0))
|
||||
})
|
||||
})
|
||||
|
||||
Context("When updating an ExternalSecret", func() {
|
||||
It("should increment the syncCallsTotal metric", func() {
|
||||
ctx := context.Background()
|
||||
es := &esv1alpha1.ExternalSecret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: ExternalSecretName,
|
||||
Namespace: ExternalSecretNamespace,
|
||||
},
|
||||
Spec: esv1alpha1.ExternalSecretSpec{
|
||||
SecretStoreRef: esv1alpha1.SecretStoreRef{
|
||||
Name: ExternalSecretStore,
|
||||
},
|
||||
Target: esv1alpha1.ExternalSecretTarget{
|
||||
Name: ExternalSecretTargetSecretName,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Expect(k8sClient.Create(ctx, es)).Should(Succeed())
|
||||
|
||||
createdES := &esv1alpha1.ExternalSecret{}
|
||||
|
||||
Eventually(func() error {
|
||||
esLookupKey := types.NamespacedName{Name: ExternalSecretName, Namespace: ExternalSecretNamespace}
|
||||
|
||||
err := k8sClient.Get(ctx, esLookupKey, createdES)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
createdES.Spec.RefreshInterval = &metav1.Duration{Duration: 10 * time.Second}
|
||||
|
||||
err = k8sClient.Update(ctx, createdES)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}, timeout, interval).Should(Succeed())
|
||||
|
||||
Eventually(func() float64 {
|
||||
Expect(syncCallsTotal.WithLabelValues(ExternalSecretName, ExternalSecretNamespace).Write(&metric)).To(Succeed())
|
||||
return metric.GetCounter().GetValue()
|
||||
}, timeout, interval).Should(Equal(3.0))
|
||||
})
|
||||
})
|
||||
|
||||
Context("When syncing ExternalSecret value", func() {
|
||||
It("should set the secret value and sync labels/annotations", func() {
|
||||
By("creating an ExternalSecret")
|
||||
ctx := context.Background()
|
||||
const targetProp = "targetProperty"
|
||||
const secretVal = "someValue"
|
||||
|
@ -171,7 +232,6 @@ var _ = Describe("ExternalSecret controller", func() {
|
|||
})
|
||||
|
||||
It("should refresh secret value", func() {
|
||||
By("creating an ExternalSecret")
|
||||
ctx := context.Background()
|
||||
const targetProp = "targetProperty"
|
||||
const secretVal = "someValue"
|
||||
|
@ -227,7 +287,6 @@ var _ = Describe("ExternalSecret controller", func() {
|
|||
})
|
||||
|
||||
It("should fetch secrets using dataFrom", func() {
|
||||
By("creating an ExternalSecret")
|
||||
ctx := context.Background()
|
||||
const secretVal = "someValue"
|
||||
es := &esv1alpha1.ExternalSecret{
|
||||
|
@ -272,7 +331,6 @@ var _ = Describe("ExternalSecret controller", func() {
|
|||
})
|
||||
|
||||
It("should set an error condition when provider errors", func() {
|
||||
By("creating an ExternalSecret")
|
||||
ctx := context.Background()
|
||||
const targetProp = "targetProperty"
|
||||
es := &esv1alpha1.ExternalSecret{
|
||||
|
@ -317,10 +375,17 @@ var _ = Describe("ExternalSecret controller", func() {
|
|||
}
|
||||
return true
|
||||
}, timeout, interval).Should(BeTrue())
|
||||
|
||||
Eventually(func() float64 {
|
||||
Expect(syncCallsError.WithLabelValues(ExternalSecretName, ExternalSecretNamespace).Write(&metric)).To(Succeed())
|
||||
return metric.GetCounter().GetValue()
|
||||
}, timeout, interval).Should(Equal(2.0))
|
||||
|
||||
Expect(externalSecretConditionShouldBe(ExternalSecretName, ExternalSecretNamespace, esv1alpha1.ExternalSecretReady, v1.ConditionFalse, 1.0)).To(BeTrue())
|
||||
Expect(externalSecretConditionShouldBe(ExternalSecretName, ExternalSecretNamespace, esv1alpha1.ExternalSecretReady, v1.ConditionTrue, 0.0)).To(BeTrue())
|
||||
})
|
||||
|
||||
It("should set an error condition when store does not exist", func() {
|
||||
By("creating an ExternalSecret")
|
||||
ctx := context.Background()
|
||||
const targetProp = "targetProperty"
|
||||
es := &esv1alpha1.ExternalSecret{
|
||||
|
@ -364,10 +429,17 @@ var _ = Describe("ExternalSecret controller", func() {
|
|||
}
|
||||
return true
|
||||
}, timeout, interval).Should(BeTrue())
|
||||
|
||||
Eventually(func() float64 {
|
||||
Expect(syncCallsError.WithLabelValues(ExternalSecretName, ExternalSecretNamespace).Write(&metric)).To(Succeed())
|
||||
return metric.GetCounter().GetValue()
|
||||
}, timeout, interval).Should(Equal(2.0))
|
||||
|
||||
Expect(externalSecretConditionShouldBe(ExternalSecretName, ExternalSecretNamespace, esv1alpha1.ExternalSecretReady, v1.ConditionFalse, 1.0)).To(BeTrue())
|
||||
Expect(externalSecretConditionShouldBe(ExternalSecretName, ExternalSecretNamespace, esv1alpha1.ExternalSecretReady, v1.ConditionTrue, 0.0)).To(BeTrue())
|
||||
})
|
||||
|
||||
It("should set an error condition when store provider constructor fails", func() {
|
||||
By("creating an ExternalSecret")
|
||||
ctx := context.Background()
|
||||
const targetProp = "targetProperty"
|
||||
es := &esv1alpha1.ExternalSecret{
|
||||
|
@ -415,10 +487,17 @@ var _ = Describe("ExternalSecret controller", func() {
|
|||
}
|
||||
return true
|
||||
}, timeout, interval).Should(BeTrue())
|
||||
|
||||
Eventually(func() float64 {
|
||||
Expect(syncCallsError.WithLabelValues(ExternalSecretName, ExternalSecretNamespace).Write(&metric)).To(Succeed())
|
||||
return metric.GetCounter().GetValue()
|
||||
}, timeout, interval).Should(Equal(2.0))
|
||||
|
||||
Expect(externalSecretConditionShouldBe(ExternalSecretName, ExternalSecretNamespace, esv1alpha1.ExternalSecretReady, v1.ConditionFalse, 1.0)).To(BeTrue())
|
||||
Expect(externalSecretConditionShouldBe(ExternalSecretName, ExternalSecretNamespace, esv1alpha1.ExternalSecretReady, v1.ConditionTrue, 0.0)).To(BeTrue())
|
||||
})
|
||||
|
||||
It("should not process stores with mismatching controller field", func() {
|
||||
By("creating an ExternalSecret")
|
||||
ctx := context.Background()
|
||||
storeName := "example-ts-foo"
|
||||
Expect(k8sClient.Create(context.Background(), &esv1alpha1.SecretStore{
|
||||
|
@ -483,8 +562,21 @@ var _ = Describe("ExternalSecret controller", func() {
|
|||
cond := GetExternalSecretCondition(createdES.Status, esv1alpha1.ExternalSecretReady)
|
||||
return cond == nil
|
||||
}, timeout, interval).Should(BeTrue())
|
||||
})
|
||||
|
||||
// Condition True and False should be 0, since the Condition was not created
|
||||
Eventually(func() float64 {
|
||||
Expect(externalSecretCondition.WithLabelValues(ExternalSecretName, ExternalSecretNamespace, string(esv1alpha1.ExternalSecretReady), string(v1.ConditionTrue)).Write(&metric)).To(Succeed())
|
||||
return metric.GetGauge().GetValue()
|
||||
}, timeout, interval).Should(Equal(0.0))
|
||||
|
||||
Eventually(func() float64 {
|
||||
Expect(externalSecretCondition.WithLabelValues(ExternalSecretName, ExternalSecretNamespace, string(esv1alpha1.ExternalSecretReady), string(v1.ConditionFalse)).Write(&metric)).To(Succeed())
|
||||
return metric.GetGauge().GetValue()
|
||||
}, timeout, interval).Should(Equal(0.0))
|
||||
|
||||
Expect(externalSecretConditionShouldBe(ExternalSecretName, ExternalSecretNamespace, esv1alpha1.ExternalSecretReady, v1.ConditionFalse, 0.0)).To(BeTrue())
|
||||
Expect(externalSecretConditionShouldBe(ExternalSecretName, ExternalSecretNamespace, esv1alpha1.ExternalSecretReady, v1.ConditionTrue, 0.0)).To(BeTrue())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -510,6 +602,13 @@ func CreateNamespace(baseName string, c client.Client) (string, error) {
|
|||
return ns.Name, nil
|
||||
}
|
||||
|
||||
func externalSecretConditionShouldBe(name, ns string, ct esv1alpha1.ExternalSecretConditionType, cs v1.ConditionStatus, v float64) bool {
|
||||
return Eventually(func() float64 {
|
||||
Expect(externalSecretCondition.WithLabelValues(name, ns, string(ct), string(cs)).Write(&metric)).To(Succeed())
|
||||
return metric.GetGauge().GetValue()
|
||||
}, timeout, interval).Should(Equal(v))
|
||||
}
|
||||
|
||||
func init() {
|
||||
fakeProvider = fake.New()
|
||||
schema.ForceRegister(fakeProvider, &esv1alpha1.SecretStoreProvider{
|
||||
|
|
62
pkg/controllers/externalsecret/metrics.go
Normal file
62
pkg/controllers/externalsecret/metrics.go
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
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 externalsecret
|
||||
|
||||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"sigs.k8s.io/controller-runtime/pkg/metrics"
|
||||
|
||||
esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
|
||||
)
|
||||
|
||||
const (
|
||||
ExternalSecretSubsystem = "externalsecret"
|
||||
SyncCallsKey = "sync_calls_total"
|
||||
SyncCallsErrorKey = "sync_calls_error"
|
||||
externalSecretStatusConditionKey = "status_condition"
|
||||
)
|
||||
|
||||
var (
|
||||
syncCallsTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||
Subsystem: ExternalSecretSubsystem,
|
||||
Name: SyncCallsKey,
|
||||
Help: "Total number of the External Secret sync calls",
|
||||
}, []string{"name", "namespace"})
|
||||
|
||||
syncCallsError = prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||
Subsystem: ExternalSecretSubsystem,
|
||||
Name: SyncCallsErrorKey,
|
||||
Help: "Total number of the External Secret sync errors",
|
||||
}, []string{"name", "namespace"})
|
||||
|
||||
externalSecretCondition = prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Subsystem: ExternalSecretSubsystem,
|
||||
Name: externalSecretStatusConditionKey,
|
||||
Help: "The status condition of a specific External Secret",
|
||||
}, []string{"name", "namespace", "condition", "status"})
|
||||
)
|
||||
|
||||
func updateExternalSecretCondition(es *esv1alpha1.ExternalSecret, condition *esv1alpha1.ExternalSecretStatusCondition, value float64) {
|
||||
externalSecretCondition.With(prometheus.Labels{
|
||||
"name": es.Name,
|
||||
"namespace": es.Namespace,
|
||||
"condition": string(condition.Type),
|
||||
"status": string(condition.Status),
|
||||
}).Set(value)
|
||||
}
|
||||
|
||||
func init() {
|
||||
metrics.Registry.MustRegister(syncCallsTotal, syncCallsError, externalSecretCondition)
|
||||
}
|
|
@ -43,16 +43,26 @@ func GetExternalSecretCondition(status esv1alpha1.ExternalSecretStatus, condType
|
|||
|
||||
// SetExternalSecretCondition updates the external secret to include the provided
|
||||
// condition.
|
||||
func SetExternalSecretCondition(status *esv1alpha1.ExternalSecretStatus, condition esv1alpha1.ExternalSecretStatusCondition) {
|
||||
currentCond := GetExternalSecretCondition(*status, condition.Type)
|
||||
func SetExternalSecretCondition(es *esv1alpha1.ExternalSecret, condition esv1alpha1.ExternalSecretStatusCondition) {
|
||||
currentCond := GetExternalSecretCondition(es.Status, condition.Type)
|
||||
|
||||
if currentCond != nil && currentCond.Status == condition.Status && currentCond.Reason == condition.Reason {
|
||||
updateExternalSecretCondition(es, &condition, 1.0)
|
||||
return
|
||||
}
|
||||
|
||||
// Do not update lastTransitionTime if the status of the condition doesn't change.
|
||||
if currentCond != nil && currentCond.Status == condition.Status {
|
||||
condition.LastTransitionTime = currentCond.LastTransitionTime
|
||||
}
|
||||
status.Conditions = append(filterOutCondition(status.Conditions, condition.Type), condition)
|
||||
|
||||
es.Status.Conditions = append(filterOutCondition(es.Status.Conditions, condition.Type), condition)
|
||||
|
||||
if currentCond != nil {
|
||||
updateExternalSecretCondition(es, currentCond, 0.0)
|
||||
}
|
||||
|
||||
updateExternalSecretCondition(es, &condition, 1.0)
|
||||
}
|
||||
|
||||
func filterOutCondition(conditions []esv1alpha1.ExternalSecretStatusCondition, condType esv1alpha1.ExternalSecretConditionType) []esv1alpha1.ExternalSecretStatusCondition {
|
||||
|
|
Loading…
Reference in a new issue