Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
274b711
feat(rta): extend Real-Time Analytics to MySQL
theTibi Jun 16, 2026
0799543
feat(rta): full processlist raw data + hide-COMMIT toggle for MySQL
theTibi Jun 17, 2026
d52d9b1
fix(rta): per-type version gate, latency floor, collector tests + nits
theTibi Jun 17, 2026
93f4012
fix(rta): remove MySQL statement-latency collection floor
theTibi Jun 18, 2026
d3e3c47
fix(rta): preflight checks for MySQL collector with clear error status
theTibi Jun 19, 2026
bf0391f
fix(rta): address review — 3.9.0 gate, defensive support check, nits
theTibi Jun 19, 2026
c3e860b
feat(rta): inventory add/change API + CLI and API tests for MySQL RTA…
theTibi Jun 19, 2026
8b4f967
Merge branch 'main' into rta-mysql-test
theTibi Jun 19, 2026
54ad5d1
Merge remote-tracking branch 'origin/main' into rta-mysql-test
theTibi Jul 4, 2026
48e93be
Merge remote-tracking branch 'origin/main' into rta-mysql-test
theTibi Jul 31, 2026
8465a49
feat(rta): add database and user filters to the overview table
theTibi Jul 31, 2026
433dd46
fix(rta): address CodeRabbit review findings
theTibi Aug 1, 2026
6764b4e
Merge branch 'main' into rta-mysql-test
theTibi Aug 1, 2026
f281253
fix(rta): guard nil pmm-agent version; fix stale processlist doc
theTibi Aug 1, 2026
6e2d77a
feat(rta): pin Elapsed time to the right edge of the overview table
theTibi Aug 1, 2026
4a57085
chore(rta): align generated files with make gen output
theTibi Aug 2, 2026
1e03295
fix(rta): resolve golangci-lint findings in new RTA MySQL code
theTibi Aug 2, 2026
df2a3b3
feat(rta): implement comma-separated filter for Database and User col…
theTibi Aug 2, 2026
45a36bb
PMM-15283 Hide Database/User columns by default and compact Elapsed time
theTibi Aug 4, 2026
2b15eef
Merge branch 'main' into rta-mysql-test
theTibi Aug 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 122 additions & 0 deletions admin/commands/inventory/add_agent_rta_mysql.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// Copyright (C) 2023 Percona LLC
//
// 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 inventory

import (
"time"

"github.com/percona/pmm/admin/commands"
"github.com/percona/pmm/admin/pkg/flags"
"github.com/percona/pmm/api/inventory/v1/json/client"
agents "github.com/percona/pmm/api/inventory/v1/json/client/agents_service"
)

var addAgentRTAMySQLAgentResultT = commands.ParseTemplate(`
Real-Time Analytics MySQL agent added.
Agent ID : {{ .Agent.AgentID }}
PMM-Agent ID : {{ .Agent.PMMAgentID }}
Service ID : {{ .Agent.ServiceID }}
Username : {{ .Agent.Username }}
TLS enabled : {{ .Agent.TLS }}
Skip TLS verification : {{ .Agent.TLSSkipVerify }}

Disabled : {{ .Agent.Disabled }}
Custom labels : {{ formatCustomLabels .Agent.CustomLabels }}
Collect interval : {{ .Agent.RtaOptions.CollectInterval }}
Log level : {{ formatLogLevel .Agent.LogLevel }}
`)

type addAgentRTAMySQLAgentResult struct {
Agent *agents.AddAgentOKBodyRtaMysqlAgent `json:"rta_mysql_agent"`
}

func (res *addAgentRTAMySQLAgentResult) Result() {}

func (res *addAgentRTAMySQLAgentResult) String() string {
return commands.RenderTemplate(addAgentRTAMySQLAgentResultT, res)
}

// AddAgentRTAMySQLAgentCommand is used by Kong for CLI flags and commands.
type AddAgentRTAMySQLAgentCommand struct {
flags.LogLevelFatalFlags

PMMAgentID string `arg:"" help:"The pmm-agent identifier which runs this instance"`
ServiceID string `arg:"" help:"Service identifier"`
Username string `arg:"" optional:"" help:"MySQL username for getting queries data"`
Password string `help:"MySQL password for getting queries data"`
CustomLabels map[string]string `mapsep:"," help:"Custom user-assigned labels"`
SkipConnectionCheck bool `help:"Skip connection check"`
TLS bool `help:"Use TLS to connect to the database"`
TLSSkipVerify bool `help:"Skip TLS certificate verification"`
TLSCaFile string `help:"Path to certificate authority file"`
TLSCertFile string `help:"Path to client certificate file"`
TLSKeyFile string `help:"Path to client key file"`
CollectInterval *time.Duration `placeholder:"DURATION" help:"Query collect interval (default: server-defined 2s)"`
}

// RunCmd executes the AddAgentRTAMySQLAgentCommand and returns the result.
func (cmd *AddAgentRTAMySQLAgentCommand) RunCmd() (commands.Result, error) {
customLabels := commands.ParseKeyValuePair(&cmd.CustomLabels)

tlsCa, err := commands.ReadFile(cmd.TLSCaFile)
if err != nil {
return nil, err
}

tlsCert, err := commands.ReadFile(cmd.TLSCertFile)
if err != nil {
return nil, err
}

tlsKey, err := commands.ReadFile(cmd.TLSKeyFile)
if err != nil {
return nil, err
}

params := &agents.AddAgentParams{
Body: agents.AddAgentBody{
RtaMysqlAgent: &agents.AddAgentParamsBodyRtaMysqlAgent{
PMMAgentID: cmd.PMMAgentID,
ServiceID: cmd.ServiceID,
Username: cmd.Username,
Password: cmd.Password,
CustomLabels: *customLabels,
SkipConnectionCheck: cmd.SkipConnectionCheck,
TLS: cmd.TLS,
TLSSkipVerify: cmd.TLSSkipVerify,
TLSCa: tlsCa,
TLSCert: tlsCert,
TLSKey: tlsKey,
LogLevel: cmd.LogLevel.EnumValue(),
},
},
Context: commands.Ctx,
}

if cmd.CollectInterval != nil {
params.Body.RtaMysqlAgent.RtaOptions = &agents.AddAgentParamsBodyRtaMysqlAgentRtaOptions{
CollectInterval: cmd.CollectInterval.String(),
}
}

resp, err := client.Default.AgentsService.AddAgent(params)
if err != nil {
return nil, err
}

return &addAgentRTAMySQLAgentResult{
Agent: resp.Payload.RtaMysqlAgent,
}, nil
}
226 changes: 226 additions & 0 deletions admin/commands/inventory/change_agent_rta_mysql.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
// Copyright (C) 2023 Percona LLC
//
// 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 inventory

import (
"fmt"
"time"

"github.com/percona/pmm/admin/commands"
"github.com/percona/pmm/admin/pkg/flags"
"github.com/percona/pmm/api/inventory/v1/json/client"
agents "github.com/percona/pmm/api/inventory/v1/json/client/agents_service"
)

var changeAgentRTAMySQLAgentResultT = commands.ParseTemplate(`
Real-Time Analytics MySQL agent configuration updated.
Agent ID : {{ .Agent.AgentID }}
PMM-Agent ID : {{ .Agent.PMMAgentID }}
Service ID : {{ .Agent.ServiceID }}
Username : {{ .Agent.Username }}
TLS enabled : {{ .Agent.TLS }}
Skip TLS verification : {{ .Agent.TLSSkipVerify }}

Disabled : {{ .Agent.Disabled }}
Custom labels : {{ formatCustomLabels .Agent.CustomLabels }}
Collect interval : {{ .Agent.RtaOptions.CollectInterval }}
Log level : {{ formatLogLevel .Agent.LogLevel }}

{{- if .Changes}}
Configuration changes applied:
{{- range .Changes}}
- {{ . }}
{{- end}}
{{- end}}
`)

type changeAgentRTAMySQLAgentResult struct {
Agent *agents.ChangeAgentOKBodyRtaMysqlAgent `json:"rta_mysql_agent"`
Changes []string `json:"changes,omitempty"`
}

func (res *changeAgentRTAMySQLAgentResult) Result() {}

func (res *changeAgentRTAMySQLAgentResult) String() string {
return commands.RenderTemplate(changeAgentRTAMySQLAgentResultT, res)
}

// ChangeAgentRTAMySQLAgentCommand is used by Kong for CLI flags and commands.
type ChangeAgentRTAMySQLAgentCommand struct {
// Embedded flags
flags.LogLevelFatalChangeFlags

AgentID string `arg:"" help:"Real-Time Analytics MySQL Agent ID"`

// NOTE: Only provided flags will be changed, others will remain unchanged

// Basic options
Enable *bool `help:"Enable or disable the agent"`
Username *string `help:"MySQL username for getting queries data"`
Password *string `help:"MySQL password for getting queries data"`

// TLS options
TLS *bool `help:"Use TLS to connect to the database"`
TLSSkipVerify *bool `help:"Skip TLS certificate verification"`
TLSCaFile *string `help:"Path to certificate authority file"`
TLSCertFile *string `help:"Path to client certificate file"`
TLSKeyFile *string `help:"Path to client key file"`

// RTA specific options
CollectInterval *time.Duration `placeholder:"DURATION" help:"Query collect interval (default: server-defined 2s)"`

// Custom labels
CustomLabels *map[string]string `mapsep:"," help:"Custom user-assigned labels"`

SkipConnectionCheck *bool `help:"Skip connection check"`
}

// readFlagFile reads the file behind an optional CLI flag; a nil path means
// the flag was not provided.
func readFlagFile(path *string, what string) (*string, error) {
if path == nil {
return nil, nil //nolint:nilnil
}

content, err := commands.ReadFile(*path)
if err != nil {
return nil, fmt.Errorf("failed to read %s: %w", what, err)
}

return &content, nil
}

// RunCmd executes the ChangeAgentRTAMySQLAgentCommand and returns the result.
func (cmd *ChangeAgentRTAMySQLAgentCommand) RunCmd() (commands.Result, error) {
// Parse custom labels if provided
customLabels := commands.ParseKeyValuePair(cmd.CustomLabels)

// Read TLS files if provided
tlsCa, err := readFlagFile(cmd.TLSCaFile, "TLS CA file")
if err != nil {
return nil, err
}

tlsCert, err := readFlagFile(cmd.TLSCertFile, "TLS certificate file")
if err != nil {
return nil, err
}

tlsKey, err := readFlagFile(cmd.TLSKeyFile, "TLS key file")
if err != nil {
return nil, err
}

body := &agents.ChangeAgentParamsBodyRtaMysqlAgent{
Enable: cmd.Enable,
Username: cmd.Username,
Password: cmd.Password,
TLS: cmd.TLS,
TLSSkipVerify: cmd.TLSSkipVerify,
TLSCa: tlsCa,
TLSCert: tlsCert,
TLSKey: tlsKey,
LogLevel: convertLogLevelPtr(cmd.LogLevel),
SkipConnectionCheck: cmd.SkipConnectionCheck,
}

if customLabels != nil {
body.CustomLabels = &agents.ChangeAgentParamsBodyRtaMysqlAgentCustomLabels{
Values: *customLabels,
}
}

if cmd.CollectInterval != nil {
body.RtaOptions = &agents.ChangeAgentParamsBodyRtaMysqlAgentRtaOptions{
CollectInterval: cmd.CollectInterval.String(),
}
}

params := &agents.ChangeAgentParams{
AgentID: cmd.AgentID,
Body: agents.ChangeAgentBody{
RtaMysqlAgent: body,
},
Context: commands.Ctx,
}

resp, err := client.Default.AgentsService.ChangeAgent(params)
if err != nil {
return nil, err
}

return &changeAgentRTAMySQLAgentResult{
Agent: resp.Payload.RtaMysqlAgent,
Changes: cmd.describeChanges(customLabels),
}, nil
}

// describeChanges lists the modifications applied by this command invocation.
func (cmd *ChangeAgentRTAMySQLAgentCommand) describeChanges(customLabels *map[string]string) []string {
var changes []string

if cmd.Enable != nil {
if *cmd.Enable {
changes = append(changes, "enabled agent")
} else {
changes = append(changes, "disabled agent")
}
}
if cmd.Username != nil {
changes = append(changes, "updated username")
}
if cmd.Password != nil {
changes = append(changes, "updated password")
}
if cmd.TLS != nil {
if *cmd.TLS {
changes = append(changes, "enabled TLS")
} else {
changes = append(changes, "disabled TLS")
}
}
if cmd.TLSSkipVerify != nil {
if *cmd.TLSSkipVerify {
changes = append(changes, "enabled TLS skip verification")
} else {
changes = append(changes, "disabled TLS skip verification")
}
}
if cmd.TLSCaFile != nil {
changes = append(changes, "updated TLS CA certificate")
}
if cmd.TLSCertFile != nil {
changes = append(changes, "updated TLS certificate")
}
if cmd.TLSKeyFile != nil {
changes = append(changes, "updated TLS key")
}
if cmd.LogLevel != nil {
changes = append(changes, fmt.Sprintf("changed log level to %s", *cmd.LogLevel))
}
if customLabels != nil {
if len(*customLabels) != 0 {
changes = append(changes, "updated custom labels")
} else {
changes = append(changes, "custom labels are removed")
}
}

if cmd.CollectInterval != nil {
changes = append(changes, fmt.Sprintf("changed collect interval to %s", *cmd.CollectInterval))
}

return changes
}
2 changes: 2 additions & 0 deletions admin/commands/inventory/inventory.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ type AddAgentCommand struct {

RDSExporter AddAgentRDSExporterCommand `cmd:"" help:"Add rds_exporter to inventory"`
RTAMongoDBAgent AddAgentRTAMongoDBAgentCommand `cmd:"" name:"rta-mongodb-agent" help:"Add Real-Time Analytics MongoDB agent to inventory"`
RTAMySQLAgent AddAgentRTAMySQLAgentCommand `cmd:"" name:"rta-mysql-agent" help:"Add Real-Time Analytics MySQL agent to inventory"`
}

// AddNodeCommand is used by Kong for CLI flags and commands.
Expand Down Expand Up @@ -119,6 +120,7 @@ type ChangeAgentCommand struct {
QANPostgreSQLPgStatementsAgent ChangeAgentQANPostgreSQLPgStatementsAgentCommand `cmd:"" name:"qan-postgresql-pgstatements-agent" help:"Change QAN PostgreSQL pgstatements agent configuration (only passed flags will be changed)"`
QANPostgreSQLPgStatMonitorAgent ChangeAgentQANPostgreSQLPgStatMonitorAgentCommand `cmd:"" name:"qan-postgresql-pgstatmonitor-agent" help:"Change QAN PostgreSQL pgstatmonitor agent configuration (only passed flags will be changed)"`
RTAMongoDBAgent ChangeAgentRTAMongoDBAgentCommand `cmd:"" name:"rta-mongodb-agent" help:"Change Real-Time Analytics MongoDB agent configuration (only passed flags will be changed)"`
RTAMySQLAgent ChangeAgentRTAMySQLAgentCommand `cmd:"" name:"rta-mysql-agent" help:"Change Real-Time Analytics MySQL agent configuration (only passed flags will be changed)"`
}

// formatTypeValue checks acceptable type value and variations contains input and returns type value.
Expand Down
Loading
Loading