Skip to content

Commit 193496c

Browse files
committed
feat(metrics): Emit metrics for CatalogSource state
This PR introduces a prometheus gauage vector `catalogsource_ready` that is emitted by each CatalogSource to indicate the connectivity.State of the CatalogSource object. A value of 1 for the vector indicates that the CatalogSource object is in a READY state, while a value of 0 indicates that the CatalogSource object is in one of IDLE/CONNECTING/ TRANSIENT_FAILURE/SHUTDOWN state. If/When the CatalogSource object eventually reaches a READY state, the value for the vector is set to 0. Signed-off-by: Anik Bhattacharjee <[email protected]>
1 parent 1e0b2ba commit 193496c

File tree

5 files changed

+161
-1
lines changed

5 files changed

+161
-1
lines changed

pkg/controller/operators/catalog/operator.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,7 @@ func (o *Operator) syncSourceState(state grpc.SourceState) {
396396
o.sourcesLastUpdate.Set(o.now().Time)
397397

398398
o.logger.Infof("state.Key.Namespace=%s state.Key.Name=%s state.State=%s", state.Key.Namespace, state.Key.Name, state.State.String())
399+
metrics.RegisterCatalogSourceState(state.Key.Name, state.Key.Namespace, state.State)
399400

400401
switch state.State {
401402
case connectivity.Ready:
@@ -513,6 +514,8 @@ func (o *Operator) handleCatSrcDeletion(obj interface{}) {
513514
o.logger.WithError(err).Warn("error closing client")
514515
}
515516
o.logger.WithField("source", sourceKey).Info("removed client for deleted catalogsource")
517+
518+
metrics.DeleteCatalogSourceStateMetric(catsrc.GetName(), catsrc.GetNamespace())
516519
}
517520

518521
func validateSourceType(logger *logrus.Entry, in *v1alpha1.CatalogSource) (out *v1alpha1.CatalogSource, continueSync bool, _ error) {

pkg/metrics/metrics.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"time"
55

66
"github.com/prometheus/client_golang/prometheus"
7+
"google.golang.org/grpc/connectivity"
78
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
89
"k8s.io/apimachinery/pkg/labels"
910

@@ -141,6 +142,14 @@ var (
141142
},
142143
)
143144

145+
catalogSourceReady = prometheus.NewGaugeVec(
146+
prometheus.GaugeOpts{
147+
Name: "catalogSource_ready",
148+
Help: "State of a CatalogSource. 1 indicates that the CatalogSource is in a READY state. 0 indicates CatalogSource is in a Non READY state.",
149+
},
150+
[]string{NAMESPACE_LABEL, NAME_LABEL},
151+
)
152+
144153
// exported since it's not handled by HandleMetrics
145154
CSVUpgradeCount = prometheus.NewCounter(
146155
prometheus.CounterOpts{
@@ -206,6 +215,7 @@ func RegisterCatalog() {
206215
prometheus.MustRegister(installPlanCount)
207216
prometheus.MustRegister(subscriptionCount)
208217
prometheus.MustRegister(catalogSourceCount)
218+
prometheus.MustRegister(catalogSourceReady)
209219
prometheus.MustRegister(SubscriptionSyncCount)
210220
prometheus.MustRegister(dependencyResolutionSummary)
211221
}
@@ -214,6 +224,19 @@ func CounterForSubscription(name, installedCSV, channelName, packageName, planAp
214224
return SubscriptionSyncCount.WithLabelValues(name, installedCSV, channelName, packageName, planApprovalStrategy)
215225
}
216226

227+
func RegisterCatalogSourceState(name, namespace string, state connectivity.State) {
228+
switch state {
229+
case connectivity.Ready:
230+
catalogSourceReady.WithLabelValues(namespace, name).Set(1)
231+
default:
232+
catalogSourceReady.WithLabelValues(namespace, name).Set(0)
233+
}
234+
}
235+
236+
func DeleteCatalogSourceStateMetric(name, namespace string) {
237+
catalogSourceReady.DeleteLabelValues(namespace, name)
238+
}
239+
217240
func DeleteCSVMetric(oldCSV *olmv1alpha1.ClusterServiceVersion) {
218241
// Delete the old CSV metrics
219242
csvAbnormal.DeleteLabelValues(oldCSV.Namespace, oldCSV.Name, oldCSV.Spec.Version.String(), string(oldCSV.Status.Phase), string(oldCSV.Status.Reason))

test/e2e/like_metric_matcher_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ func WithName(name string) MetricPredicate {
5151
return WithLabel("name", name)
5252
}
5353

54+
func WithNamespace(namespace string) MetricPredicate {
55+
return WithLabel("namespace", namespace)
56+
}
57+
5458
func WithChannel(channel string) MetricPredicate {
5559
return WithLabel("channel", channel)
5660
}

test/e2e/metrics_e2e_test.go

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,23 @@ package e2e
55
import (
66
"bytes"
77
"context"
8+
"fmt"
89
"regexp"
910
"strings"
1011

12+
"github.com/blang/semver/v4"
1113
. "github.com/onsi/ginkgo"
1214
. "github.com/onsi/gomega"
1315
io_prometheus_client "github.com/prometheus/client_model/go"
1416
"github.com/prometheus/common/expfmt"
1517
corev1 "k8s.io/api/core/v1"
18+
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
1619
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1720
"k8s.io/apimachinery/pkg/util/net"
1821

1922
"github.com/operator-framework/api/pkg/operators/v1alpha1"
2023
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned"
24+
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry"
2125
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient"
2226
"github.com/operator-framework/operator-lifecycle-manager/test/e2e/ctx"
2327
)
@@ -266,6 +270,109 @@ var _ = Describe("Metrics are generated for OLM managed resources", func() {
266270
})
267271
})
268272
})
273+
274+
Context("Metrics emitted by CatalogSources", func() {
275+
When("A valid CatalogSource object is created", func() {
276+
var (
277+
name = "metrics-catsrc-valid"
278+
cleanup func()
279+
cleanupDone = false
280+
)
281+
BeforeEach(func() {
282+
mainPackageName := genName("nginx-")
283+
284+
mainPackageStable := fmt.Sprintf("%s-stable", mainPackageName)
285+
286+
stableChannel := "stable"
287+
288+
mainCRD := newCRD(genName("ins-"))
289+
mainCSV := newCSV(mainPackageStable, testNamespace, "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{mainCRD}, nil, nil)
290+
291+
mainManifests := []registry.PackageManifest{
292+
{
293+
PackageName: mainPackageName,
294+
Channels: []registry.PackageChannel{
295+
{Name: stableChannel, CurrentCSVName: mainPackageStable},
296+
},
297+
DefaultChannelName: stableChannel,
298+
},
299+
}
300+
_, cleanup = createInternalCatalogSource(c, crc, name, testNamespace, mainManifests, []apiextensions.CustomResourceDefinition{mainCRD}, []v1alpha1.ClusterServiceVersion{mainCSV})
301+
})
302+
AfterEach(func() {
303+
if !cleanupDone {
304+
cleanup()
305+
}
306+
})
307+
It("emits metrics for the catalogSource", func() {
308+
Eventually(func() []Metric {
309+
return getMetricsFromPod(c, getPodWithLabel(c, "app=catalog-operator"), "8081")
310+
}).Should(And(
311+
ContainElement(LikeMetric(
312+
WithFamily("catalog_source_count"),
313+
WithValueGreaterThan(0),
314+
)),
315+
ContainElement(LikeMetric(
316+
WithFamily("catalogSource_ready"),
317+
WithName(name),
318+
WithNamespace(testNamespace),
319+
WithValue(1),
320+
)),
321+
))
322+
})
323+
When("The CatalogSource object is deleted", func() {
324+
BeforeEach(func() {
325+
cleanup()
326+
cleanupDone = true
327+
})
328+
It("deletes the metrics for the CatalogSource", func() {
329+
Eventually(func() []Metric {
330+
return getMetricsFromPod(c, getPodWithLabel(c, "app=catalog-operator"), "8081")
331+
}).Should(And(
332+
Not(ContainElement(LikeMetric(
333+
WithFamily("catalogSource_ready"),
334+
WithName(name),
335+
WithNamespace(testNamespace),
336+
)))))
337+
})
338+
})
339+
})
340+
341+
When("A CatalogSource object is in an invalid state", func() {
342+
var (
343+
name = "metrics-catsrc-invalid"
344+
cleanup func()
345+
)
346+
BeforeEach(func() {
347+
_, cleanup = createInvalidGRPCCatalogSource(crc, name, testNamespace)
348+
})
349+
AfterEach(func() {
350+
cleanup()
351+
})
352+
It("emits metrics for the CatlogSource with a Value greater than 0", func() {
353+
Eventually(func() []Metric {
354+
return getMetricsFromPod(c, getPodWithLabel(c, "app=catalog-operator"), "8081")
355+
}).Should(And(
356+
ContainElement(LikeMetric(
357+
WithFamily("catalogSource_ready"),
358+
WithName(name),
359+
WithNamespace(testNamespace),
360+
WithValue(0),
361+
)),
362+
))
363+
Consistently(func() []Metric {
364+
return getMetricsFromPod(c, getPodWithLabel(c, "app=catalog-operator"), "8081")
365+
}, "3m").Should(And(
366+
ContainElement(LikeMetric(
367+
WithFamily("catalogSource_ready"),
368+
WithName(name),
369+
WithNamespace(testNamespace),
370+
WithValue(0),
371+
)),
372+
))
373+
})
374+
})
375+
})
269376
})
270377

271378
func getPodWithLabel(client operatorclient.ClientInterface, label string) *corev1.Pod {

test/e2e/util_test.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,30 @@ func buildServiceAccountCleanupFunc(t GinkgoTInterface, c operatorclient.ClientI
521521
}
522522
}
523523

524+
func createInvalidGRPCCatalogSource(crc versioned.Interface, name, namespace string) (*v1alpha1.CatalogSource, cleanupFunc) {
525+
526+
catalogSource := &v1alpha1.CatalogSource{
527+
TypeMeta: metav1.TypeMeta{
528+
Kind: v1alpha1.CatalogSourceKind,
529+
APIVersion: v1alpha1.CatalogSourceCRDAPIVersion,
530+
},
531+
ObjectMeta: metav1.ObjectMeta{
532+
Name: name,
533+
Namespace: namespace,
534+
},
535+
Spec: v1alpha1.CatalogSourceSpec{
536+
SourceType: "grpc",
537+
Image: "localhost:0/not/exists:catsrc",
538+
},
539+
}
540+
541+
ctx.Ctx().Logf("Creating catalog source %s in namespace %s...", name, namespace)
542+
catalogSource, err := crc.OperatorsV1alpha1().CatalogSources(namespace).Create(context.TODO(), catalogSource, metav1.CreateOptions{})
543+
Expect(err).ToNot(HaveOccurred())
544+
ctx.Ctx().Logf("Catalog source %s created", name)
545+
return catalogSource, buildCatalogSourceCleanupFunc(crc, namespace, catalogSource)
546+
}
547+
524548
func createInternalCatalogSource(c operatorclient.ClientInterface, crc versioned.Interface, name, namespace string, manifests []registry.PackageManifest, crds []apiextensions.CustomResourceDefinition, csvs []v1alpha1.ClusterServiceVersion) (*v1alpha1.CatalogSource, cleanupFunc) {
525549
configMap, configMapCleanup := createConfigMapForCatalogData(c, name, namespace, manifests, crds, csvs)
526550

@@ -539,7 +563,6 @@ func createInternalCatalogSource(c operatorclient.ClientInterface, crc versioned
539563
ConfigMap: configMap.GetName(),
540564
},
541565
}
542-
catalogSource.SetNamespace(namespace)
543566

544567
ctx.Ctx().Logf("Creating catalog source %s in namespace %s...", name, namespace)
545568
catalogSource, err := crc.OperatorsV1alpha1().CatalogSources(namespace).Create(context.TODO(), catalogSource, metav1.CreateOptions{})

0 commit comments

Comments
 (0)