Skip to content
This repository has been archived by the owner on Jun 21, 2022. It is now read-only.

Commit

Permalink
PMM-5364 SSL support for Mongo. (#528)
Browse files Browse the repository at this point in the history
* PMM-5364 Deps.

* PMM-5364 Add new fields to add request.

* PMM-5364 Deps.

* PMM-5364 Deps.

* PMM-5364 Add new fields into DB.

* PMM-5364 Reform.

* PMM-5364 Another fields changes.

* PMM-5364 Deps.

* PMM-5364 Deps.

* PMM-5364 Fields.

* PMM-5364 Deps.

* PMM-7026 One more revert.

* PMM-7026 Deps.

* PMM-5364 Changes fields into struct.

* PMM-5364 Fix.

* PMM-5364 Changes.

* PMM-5364 Deps.

* PMM-5364 Gen.

* PMM-5364 Deps.

* PMM-5364 Add TLS keys to MongoDBExplainAction request.

* PMM-5364 Remove old code.

* PMM-5364 Changes.

* PMM-5364 Fix new db fields VM problem.

* PMM-5364 Naming changes.

* PMM-5364 Changes.

* PMM-5364 Deps.

* PMM-5364 Deps.

* PMM-5364 Fix build.

* PMM-5364 Fix test.

* PMM-5367 Changes.

* PMM-5364 Deps.

* PMM-5364 Set default prefix to same.

* PMM-5364 Places for paste creating certs.

* PMM-5364 Fix test.

* PMM-5364 Fix test.

* PMM-5364 Make MongoDBOptions struct.

* PMM-5364 Fix tests.

* PMM-5364 Use text files parameters as .TextFiles. .

* PMM-5364 Use text files parameters as .TextFiles .

* PMM-5364 Refactoring .

* PMM-5364 Fix connection checker.

* PMM-5364 Fix tests.

* PMM-5364 Fix QAN MongoDB Profiler.

* PMM-5364 Fix Explain Action for MongoDB SSL.

* PMM-5364 Fix merge conflicts.

Co-authored-by: Nurlan Moldomurov <nurlan.moldomurov@percona.com>
Co-authored-by: Alexey Palazhchenko <alexey.palazhchenko@percona.com>
  • Loading branch information
3 people authored Dec 29, 2020
1 parent 3139917 commit 0db06f7
Show file tree
Hide file tree
Showing 29 changed files with 2,315 additions and 1,731 deletions.
4 changes: 2 additions & 2 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Makefile.devcontainer
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ install-race: ## Install pmm-managed binaries with race detect
go build -race -v $(PMM_LD_FLAGS) -o $(PMM_RELEASE_PATH)/pmm-managed-starlark ./cmd/pmm-managed-starlark

PMM_TEST_PACKAGES ?= ./...
PMM_TEST_FLAGS ?= -timeout=60s
PMM_TEST_FLAGS ?= -timeout=90s
PMM_TEST_RUN_UPDATE ?= 0

test: ## Run tests.
Expand Down
22 changes: 22 additions & 0 deletions models/agent_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,25 @@ import (
"gopkg.in/reform.v1"
)

// MongoDBOptionsParams contains methods to create MongoDBOptions object.
type MongoDBOptionsParams interface {
GetTlsCertificateKey() string
GetTlsCertificateKeyFilePassword() string
GetTlsCa() string
}

// MongoDBOptionsFromRequest creates MongoDBOptionsParams object from request.
func MongoDBOptionsFromRequest(params MongoDBOptionsParams) *MongoDBOptions {
if params.GetTlsCertificateKey() != "" || params.GetTlsCertificateKeyFilePassword() != "" || params.GetTlsCa() != "" {
return &MongoDBOptions{
TLSCertificateKey: params.GetTlsCertificateKey(),
TLSCertificateKeyFilePassword: params.GetTlsCertificateKeyFilePassword(),
TLSCa: params.GetTlsCa(),
}
}
return nil
}

func checkUniqueAgentID(q *reform.Querier, id string) error {
if id == "" {
panic("empty Agent ID")
Expand Down Expand Up @@ -312,6 +331,7 @@ func createPMMAgentWithID(q *reform.Querier, id, runsOnNodeID string, customLabe
if err := agent.SetCustomLabels(customLabels); err != nil {
return nil, err
}

if err := q.Insert(agent); err != nil {
return nil, errors.WithStack(err)
}
Expand Down Expand Up @@ -449,6 +469,7 @@ type CreateAgentParams struct {
CustomLabels map[string]string
TLS bool
TLSSkipVerify bool
MongoDBOptions *MongoDBOptions
TableCountTablestatsGroupLimit int32
QueryExamplesDisabled bool
MaxQueryLogSize int64
Expand Down Expand Up @@ -500,6 +521,7 @@ func CreateAgent(q *reform.Querier, agentType AgentType, params *CreateAgentPara
Password: pointer.ToStringOrNil(params.Password),
TLS: params.TLS,
TLSSkipVerify: params.TLSSkipVerify,
MongoDBOptions: params.MongoDBOptions,
TableCountTablestatsGroupLimit: params.TableCountTablestatsGroupLimit,
QueryExamplesDisabled: params.QueryExamplesDisabled,
MaxQueryLogSize: params.MaxQueryLogSize,
Expand Down
94 changes: 90 additions & 4 deletions models/agent_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@
package models

import (
"database/sql/driver"
"fmt"
"net"
"net/url"
"strconv"
"strings"
"time"

"github.com/AlekSi/pointer"
Expand All @@ -35,6 +37,9 @@ import (
// pmm-managed's PostgreSQL, qan-api's ClickHouse, and VictoriaMetrics.
type AgentType string

const certificateKeyFilePlaceholder = "certificateKeyFilePlaceholder"
const caFilePlaceholder = "caFilePlaceholder"

// Agent types (in the same order as in agents.proto).
const (
PMMAgentType AgentType = "pmm-agent"
Expand All @@ -56,11 +61,23 @@ const (
// PMMServerAgentID is a special Agent ID representing pmm-agent on PMM Server.
const PMMServerAgentID string = "pmm-server" // no /agent_id/ prefix

// MongoDBOptions represents structure for special MongoDB options.
type MongoDBOptions struct {
TLSCertificateKey string `json:"tls_certificate_key"`
TLSCertificateKeyFilePassword string `json:"tls_certificate_key_file_password"`
TLSCa string `json:"tls_ca"`
}

// Value implements database/sql/driver.Valuer interface. Should be defined on the value.
func (c MongoDBOptions) Value() (driver.Value, error) { return jsonValue(c) }

// Scan implements database/sql.Scanner interface. Should be defined on the pointer.
func (c *MongoDBOptions) Scan(src interface{}) error { return jsonScan(c, src) }

// PMMAgentWithPushMetricsSupport - version of pmmAgent,
// that support vmagent and push metrics mode
// will be released with PMM Agent v2.12.
// TODO fix it to 2.11.99 before release
var PMMAgentWithPushMetricsSupport = version.MustParse("2.11.1")
var PMMAgentWithPushMetricsSupport = version.MustParse("2.11.99")

// Agent represents Agent as stored in database.
//reform:agents
Expand Down Expand Up @@ -105,6 +122,8 @@ type Agent struct {
RDSBasicMetricsDisabled bool `reform:"rds_basic_metrics_disabled"`
RDSEnhancedMetricsDisabled bool `reform:"rds_enhanced_metrics_disabled"`
PushMetrics bool `reform:"push_metrics"`

MongoDBOptions *MongoDBOptions `reform:"mongo_db_tls_options"`
}

// BeforeInsert implements reform.BeforeInserter interface.
Expand Down Expand Up @@ -169,13 +188,17 @@ func (s *Agent) UnifiedLabels() (map[string]string, error) {
}

// DSN returns DSN string for accessing given Service with this Agent (and implicit driver).
func (s *Agent) DSN(service *Service, dialTimeout time.Duration, database string) string {
func (s *Agent) DSN(service *Service, dialTimeout time.Duration, database string, tdp *DelimiterPair) string {
host := pointer.GetString(service.Address)
port := pointer.GetUint16(service.Port)
socket := pointer.GetString(service.Socket)
username := pointer.GetString(s.Username)
password := pointer.GetString(s.Password)

if tdp == nil {
tdp = s.TemplateDelimiters(service)
}

switch s.AgentType {
case MySQLdExporterType, ProxySQLExporterType:
cfg := mysql.NewConfig()
Expand Down Expand Up @@ -256,6 +279,18 @@ func (s *Agent) DSN(service *Service, dialTimeout time.Duration, database string
if s.TLSSkipVerify {
q.Add("tlsInsecure", "true")
}

if s.MongoDBOptions != nil {
if s.MongoDBOptions.TLSCertificateKey != "" {
q.Add("tlsCertificateKeyFile", tdp.Left+".TextFiles."+certificateKeyFilePlaceholder+tdp.Right)
}
if s.MongoDBOptions.TLSCertificateKeyFilePassword != "" {
q.Add("tlsCertificateKeyFilePassword", s.MongoDBOptions.TLSCertificateKeyFilePassword)
}
if s.MongoDBOptions.TLSCa != "" {
q.Add("tlsCaFile", tdp.Left+".TextFiles."+caFilePlaceholder+tdp.Right)
}
}
}

address := socket
Expand All @@ -275,7 +310,10 @@ func (s *Agent) DSN(service *Service, dialTimeout time.Duration, database string
case username != "":
u.User = url.User(username)
}
return u.String()
dsn := u.String()
dsn = strings.ReplaceAll(dsn, url.QueryEscape(tdp.Left), tdp.Left)
dsn = strings.ReplaceAll(dsn, url.QueryEscape(tdp.Right), tdp.Right)
return dsn

case PostgresExporterType, QANPostgreSQLPgStatementsAgentType, QANPostgreSQLPgStatMonitorAgentType:
q := make(url.Values)
Expand Down Expand Up @@ -341,6 +379,54 @@ func (s *Agent) IsMySQLTablestatsGroupEnabled() bool {
}
}

// Files returns files map required to connect to DB.
func (s Agent) Files() map[string]string {
switch s.AgentType {
case MySQLdExporterType, ProxySQLExporterType:
return nil
case QANMySQLPerfSchemaAgentType, QANMySQLSlowlogAgentType:
return nil
case QANMongoDBProfilerAgentType, MongoDBExporterType:
if s.MongoDBOptions != nil {
return map[string]string{
caFilePlaceholder: s.MongoDBOptions.TLSCa,
certificateKeyFilePlaceholder: s.MongoDBOptions.TLSCertificateKey,
}
}
return nil
case PostgresExporterType, QANPostgreSQLPgStatementsAgentType, QANPostgreSQLPgStatMonitorAgentType:
return nil
default:
panic(fmt.Errorf("unhandled AgentType %q", s.AgentType))
}
}

// TemplateDelimiters returns a pair of safe template delimiters that are not present in agent parameters.
func (s Agent) TemplateDelimiters(svc *Service) *DelimiterPair {
templateParams := []string{
pointer.GetString(svc.Address),
pointer.GetString(s.Username),
pointer.GetString(s.Password),
pointer.GetString(s.MetricsPath),
}

switch svc.ServiceType {
case MySQLServiceType:
case MongoDBServiceType:
if s.MongoDBOptions != nil {
templateParams = append(templateParams, s.MongoDBOptions.TLSCertificateKeyFilePassword)
}
case PostgreSQLServiceType:
case ProxySQLServiceType:
case ExternalServiceType:
}

tdp := TemplateDelimsPair(
templateParams...,
)
return &tdp
}

// check interfaces
var (
_ reform.BeforeInserter = (*Agent)(nil)
Expand Down
7 changes: 6 additions & 1 deletion models/agent_model_reform.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 0db06f7

Please sign in to comment.