pmm-managed manages the configuration of PMM server components (VictoriaMetrics, Grafana, QAN, etc.) and exposes an API for interacting with them. The API is also consumed by pmm-admin tool.
PMM uses reform (NOT gorm) for PostgreSQL interactions:
// Models are defined with reform tags and generated code
//go:generate ../../bin/reform
//reform:nodes
type Node struct {
NodeID string `reform:"node_id,pk"`
NodeName string `reform:"node_name"`
// ...
}
// All DB operations use reform.Querier
func FindNodeByID(q *reform.Querier, id string) (*Node, error) {
// Use reform methods, not raw SQL when possible
}
// Transactions use reform.TX
db.InTransactionContext(ctx, nil, func(tx *reform.TX) error {
// Transaction logic
})Key points:
- Models live in
managed/models/ *_model.gofiles have//go:generatedirectives*_helpers.gofiles contain CRUD operations- Always use
reform.Querierparameter, not concrete types - Check for
reform.ErrNoRowsexplicitly
Services follow a consistent pattern in managed/services/:
type Service struct {
db *reform.DB
l *logrus.Entry
// dependencies
}
// Constructor with dependency injection
func New(db *reform.DB, logger *logrus.Entry) *Service {
return &Service{db: db, l: logger}
}Services are composed in managed/cmd/pmm-managed/main.go and injected throughout the application.
APIs are defined in .proto files under /api/:
- Generate with:
make genfrom the root of the repository - Creates Go code, Swagger specs, and gRPC gateway mappings
- Never edit generated files (
.pb.go,.pb.gw.go, swagger files) - Update proto files, then regenerate
PMM supports HA using Raft consensus (/managed/services/ha/):
- Distributed state is managed via Raft
- pmm-agent states are synchronized across nodes
- Uses
hashicorp/raftlibrary - Critical for ensuring consistency in multi-node setups
- Use
testify/assertandtestify/require - Mock generation via
mockery(config in.mockery.yaml) - Use
testdbhelper for DB tests
- Located in
/api-tests/(separate from unit tests) - Use
testify/assertandtestify/require - Setup/teardown pattern with
testdb.Open()helper - Run against live PMM Server:
make api-test
Multiple code generation tools are used:
- Protocol Buffers - APIs (
make genfrom root) - reform - ORM model generation (
//go:generate ../../bin/reform) - mockery - Mock generation for interfaces
- swagger - API documentation
Always run make gen after:
- Adding/modifying
.protofiles - Adding/modifying reform models
- Changing interface signatures that need mocks
- Prefer modern Go idioms (context, error wrapping)
- Prefer modern slice helpers (e.g.,
slices.Contains), range loops - Use
anyinstead ofinterface{}
- Don't use
gormor other ORMs - onlyreform - Don't edit generated files manually
- Don't create subshells in Makefiles without explicit reason
- Don't skip
make genafter proto/model changes - Don't commit test binaries or test artifacts (add to
.gitignoreif needed) - Don't comment on every single line of code unnecessarily, only where clarity is needed
- Don't inline comments (i.e.
code // comment), always put comments on separate lines - Don't use named return values in functions
- Use
status.Error()for gRPC errors with proper codes - Check
reform.ErrNoRowsfor "not found" scenarios - Wrap errors with context:
fmt.Errorf("descriptive context: %w", err) - Return early on errors to avoid deep nesting
- Use
errors.Is()anderrors.As()for error type checking - Use
errors.WithStack()wisely and only when stack traces are needed - Use standard
errorspackage instead ofgithub.com/pkg/errors
- Use structured logging with
logrus - Pass
*logrus.Entry(not*logrus.Logger) to maintain context - Format:
s.l.WithField("key", value).Error("message")
- Agents are registered and managed via
managed/services/agents/registry.go - Communication uses bidirectional gRPC streams
- Agent states are tracked in PostgreSQL and synchronized with HA state machine
- Use RESTful conventions (GET/POST/PUT/DELETE with resource paths)
- Use custom endpoints only when necessary (e.g., actions)
managed/models/database.go- Database schema and migrationsmanaged/cmd/pmm-managed/main.go- Application bootstrap and wiringdocker-compose.yml- Development environment configurationMakefileandMakefile.include- Common make targets.devcontainer/setup.py- Devcontainer initialization