Skip to content

Commit 38d343d

Browse files
committed
PMM-15360: Gate OpenManager's service on the switch
Implements Enabled() bool on om.Service, so every /v1/om/* RPC and the scheduled topology collection refuse while OpenManager is off, via the same generic gRPC-service-enabled interceptor BackupService and the other preview features already use -- no proto or per-method changes needed. Adds IsAvailable(), a read against SEP's om_inventory app (the same call the scheduled collection already makes), and wires it into ChangeSettings so turning OpenManager on is refused unless the app is actually reachable -- rather than accepting a setting that would do nothing. Turning it on also kicks an immediate topology collection (extracted into its own method: golangci-lint's gocognit check flags ChangeSettings once this is inlined alongside Advisors' and telemetry's own transition handling). The generated swagger client's field names are OMEnabled/EnableOM here (not OmEnabled/EnableOm, as in the settings-flag commit this stacks on): this branch's api/Makefile, via PMM-15326-om-api already merged into PMM-15326-om-backend, registers "om" as an additional-initialism for the swagger client generator. That commit hasn't reached the epic yet, so the settings-flag PR's own branch (based on the epic directly) correctly generates the un-capitalized form; this is the make gen output for this branch specifically. Does not touch SEP's om_inventory app in any way -- turning the switch off does not stop its periodic sweep. That's deferred to a follow-up. Signed-off-by: Pawel Lebioda <pawel.lebioda@percona.com>
1 parent 839e3dc commit 38d343d

7 files changed

Lines changed: 81 additions & 10 deletions

File tree

api/server/v1/json/client/server_service/change_settings_responses.go

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/server/v1/json/client/server_service/get_read_only_settings_responses.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/server/v1/json/client/server_service/get_settings_responses.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

managed/cmd/pmm-managed/main.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,6 +1065,15 @@ func main() { //nolint:gocognit,maintidx,cyclop
10651065
VMURL: *victoriaMetricsURLF,
10661066
})
10671067

1068+
// Where SEP is, optional. Empty means OM builds its document from PMM's own inventory
1069+
// and metrics alone and records the probe source as disabled.
1070+
//
1071+
// Constructed here, ahead of serverParams below, so server.Server can hold it for the
1072+
// OpenManager enable/disable switch (Enabled gate, IsAvailable check).
1073+
omService := om.New(db, v1.NewAPI(vmClient), haService, logrus.WithField("component", "om"))
1074+
omService.WithProbeSource(*sepURLF, *sepTokenF)
1075+
prom.MustRegister(om.NewMetricsCollector(omService))
1076+
10681077
serverParams := &server.Params{
10691078
DB: db,
10701079
VMDB: vmdb,
@@ -1081,6 +1090,7 @@ func main() { //nolint:gocognit,maintidx,cyclop
10811090
HAService: haService,
10821091
Nomad: nomad,
10831092
QANClient: qanClient,
1093+
OmService: omService,
10841094
}
10851095

10861096
server, err := server.NewServer(serverParams)
@@ -1197,12 +1207,6 @@ func main() { //nolint:gocognit,maintidx,cyclop
11971207
return nil
11981208
}))
11991209

1200-
// Where SEP is, optional. Empty means OM builds its document from PMM's own inventory
1201-
// and metrics alone and records the probe source as disabled.
1202-
omService := om.New(db, v1.NewAPI(vmClient), haService, logrus.WithField("component", "om"))
1203-
omService.WithProbeSource(*sepURLF, *sepTokenF)
1204-
prom.MustRegister(om.NewMetricsCollector(omService))
1205-
12061210
// Leader-only, like every other periodic writer here. A collection persists a run and
12071211
// its snapshot and then prunes the shared history, so running it on every node of an
12081212
// HA cluster would have each node writing runs and pruning the others' -- and the

managed/services/om/service.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,33 @@ func (s *Service) WithProbeSource(sepURL, token string) *Service {
157157
return s
158158
}
159159

160+
// Enabled returns true if OpenManager is enabled, so every /v1/om/* RPC and the
161+
// scheduled collection in Run refuse while it is off, via the same generic
162+
// gRPC-service-enabled interceptor BackupService and the other preview features use.
163+
func (s *Service) Enabled() bool {
164+
settings, err := models.GetSettings(s.db)
165+
if err != nil {
166+
s.l.WithError(err).Error("can't get settings")
167+
return false
168+
}
169+
return settings.IsOMEnabled()
170+
}
171+
172+
// IsAvailable reports whether SEP's om_inventory app is configured and reachable.
173+
//
174+
// Used to gate turning OpenManager on: an admin flipping the switch with no inventory
175+
// app to talk to would enable a UI backed by a source that can never answer, with no
176+
// way to tell "off" from "broken" apart from reading logs. It does not drive anything
177+
// on SEP's side -- this is the same read every scheduled collection already performs
178+
// via probeSource.collect, just run once up front rather than waited out.
179+
func (s *Service) IsAvailable(ctx context.Context) bool {
180+
if s.probe == nil || s.probe.app.client == nil {
181+
return false
182+
}
183+
_, err := s.probe.fetch(ctx)
184+
return err == nil
185+
}
186+
160187
// GetTopology returns the whole MongoDB estate as one document.
161188
//
162189
// A pure read path: memory, then the stored snapshot, never a collection. Collection is
@@ -261,6 +288,9 @@ func (s *Service) Run(ctx context.Context) {
261288
case <-ctx.Done():
262289
return
263290
case <-ticker.C:
291+
if !s.Enabled() {
292+
continue
293+
}
264294
_, err := s.discover(ctx)
265295
if err != nil && ctx.Err() == nil {
266296
s.l.Warnf("scheduled collection failed: %s", err)

managed/services/server/deps.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"net/url"
2121
"time"
2222

23+
omv1 "github.com/percona/pmm/api/om/v1"
2324
serverv1 "github.com/percona/pmm/api/server/v1"
2425
"github.com/percona/pmm/managed/models"
2526
)
@@ -117,3 +118,12 @@ type victoriaMetricsParams interface {
117118
type nomadService interface {
118119
UpdateConfiguration(settings *models.Settings) error
119120
}
121+
122+
// omService is a subset of methods of om.Service used by this package.
123+
// We use it instead of the real type to avoid a dependency cycle.
124+
type omService interface {
125+
// IsAvailable reports whether SEP's OpenManager Inventory app is configured and
126+
// reachable, gating whether OpenManager may be enabled.
127+
IsAvailable(ctx context.Context) bool
128+
TriggerTopologyCollection(ctx context.Context, req *omv1.TriggerTopologyCollectionRequest) (*omv1.TriggerTopologyCollectionResponse, error)
129+
}

managed/services/server/server.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import (
3838
"google.golang.org/protobuf/types/known/timestamppb"
3939
"gopkg.in/reform.v1"
4040

41+
omv1 "github.com/percona/pmm/api/om/v1"
4142
serverv1 "github.com/percona/pmm/api/server/v1"
4243
"github.com/percona/pmm/managed/models"
4344
"github.com/percona/pmm/managed/utils/distribution"
@@ -63,6 +64,7 @@ type Server struct {
6364
haService haService
6465
updater *Updater
6566
nomad nomadService
67+
omService omService
6668

6769
l *logrus.Entry
6870

@@ -89,6 +91,7 @@ type Params struct {
8991
Dus *distribution.Service
9092
HAService haService
9193
Nomad nomadService
94+
OmService omService
9295
}
9396

9497
// NewServer returns new server for Server service.
@@ -114,6 +117,7 @@ func NewServer(params *Params) (*Server, error) {
114117
updater: params.Updater,
115118
haService: params.HAService,
116119
nomad: params.Nomad,
120+
omService: params.OmService,
117121
l: logrus.WithField("component", "server"),
118122
envSettings: &models.ChangeSettingsParams{},
119123
}
@@ -470,6 +474,14 @@ func (s *Server) validateChangeSettingsRequest(ctx context.Context, req *serverv
470474
return status.Error(codes.FailedPrecondition, "Azure Discover is configured via PMM_ENABLE_AZURE_DISCOVER environment variable.")
471475
}
472476

477+
if req.EnableOm != nil && s.envSettings.EnableOM != nil && *req.EnableOm != *s.envSettings.EnableOM {
478+
return status.Error(codes.FailedPrecondition, "OpenManager is configured via PMM_ENABLE_OM environment variable.")
479+
}
480+
481+
if req.EnableOm != nil && *req.EnableOm && s.omService != nil && !s.omService.IsAvailable(ctx) {
482+
return status.Error(codes.FailedPrecondition, "OpenManager cannot be enabled: the OpenManager Inventory app is not available in SEP.")
483+
}
484+
473485
if !canUpdateDurationSetting(metricsRes.GetHr().AsDuration(), s.envSettings.MetricsResolutions.HR) {
474486
return status.Error(
475487
codes.FailedPrecondition,
@@ -522,6 +534,7 @@ func (s *Server) ChangeSettings(ctx context.Context, req *serverv1.ChangeSetting
522534
EnableBackupManagement: req.EnableBackupManagement,
523535
EnableAccessControl: req.EnableAccessControl,
524536
EnableInternalPgQAN: req.EnableInternalPgQan,
537+
EnableOM: req.EnableOm,
525538
AdvisorsRunInterval: models.AdvisorsRunIntervals{
526539
RareInterval: advisorsRunInterval.GetRareInterval().AsDuration(),
527540
StandardInterval: advisorsRunInterval.GetStandardInterval().AsDuration(),
@@ -613,11 +626,25 @@ func (s *Server) ChangeSettings(ctx context.Context, req *serverv1.ChangeSetting
613626
}
614627
}
615628

629+
s.triggerOMCollectionIfJustEnabled(ctx, oldSettings, newSettings)
630+
616631
return &serverv1.ChangeSettingsResponse{
617632
Settings: s.convertSettings(newSettings, disableInternalPgQan),
618633
}, nil
619634
}
620635

636+
// triggerOMCollectionIfJustEnabled kicks a topology collection so OpenManager's page is
637+
// not empty on first view, instead of waiting out the next scheduled tick.
638+
func (s *Server) triggerOMCollectionIfJustEnabled(ctx context.Context, oldSettings, newSettings *models.Settings) {
639+
if oldSettings.IsOMEnabled() || !newSettings.IsOMEnabled() || s.omService == nil {
640+
return
641+
}
642+
_, err := s.omService.TriggerTopologyCollection(ctx, &omv1.TriggerTopologyCollectionRequest{})
643+
if err != nil {
644+
s.l.Warnf("failed to trigger OpenManager topology collection after enabling: %s", err)
645+
}
646+
}
647+
621648
func (s *Server) getInternalPgQANAgent(q *reform.Querier) (*models.Agent, error) {
622649
agents, err := models.FindAgents(q, models.AgentFilters{
623650
PMMAgentID: models.PMMServerAgentID,

0 commit comments

Comments
 (0)