From 28375f6cd297e5d19e207f397b8c3c32c5523fa2 Mon Sep 17 00:00:00 2001 From: Gilles Miraillet Date: Wed, 12 Feb 2025 22:53:50 +0100 Subject: [PATCH] turn back on calls to `/v2/spaces/:space_guid/summary` if bbs is not configured or unreachable --- collectors/applications.go | 12 +++++++----- fetcher/bbs_client.go | 17 ++++++++++++++++- fetcher/fetcher.go | 11 +++++++---- fetcher/fetcher_handlers.go | 25 ++++++++++++++++++++++++- filters/filters.go | 8 ++++++++ models/model.go | 1 + 6 files changed, 63 insertions(+), 11 deletions(-) diff --git a/collectors/applications.go b/collectors/applications.go index eceb7888..a1bba4b0 100644 --- a/collectors/applications.go +++ b/collectors/applications.go @@ -6,8 +6,6 @@ import ( "fmt" "time" - "code.cloudfoundry.org/cli/resources" - "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant" "github.com/cloudfoundry/cf_exporter/models" "github.com/prometheus/client_golang/prometheus" @@ -251,7 +249,7 @@ func (c ApplicationsCollector) reportApp(application models.Application, objs *m break } } - detectedBuildpack, buildpack := c.collectAppBuildpacks(application, objs.Droplets) + detectedBuildpack, buildpack := c.collectAppBuildpacks(application, objs) c.applicationInfoMetric.WithLabelValues( application.GUID, @@ -287,6 +285,10 @@ func (c ApplicationsCollector) reportApp(application models.Application, objs *m } } } + } else if len(objs.AppSummaries) > 0 { + if appSummary, ok := objs.AppSummaries[application.GUID]; ok { + runningInstances = appSummary.RunningInstances + } } c.applicationInstancesRunningMetric.WithLabelValues( application.GUID, @@ -318,11 +320,11 @@ func (c ApplicationsCollector) reportApp(application models.Application, objs *m return nil } -func (c ApplicationsCollector) collectAppBuildpacks(application models.Application, droplets map[string]resources.Droplet) (detectedBuildpack string, buildpack string) { +func (c ApplicationsCollector) collectAppBuildpacks(application models.Application, objs *models.CFObjects) (detectedBuildpack string, buildpack string) { detectedBuildpack = "" buildpack = "" if dropletGUID := application.Relationships[constant.RelationshipTypeCurrentDroplet].GUID; dropletGUID != "" { - if droplet, ok := droplets[dropletGUID]; ok { + if droplet, ok := objs.Droplets[dropletGUID]; ok { // 1. detectedBuildpack = droplet.Buildpacks[0].DetectOutput // 2. diff --git a/fetcher/bbs_client.go b/fetcher/bbs_client.go index bf96b2da..0bd25fe7 100644 --- a/fetcher/bbs_client.go +++ b/fetcher/bbs_client.go @@ -1,6 +1,7 @@ package fetcher import ( + "fmt" "strings" "time" @@ -51,7 +52,13 @@ func NewBBSClient(config *BBSConfig) (*BBSClient, error) { bbsClientConfig.MaxIdleConnsPerHost = maxIdleConnsPerHost } bbsClient.client, err = bbs.NewClientWithConfig(bbsClientConfig) - return &bbsClient, err + if err != nil { + return nil, err + } + if bbsClient.client.Ping(bbsClient.logger, trace.GenerateTraceID()) { + return &bbsClient, nil + } + return nil, fmt.Errorf("failed to ping BBS") } func (b *BBSClient) GetActualLRPs() ([]*models.ActualLRP, error) { @@ -60,3 +67,11 @@ func (b *BBSClient) GetActualLRPs() ([]*models.ActualLRP, error) { return actualLRPs, err } + +func (b *BBSClient) TestConnection() error { + traceID := trace.GenerateTraceID() + if b.client.Ping(b.logger, traceID) { + return nil + } + return fmt.Errorf("failed to ping BBS") +} diff --git a/fetcher/fetcher.go b/fetcher/fetcher.go index d709937a..178aab29 100644 --- a/fetcher/fetcher.go +++ b/fetcher/fetcher.go @@ -39,12 +39,14 @@ type Fetcher struct { cfConfig *CFConfig bbsConfig *BBSConfig worker *Worker + filters *filters.Filter } func NewFetcher(threads int, config *CFConfig, bbsConfig *BBSConfig, filter *filters.Filter) *Fetcher { return &Fetcher{ cfConfig: config, bbsConfig: bbsConfig, + filters: filter, worker: NewWorker(threads, filter), } } @@ -97,11 +99,12 @@ func (c *Fetcher) fetch() *models.CFObjects { result.Error = err return result } - bbs, err := NewBBSClient(c.bbsConfig) - if err != nil { + + var bbs *BBSClient + if c.bbsConfig.URL != "" { + bbs, err = NewBBSClient(c.bbsConfig) log.WithError(err).Error("unable to initialize bbs client") - result.Error = err - return result + c.filters.Disable([]string{filters.ActualLRPs}) } c.workInit() diff --git a/fetcher/fetcher_handlers.go b/fetcher/fetcher_handlers.go index be7a0787..c73b3625 100644 --- a/fetcher/fetcher_handlers.go +++ b/fetcher/fetcher_handlers.go @@ -1,9 +1,12 @@ package fetcher import ( + "fmt" "regexp" "time" + "github.com/cloudfoundry/cf_exporter/filters" + models2 "code.cloudfoundry.org/bbs/models" "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" @@ -70,10 +73,30 @@ func (c *Fetcher) fetchOrgQuotas(session *SessionExt, _ *BBSClient, entry *model // fetchSpaces // 1. silent fail because space may have been deleted between listing and // summary fetching attempt. See cloudfoundry/cf_exporter#85 -func (c *Fetcher) fetchSpaces(session *SessionExt, _ *BBSClient, entry *models.CFObjects) error { +func (c *Fetcher) fetchSpaces(session *SessionExt, bbs *BBSClient, entry *models.CFObjects) error { spaces, _, _, err := session.V3().GetSpaces(LargeQuery) if err == nil { loadIndex(entry.Spaces, spaces, func(r resources.Space) string { return r.GUID }) + if bbs == nil { + total := len(spaces) + for idx := 0; idx < total; idx++ { + space := spaces[idx] + name := fmt.Sprintf("space_summaries %04d/%04d (%s)", idx, total, space.GUID) + c.worker.PushIf(name, func(session *SessionExt, _ *BBSClient, entry *models.CFObjects) error { + spaceSum, err := session.GetSpaceSummary(space.GUID) + if err == nil { + c.Lock() + for _, app := range spaceSum.Apps { + entry.AppSummaries[app.GUID] = app + } + c.Unlock() + } else { + log.WithError(err).Warnf("could not fetch space '%s' summary", space.GUID) + } + return nil + }, filters.Applications) + } + } } return err } diff --git a/filters/filters.go b/filters/filters.go index 48a4cce4..dafeccc7 100644 --- a/filters/filters.go +++ b/filters/filters.go @@ -86,6 +86,14 @@ func NewFilter(active ...string) (*Filter, error) { return filter, nil } +func (f *Filter) Disable(deactivate []string) { + for _, val := range deactivate { + if _, ok := f.activated[val]; ok { + f.activated[val] = false + } + } +} + func (f *Filter) setActive(active []string) error { // override default states with all disabled f.activated = map[string]bool{ diff --git a/models/model.go b/models/model.go index a40a80ac..0fc5356d 100644 --- a/models/model.go +++ b/models/model.go @@ -33,6 +33,7 @@ type CFObjects struct { ServiceOfferings map[string]resources.ServiceOffering `json:"service_offerings"` ServicePlans map[string]resources.ServicePlan `json:"service_plans"` ServiceBindings map[string]resources.ServiceCredentialBinding `json:"service_bindings"` + AppSummaries map[string]AppSummary `json:"app_summaries"` AppProcesses map[string][]resources.Process `json:"app_processes"` ProcessActualLRPs map[string][]*models.ActualLRP `json:"process_actual_lrps"` Events map[string]Event `json:"events"`