diff --git a/cli-tests/package-lock.json b/cli-tests/package-lock.json index 693fc48956..d59a2ce49d 100644 --- a/cli-tests/package-lock.json +++ b/cli-tests/package-lock.json @@ -20,8 +20,8 @@ "devDependencies": { "@types/promise-retry": "^1.1.3", "@types/shelljs": "^0.8.12", - "@typescript-eslint/eslint-plugin": "^7.11.0", - "@typescript-eslint/parser": "^7.11.0", + "@typescript-eslint/eslint-plugin": "^7.12.0", + "@typescript-eslint/parser": "^7.12.0", "eslint": "8.56", "eslint-config-airbnb-base": "^15.0.0", "eslint-config-airbnb-typescript": "^18.0.0", @@ -249,16 +249,16 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.11.0.tgz", - "integrity": "sha512-P+qEahbgeHW4JQ/87FuItjBj8O3MYv5gELDzr8QaQ7fsll1gSMTYb6j87MYyxwf3DtD7uGFB9ShwgmCJB5KmaQ==", + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.12.0.tgz", + "integrity": "sha512-7F91fcbuDf/d3S8o21+r3ZncGIke/+eWk0EpO21LXhDfLahriZF9CGj4fbAetEjlaBdjdSm9a6VeXbpbT6Z40Q==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.11.0", - "@typescript-eslint/type-utils": "7.11.0", - "@typescript-eslint/utils": "7.11.0", - "@typescript-eslint/visitor-keys": "7.11.0", + "@typescript-eslint/scope-manager": "7.12.0", + "@typescript-eslint/type-utils": "7.12.0", + "@typescript-eslint/utils": "7.12.0", + "@typescript-eslint/visitor-keys": "7.12.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -282,15 +282,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.11.0.tgz", - "integrity": "sha512-yimw99teuaXVWsBcPO1Ais02kwJ1jmNA1KxE7ng0aT7ndr1pT1wqj0OJnsYVGKKlc4QJai86l/025L6z8CljOg==", + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.12.0.tgz", + "integrity": "sha512-dm/J2UDY3oV3TKius2OUZIFHsomQmpHtsV0FTh1WO8EKgHLQ1QCADUqscPgTpU+ih1e21FQSRjXckHn3txn6kQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.11.0", - "@typescript-eslint/types": "7.11.0", - "@typescript-eslint/typescript-estree": "7.11.0", - "@typescript-eslint/visitor-keys": "7.11.0", + "@typescript-eslint/scope-manager": "7.12.0", + "@typescript-eslint/types": "7.12.0", + "@typescript-eslint/typescript-estree": "7.12.0", + "@typescript-eslint/visitor-keys": "7.12.0", "debug": "^4.3.4" }, "engines": { @@ -310,13 +310,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.11.0.tgz", - "integrity": "sha512-27tGdVEiutD4POirLZX4YzT180vevUURJl4wJGmm6TrQoiYwuxTIY98PBp6L2oN+JQxzE0URvYlzJaBHIekXAw==", + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.12.0.tgz", + "integrity": "sha512-itF1pTnN6F3unPak+kutH9raIkL3lhH1YRPGgt7QQOh43DQKVJXmWkpb+vpc/TiDHs6RSd9CTbDsc/Y+Ygq7kg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.11.0", - "@typescript-eslint/visitor-keys": "7.11.0" + "@typescript-eslint/types": "7.12.0", + "@typescript-eslint/visitor-keys": "7.12.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -327,13 +327,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.11.0.tgz", - "integrity": "sha512-WmppUEgYy+y1NTseNMJ6mCFxt03/7jTOy08bcg7bxJJdsM4nuhnchyBbE8vryveaJUf62noH7LodPSo5Z0WUCg==", + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.12.0.tgz", + "integrity": "sha512-lib96tyRtMhLxwauDWUp/uW3FMhLA6D0rJ8T7HmH7x23Gk1Gwwu8UZ94NMXBvOELn6flSPiBrCKlehkiXyaqwA==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "7.11.0", - "@typescript-eslint/utils": "7.11.0", + "@typescript-eslint/typescript-estree": "7.12.0", + "@typescript-eslint/utils": "7.12.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -354,9 +354,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.11.0.tgz", - "integrity": "sha512-MPEsDRZTyCiXkD4vd3zywDCifi7tatc4K37KqTprCvaXptP7Xlpdw0NR2hRJTetG5TxbWDB79Ys4kLmHliEo/w==", + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.12.0.tgz", + "integrity": "sha512-o+0Te6eWp2ppKY3mLCU+YA9pVJxhUJE15FV7kxuD9jgwIAa+w/ycGJBMrYDTpVGUM/tgpa9SeMOugSabWFq7bg==", "dev": true, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -367,13 +367,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.11.0.tgz", - "integrity": "sha512-cxkhZ2C/iyi3/6U9EPc5y+a6csqHItndvN/CzbNXTNrsC3/ASoYQZEt9uMaEp+xFNjasqQyszp5TumAVKKvJeQ==", + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.12.0.tgz", + "integrity": "sha512-5bwqLsWBULv1h6pn7cMW5dXX/Y2amRqLaKqsASVwbBHMZSnHqE/HN4vT4fE0aFsiwxYvr98kqOWh1a8ZKXalCQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.11.0", - "@typescript-eslint/visitor-keys": "7.11.0", + "@typescript-eslint/types": "7.12.0", + "@typescript-eslint/visitor-keys": "7.12.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -419,15 +419,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.11.0.tgz", - "integrity": "sha512-xlAWwPleNRHwF37AhrZurOxA1wyXowW4PqVXZVUNCLjB48CqdPJoJWkrpH2nij9Q3Lb7rtWindtoXwxjxlKKCA==", + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.12.0.tgz", + "integrity": "sha512-Y6hhwxwDx41HNpjuYswYp6gDbkiZ8Hin9Bf5aJQn1bpTs3afYY4GX+MPYxma8jtoIV2GRwTM/UJm/2uGCVv+DQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.11.0", - "@typescript-eslint/types": "7.11.0", - "@typescript-eslint/typescript-estree": "7.11.0" + "@typescript-eslint/scope-manager": "7.12.0", + "@typescript-eslint/types": "7.12.0", + "@typescript-eslint/typescript-estree": "7.12.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -441,12 +441,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.11.0.tgz", - "integrity": "sha512-7syYk4MzjxTEk0g/w3iqtgxnFQspDJfn6QKD36xMuuhTzjcxY7F8EmBLnALjVyaOF1/bVocu3bS/2/F7rXrveQ==", + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.12.0.tgz", + "integrity": "sha512-uZk7DevrQLL3vSnfFl5bj4sL75qC9D6EdjemIdbtkuUmIheWpuiiylSY01JxJE7+zGrOWDZrp1WxOuDntvKrHQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.11.0", + "@typescript-eslint/types": "7.12.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { diff --git a/cli-tests/package.json b/cli-tests/package.json index 9f99b39751..82b0bf1e64 100644 --- a/cli-tests/package.json +++ b/cli-tests/package.json @@ -24,8 +24,8 @@ "devDependencies": { "@types/promise-retry": "^1.1.3", "@types/shelljs": "^0.8.12", - "@typescript-eslint/eslint-plugin": "^7.11.0", - "@typescript-eslint/parser": "^7.11.0", + "@typescript-eslint/eslint-plugin": "^7.12.0", + "@typescript-eslint/parser": "^7.12.0", "eslint": "8.56", "eslint-config-airbnb-base": "^15.0.0", "eslint-config-airbnb-typescript": "^18.0.0", diff --git a/managed/models/agent_helpers.go b/managed/models/agent_helpers.go index d509bdf435..68fd243bec 100644 --- a/managed/models/agent_helpers.go +++ b/managed/models/agent_helpers.go @@ -524,6 +524,24 @@ func FindPmmAgentIDToRunActionOrJob(pmmAgentID string, agents []*Agent) (string, return "", status.Errorf(codes.FailedPrecondition, "Couldn't find pmm-agent-id to run action") } +// ExtractPmmAgentVersionFromAgent extract PMM agent version from Agent by pmm-agent-id. +func ExtractPmmAgentVersionFromAgent(q *reform.Querier, agent *Agent) *version.Parsed { + pmmAgentID, err := ExtractPmmAgentID(agent) + if err != nil { + return nil + } + pmmAgent, err := FindAgentByID(q, pmmAgentID) + if err != nil { + return nil + } + version, err := version.Parse(pointer.GetString(pmmAgent.Version)) + if err != nil { + return nil + } + + return version +} + // ExtractPmmAgentID extract pmm-agent-id from Agent by type. func ExtractPmmAgentID(agent *Agent) (string, error) { switch agent.AgentType { diff --git a/managed/models/agent_model.go b/managed/models/agent_model.go index bd7014b303..ab838e423f 100644 --- a/managed/models/agent_model.go +++ b/managed/models/agent_model.go @@ -73,6 +73,8 @@ const ( VMAgentType AgentType = "vmagent" ) +var v2_42 = version.MustParse("2.42.0-0") + // PMMServerAgentID is a special Agent ID representing pmm-agent on PMM Server. const PMMServerAgentID = string("pmm-server") // no /agent_id/ prefix @@ -313,7 +315,7 @@ type DSNParams struct { } // DSN returns a DSN string for accessing a given Service with this Agent (and an implicit driver). -func (s *Agent) DSN(service *Service, dsnParams DSNParams, tdp *DelimiterPair) string { //nolint:cyclop,maintidx +func (s *Agent) DSN(service *Service, dsnParams DSNParams, tdp *DelimiterPair, pmmAgentVersion *version.Parsed) string { //nolint:cyclop,maintidx host := pointer.GetString(service.Address) port := pointer.GetUint16(service.Port) socket := pointer.GetString(service.Socket) @@ -340,8 +342,12 @@ func (s *Agent) DSN(service *Service, dsnParams DSNParams, tdp *DelimiterPair) s cfg.Params = make(map[string]string) if s.TLS { // It is mandatory to have "custom" as the first case. + // Except case for backward compatibility. // Skip verify for "custom" is handled on pmm-agent side. switch { + // Backward compatibility + case s.TLSSkipVerify && (pmmAgentVersion == nil || pmmAgentVersion.Less(v2_42)): + cfg.Params["tls"] = skipVerify case len(s.Files()) != 0: cfg.Params["tls"] = "custom" case s.TLSSkipVerify: @@ -371,8 +377,12 @@ func (s *Agent) DSN(service *Service, dsnParams DSNParams, tdp *DelimiterPair) s cfg.Params = make(map[string]string) if s.TLS { // It is mandatory to have "custom" as the first case. + // Except case for backward compatibility. // Skip verify for "custom" is handled on pmm-agent side. switch { + // Backward compatibility + case pmmAgentVersion != nil && s.TLSSkipVerify && pmmAgentVersion.Less(v2_42): + cfg.Params["tls"] = skipVerify case len(s.Files()) != 0: cfg.Params["tls"] = "custom" case s.TLSSkipVerify: diff --git a/managed/models/agent_model_test.go b/managed/models/agent_model_test.go index a8a09de5ad..a17b44f26c 100644 --- a/managed/models/agent_model_test.go +++ b/managed/models/agent_model_test.go @@ -66,15 +66,15 @@ func TestAgent(t *testing.T) { } { t.Run(string(typ), func(t *testing.T) { agent.AgentType = typ - assert.Equal(t, expected, agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: "database"}, nil)) + assert.Equal(t, expected, agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: "database"}, nil, nil)) }) } t.Run("MongoDBNoDatabase", func(t *testing.T) { agent.AgentType = models.MongoDBExporterType - assert.Equal(t, "mongodb://username:s3cur3%20p%40$$w0r4.@1.2.3.4:12345/?connectTimeoutMS=1000&directConnection=true&serverSelectionTimeoutMS=1000", agent.DSN(service, models.DSNParams{DialTimeout: time.Second}, nil)) - assert.Equal(t, "mongodb://username:s3cur3%20p%40$$w0r4.@1.2.3.4:12345/?directConnection=true", agent.DSN(service, models.DSNParams{}, nil)) + assert.Equal(t, "mongodb://username:s3cur3%20p%40$$w0r4.@1.2.3.4:12345/?connectTimeoutMS=1000&directConnection=true&serverSelectionTimeoutMS=1000", agent.DSN(service, models.DSNParams{DialTimeout: time.Second}, nil, nil)) + assert.Equal(t, "mongodb://username:s3cur3%20p%40$$w0r4.@1.2.3.4:12345/?directConnection=true", agent.DSN(service, models.DSNParams{}, nil, nil)) }) }) @@ -94,7 +94,7 @@ func TestAgent(t *testing.T) { } { t.Run(string(typ), func(t *testing.T) { agent.AgentType = typ - assert.Equal(t, expected, agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: "database"}, nil)) + assert.Equal(t, expected, agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: "database"}, nil, nil)) }) } }) @@ -113,7 +113,7 @@ func TestAgent(t *testing.T) { } { t.Run(string(typ), func(t *testing.T) { agent.AgentType = typ - assert.Equal(t, expected, agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: "database"}, nil)) + assert.Equal(t, expected, agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: "database"}, nil, nil)) }) } }) @@ -159,7 +159,7 @@ func TestAgent(t *testing.T) { } { t.Run(string(typ), func(t *testing.T) { agent.AgentType = typ - assert.Equal(t, expected, agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: "database"}, nil)) + assert.Equal(t, expected, agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: "database"}, nil, nil)) }) } @@ -169,8 +169,8 @@ func TestAgent(t *testing.T) { agent.MongoDBOptions.TLSCertificateKeyFilePassword = "" agent.MongoDBOptions.AuthenticationMechanism = "" - assert.Equal(t, "mongodb://username:s3cur3%20p%40$$w0r4.@1.2.3.4:12345/?connectTimeoutMS=1000&directConnection=true&serverSelectionTimeoutMS=1000&ssl=true&tlsCaFile={{.TextFiles.caFilePlaceholder}}&tlsCertificateKeyFile={{.TextFiles.certificateKeyFilePlaceholder}}", agent.DSN(service, models.DSNParams{DialTimeout: time.Second}, nil)) - assert.Equal(t, "mongodb://username:s3cur3%20p%40$$w0r4.@1.2.3.4:12345/?directConnection=true&ssl=true&tlsCaFile={{.TextFiles.caFilePlaceholder}}&tlsCertificateKeyFile={{.TextFiles.certificateKeyFilePlaceholder}}", agent.DSN(service, models.DSNParams{}, nil)) + assert.Equal(t, "mongodb://username:s3cur3%20p%40$$w0r4.@1.2.3.4:12345/?connectTimeoutMS=1000&directConnection=true&serverSelectionTimeoutMS=1000&ssl=true&tlsCaFile={{.TextFiles.caFilePlaceholder}}&tlsCertificateKeyFile={{.TextFiles.certificateKeyFilePlaceholder}}", agent.DSN(service, models.DSNParams{DialTimeout: time.Second}, nil, nil)) + assert.Equal(t, "mongodb://username:s3cur3%20p%40$$w0r4.@1.2.3.4:12345/?directConnection=true&ssl=true&tlsCaFile={{.TextFiles.caFilePlaceholder}}&tlsCertificateKeyFile={{.TextFiles.certificateKeyFilePlaceholder}}", agent.DSN(service, models.DSNParams{}, nil, nil)) expectedFiles := map[string]string{ "caFilePlaceholder": "cert", "certificateKeyFilePlaceholder": "key", @@ -185,8 +185,8 @@ func TestAgent(t *testing.T) { agent.MongoDBOptions.AuthenticationMechanism = "MONGO-X509" agent.MongoDBOptions.AuthenticationDatabase = "$external" - assert.Equal(t, "mongodb://username:s3cur3%20p%40$$w0r4.@1.2.3.4:12345/?authMechanism=MONGO-X509&authSource=%24external&connectTimeoutMS=1000&directConnection=true&serverSelectionTimeoutMS=1000&ssl=true&tlsCaFile={{.TextFiles.caFilePlaceholder}}&tlsCertificateKeyFile={{.TextFiles.certificateKeyFilePlaceholder}}", agent.DSN(service, models.DSNParams{DialTimeout: time.Second}, nil)) - assert.Equal(t, "mongodb://username:s3cur3%20p%40$$w0r4.@1.2.3.4:12345/?authMechanism=MONGO-X509&authSource=%24external&directConnection=true&ssl=true&tlsCaFile={{.TextFiles.caFilePlaceholder}}&tlsCertificateKeyFile={{.TextFiles.certificateKeyFilePlaceholder}}", agent.DSN(service, models.DSNParams{}, nil)) + assert.Equal(t, "mongodb://username:s3cur3%20p%40$$w0r4.@1.2.3.4:12345/?authMechanism=MONGO-X509&authSource=%24external&connectTimeoutMS=1000&directConnection=true&serverSelectionTimeoutMS=1000&ssl=true&tlsCaFile={{.TextFiles.caFilePlaceholder}}&tlsCertificateKeyFile={{.TextFiles.certificateKeyFilePlaceholder}}", agent.DSN(service, models.DSNParams{DialTimeout: time.Second}, nil, nil)) + assert.Equal(t, "mongodb://username:s3cur3%20p%40$$w0r4.@1.2.3.4:12345/?authMechanism=MONGO-X509&authSource=%24external&directConnection=true&ssl=true&tlsCaFile={{.TextFiles.caFilePlaceholder}}&tlsCertificateKeyFile={{.TextFiles.certificateKeyFilePlaceholder}}", agent.DSN(service, models.DSNParams{}, nil, nil)) expectedFiles := map[string]string{ "caFilePlaceholder": "cert", "certificateKeyFilePlaceholder": "key", @@ -217,15 +217,15 @@ func TestAgent(t *testing.T) { } { t.Run(string(typ), func(t *testing.T) { agent.AgentType = typ - assert.Equal(t, expected, agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: "database"}, nil)) + assert.Equal(t, expected, agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: "database"}, nil, nil)) }) } t.Run("MongoDBNoDatabase", func(t *testing.T) { agent.AgentType = models.MongoDBExporterType - assert.Equal(t, "mongodb://username:s3cur3%20p%40$$w0r4.@1.2.3.4:12345/?connectTimeoutMS=1000&directConnection=true&serverSelectionTimeoutMS=1000&ssl=true&tlsInsecure=true", agent.DSN(service, models.DSNParams{DialTimeout: time.Second}, nil)) - assert.Equal(t, "mongodb://username:s3cur3%20p%40$$w0r4.@1.2.3.4:12345/?directConnection=true&ssl=true&tlsInsecure=true", agent.DSN(service, models.DSNParams{}, nil)) + assert.Equal(t, "mongodb://username:s3cur3%20p%40$$w0r4.@1.2.3.4:12345/?connectTimeoutMS=1000&directConnection=true&serverSelectionTimeoutMS=1000&ssl=true&tlsInsecure=true", agent.DSN(service, models.DSNParams{DialTimeout: time.Second}, nil, nil)) + assert.Equal(t, "mongodb://username:s3cur3%20p%40$$w0r4.@1.2.3.4:12345/?directConnection=true&ssl=true&tlsInsecure=true", agent.DSN(service, models.DSNParams{}, nil, nil)) }) }) } @@ -255,13 +255,13 @@ func TestPostgresAgentTLS(t *testing.T) { t.Run(name, func(t *testing.T) { agent.TLS = testCase.tls agent.TLSSkipVerify = testCase.tlsSkipVerify - assert.Equal(t, testCase.expected, agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: "database"}, nil)) + assert.Equal(t, testCase.expected, agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: "database"}, nil, nil)) }) t.Run(fmt.Sprintf("AutodiscoveryLimit set TLS:%v/TLSSkipVerify:%v", testCase.tls, testCase.tlsSkipVerify), func(t *testing.T) { agent.TLS = testCase.tls agent.TLSSkipVerify = testCase.tlsSkipVerify agent.PostgreSQLOptions = &models.PostgreSQLOptions{AutoDiscoveryLimit: 10} - assert.Equal(t, testCase.expected, agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: "database"}, nil)) + assert.Equal(t, testCase.expected, agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: "database"}, nil, nil)) }) } } @@ -278,7 +278,7 @@ func TestPostgresWithSocket(t *testing.T) { Socket: pointer.ToString("/var/run/postgres"), } expect := "postgres://username@/database?connect_timeout=1&host=%2Fvar%2Frun%2Fpostgres&sslmode=verify-ca" - assert.Equal(t, expect, agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: "database"}, nil)) + assert.Equal(t, expect, agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: "database"}, nil, nil)) }) t.Run("empty-user-password", func(t *testing.T) { @@ -289,7 +289,7 @@ func TestPostgresWithSocket(t *testing.T) { Socket: pointer.ToString("/var/run/postgres"), } expect := "postgres:///database?connect_timeout=1&host=%2Fvar%2Frun%2Fpostgres&sslmode=disable" - assert.Equal(t, expect, agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: "database"}, nil)) + assert.Equal(t, expect, agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: "database"}, nil, nil)) }) t.Run("dir-with-symbols", func(t *testing.T) { @@ -300,7 +300,7 @@ func TestPostgresWithSocket(t *testing.T) { Socket: pointer.ToString(`/tmp/123\ A0m\%\$\@\8\,\+\-`), } expect := "postgres:///database?connect_timeout=1&host=%2Ftmp%2F123%5C+A0m%5C%25%5C%24%5C%40%5C8%5C%2C%5C%2B%5C-&sslmode=disable" - assert.Equal(t, expect, agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: "database"}, nil)) + assert.Equal(t, expect, agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: "database"}, nil, nil)) }) } @@ -316,7 +316,7 @@ func TestMongoWithSocket(t *testing.T) { Socket: pointer.ToString("/tmp/mongodb-27017.sock"), } expect := "mongodb://username@%2Ftmp%2Fmongodb-27017.sock/database?connectTimeoutMS=1000&directConnection=true&serverSelectionTimeoutMS=1000&ssl=true" - assert.Equal(t, expect, agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: "database"}, nil)) + assert.Equal(t, expect, agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: "database"}, nil, nil)) }) t.Run("empty-user-password", func(t *testing.T) { @@ -327,7 +327,7 @@ func TestMongoWithSocket(t *testing.T) { Socket: pointer.ToString("/tmp/mongodb-27017.sock"), } expect := "mongodb://%2Ftmp%2Fmongodb-27017.sock/database?connectTimeoutMS=1000&directConnection=true&serverSelectionTimeoutMS=1000" - assert.Equal(t, expect, agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: "database"}, nil)) + assert.Equal(t, expect, agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: "database"}, nil, nil)) }) t.Run("dir-with-symbols", func(t *testing.T) { @@ -338,7 +338,7 @@ func TestMongoWithSocket(t *testing.T) { Socket: pointer.ToString(`/tmp/123\ A0m\%\$\@\8\,\+\-/mongodb-27017.sock`), } expect := "mongodb://%2Ftmp%2F123%5C%20A0m%5C%25%5C$%5C%40%5C8%5C,%5C+%5C-%2Fmongodb-27017.sock/database?connectTimeoutMS=1000&directConnection=true&serverSelectionTimeoutMS=1000" - assert.Equal(t, expect, agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: "database"}, nil)) + assert.Equal(t, expect, agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: "database"}, nil, nil)) }) } diff --git a/managed/models/dsn_helpers.go b/managed/models/dsn_helpers.go index d4d6112262..a8d47399d0 100644 --- a/managed/models/dsn_helpers.go +++ b/managed/models/dsn_helpers.go @@ -86,7 +86,8 @@ func FindDSNByServiceIDandPMMAgentID(q *reform.Querier, serviceID, pmmAgentID, d } if len(fexp) == 1 { agent := fexp[0] - return agent.DSN(svc, dsnParams, nil), agent, nil + pmmAgentVersion := ExtractPmmAgentVersionFromAgent(q, agent) + return agent.DSN(svc, dsnParams, nil, pmmAgentVersion), agent, nil } if len(fexp) > 1 { return "", nil, status.Errorf(codes.FailedPrecondition, "Couldn't resolve dsn, as there should be only one agent") diff --git a/managed/services/agents/connection_checker.go b/managed/services/agents/connection_checker.go index 9f695d8a8a..402ad52d18 100644 --- a/managed/services/agents/connection_checker.go +++ b/managed/services/agents/connection_checker.go @@ -137,12 +137,14 @@ func (c *ConnectionChecker) CheckConnectionToService(ctx context.Context, q *ref func connectionRequest(q *reform.Querier, service *models.Service, agent *models.Agent) (*agentpb.CheckConnectionRequest, error) { var request *agentpb.CheckConnectionRequest + + pmmAgentVersion := models.ExtractPmmAgentVersionFromAgent(q, agent) switch service.ServiceType { case models.MySQLServiceType: tdp := agent.TemplateDelimiters(service) request = &agentpb.CheckConnectionRequest{ Type: inventorypb.ServiceType_MYSQL_SERVICE, - Dsn: agent.DSN(service, models.DSNParams{DialTimeout: 2 * time.Second, Database: service.DatabaseName}, nil), + Dsn: agent.DSN(service, models.DSNParams{DialTimeout: 2 * time.Second, Database: service.DatabaseName}, nil, pmmAgentVersion), Timeout: durationpb.New(3 * time.Second), TextFiles: &agentpb.TextFiles{ Files: agent.Files(), @@ -158,8 +160,9 @@ func connectionRequest(q *reform.Querier, service *models.Service, agent *models return nil, err } request = &agentpb.CheckConnectionRequest{ - Type: inventorypb.ServiceType_POSTGRESQL_SERVICE, - Dsn: agent.DSN(service, models.DSNParams{DialTimeout: 2 * time.Second, Database: service.DatabaseName, PostgreSQLSupportsSSLSNI: sqlSniSupported}, nil), + Type: inventorypb.ServiceType_POSTGRESQL_SERVICE, + Dsn: agent.DSN(service, models.DSNParams{DialTimeout: 2 * time.Second, Database: service.DatabaseName, PostgreSQLSupportsSSLSNI: sqlSniSupported}, + nil, pmmAgentVersion), Timeout: durationpb.New(3 * time.Second), TextFiles: &agentpb.TextFiles{ Files: agent.Files(), @@ -171,7 +174,7 @@ func connectionRequest(q *reform.Querier, service *models.Service, agent *models tdp := agent.TemplateDelimiters(service) request = &agentpb.CheckConnectionRequest{ Type: inventorypb.ServiceType_MONGODB_SERVICE, - Dsn: agent.DSN(service, models.DSNParams{DialTimeout: 2 * time.Second, Database: service.DatabaseName}, nil), + Dsn: agent.DSN(service, models.DSNParams{DialTimeout: 2 * time.Second, Database: service.DatabaseName}, nil, pmmAgentVersion), Timeout: durationpb.New(3 * time.Second), TextFiles: &agentpb.TextFiles{ Files: agent.Files(), @@ -182,7 +185,7 @@ func connectionRequest(q *reform.Querier, service *models.Service, agent *models case models.ProxySQLServiceType: request = &agentpb.CheckConnectionRequest{ Type: inventorypb.ServiceType_PROXYSQL_SERVICE, - Dsn: agent.DSN(service, models.DSNParams{DialTimeout: 2 * time.Second, Database: service.DatabaseName}, nil), + Dsn: agent.DSN(service, models.DSNParams{DialTimeout: 2 * time.Second, Database: service.DatabaseName}, nil, pmmAgentVersion), Timeout: durationpb.New(3 * time.Second), } case models.ExternalServiceType: diff --git a/managed/services/agents/mongodb.go b/managed/services/agents/mongodb.go index 3ff1983593..0e043e3904 100644 --- a/managed/services/agents/mongodb.go +++ b/managed/services/agents/mongodb.go @@ -78,7 +78,7 @@ func mongodbExporterConfig(node *models.Node, service *models.Service, exporter database = exporter.MongoDBOptions.AuthenticationDatabase } env := []string{ - fmt.Sprintf("MONGODB_URI=%s", exporter.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: database}, tdp)), + fmt.Sprintf("MONGODB_URI=%s", exporter.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: database}, tdp, pmmAgentVersion)), } res := &agentpb.SetStateRequest_AgentProcess{ @@ -270,11 +270,11 @@ func defaultCollectors(collectAll bool) map[string]collectorArgs { } // qanMongoDBProfilerAgentConfig returns desired configuration of qan-mongodb-profiler-agent built-in agent. -func qanMongoDBProfilerAgentConfig(service *models.Service, agent *models.Agent) *agentpb.SetStateRequest_BuiltinAgent { +func qanMongoDBProfilerAgentConfig(service *models.Service, agent *models.Agent, pmmAgentVersion *version.Parsed) *agentpb.SetStateRequest_BuiltinAgent { tdp := agent.TemplateDelimiters(service) return &agentpb.SetStateRequest_BuiltinAgent{ Type: inventorypb.AgentType_QAN_MONGODB_PROFILER_AGENT, - Dsn: agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: ""}, nil), + Dsn: agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: ""}, nil, pmmAgentVersion), DisableQueryExamples: agent.QueryExamplesDisabled, MaxQueryLength: agent.MaxQueryLength, TextFiles: &agentpb.TextFiles{ diff --git a/managed/services/agents/mysql.go b/managed/services/agents/mysql.go index a7d9487ff2..e3e1c377e6 100644 --- a/managed/services/agents/mysql.go +++ b/managed/services/agents/mysql.go @@ -141,7 +141,7 @@ func mysqldExporterConfig( TemplateRightDelim: tdp.Right, Args: args, Env: []string{ - fmt.Sprintf("DATA_SOURCE_NAME=%s", exporter.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: ""}, nil)), + fmt.Sprintf("DATA_SOURCE_NAME=%s", exporter.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: ""}, nil, pmmAgentVersion)), fmt.Sprintf("HTTP_AUTH=pmm:%s", exporter.GetAgentPassword()), }, TextFiles: exporter.Files(), @@ -153,11 +153,11 @@ func mysqldExporterConfig( } // qanMySQLPerfSchemaAgentConfig returns desired configuration of qan-mysql-perfschema built-in agent. -func qanMySQLPerfSchemaAgentConfig(service *models.Service, agent *models.Agent) *agentpb.SetStateRequest_BuiltinAgent { +func qanMySQLPerfSchemaAgentConfig(service *models.Service, agent *models.Agent, pmmAgentVersion *version.Parsed) *agentpb.SetStateRequest_BuiltinAgent { tdp := agent.TemplateDelimiters(service) return &agentpb.SetStateRequest_BuiltinAgent{ Type: inventorypb.AgentType_QAN_MYSQL_PERFSCHEMA_AGENT, - Dsn: agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: ""}, nil), + Dsn: agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: ""}, nil, pmmAgentVersion), MaxQueryLength: agent.MaxQueryLength, DisableQueryExamples: agent.QueryExamplesDisabled, DisableCommentsParsing: agent.CommentsParsingDisabled, @@ -171,11 +171,11 @@ func qanMySQLPerfSchemaAgentConfig(service *models.Service, agent *models.Agent) } // qanMySQLSlowlogAgentConfig returns desired configuration of qan-mysql-slowlog built-in agent. -func qanMySQLSlowlogAgentConfig(service *models.Service, agent *models.Agent) *agentpb.SetStateRequest_BuiltinAgent { +func qanMySQLSlowlogAgentConfig(service *models.Service, agent *models.Agent, pmmAgentVersion *version.Parsed) *agentpb.SetStateRequest_BuiltinAgent { tdp := agent.TemplateDelimiters(service) return &agentpb.SetStateRequest_BuiltinAgent{ Type: inventorypb.AgentType_QAN_MYSQL_SLOWLOG_AGENT, - Dsn: agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: ""}, nil), + Dsn: agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: ""}, nil, pmmAgentVersion), MaxQueryLength: agent.MaxQueryLength, DisableQueryExamples: agent.QueryExamplesDisabled, DisableCommentsParsing: agent.CommentsParsingDisabled, diff --git a/managed/services/agents/postgresql.go b/managed/services/agents/postgresql.go index 33523d830e..73575eafed 100644 --- a/managed/services/agents/postgresql.go +++ b/managed/services/agents/postgresql.go @@ -121,7 +121,7 @@ func postgresExporterConfig(node *models.Node, service *models.Service, exporter TemplateRightDelim: tdp.Right, Args: args, Env: []string{ - fmt.Sprintf("DATA_SOURCE_NAME=%s", exporter.DSN(service, dnsParams, nil)), + fmt.Sprintf("DATA_SOURCE_NAME=%s", exporter.DSN(service, dnsParams, nil, pmmAgentVersion)), }, TextFiles: exporter.Files(), } @@ -147,7 +147,7 @@ func qanPostgreSQLPgStatementsAgentConfig(service *models.Service, agent *models } return &agentpb.SetStateRequest_BuiltinAgent{ Type: inventorypb.AgentType_QAN_POSTGRESQL_PGSTATEMENTS_AGENT, - Dsn: agent.DSN(service, dnsParams, nil), + Dsn: agent.DSN(service, dnsParams, nil, pmmAgentVersion), MaxQueryLength: agent.MaxQueryLength, DisableCommentsParsing: agent.CommentsParsingDisabled, TextFiles: &agentpb.TextFiles{ @@ -168,7 +168,7 @@ func qanPostgreSQLPgStatMonitorAgentConfig(service *models.Service, agent *model } return &agentpb.SetStateRequest_BuiltinAgent{ Type: inventorypb.AgentType_QAN_POSTGRESQL_PGSTATMONITOR_AGENT, - Dsn: agent.DSN(service, dnsParams, nil), + Dsn: agent.DSN(service, dnsParams, nil, pmmAgentVersion), DisableQueryExamples: agent.QueryExamplesDisabled, MaxQueryLength: agent.MaxQueryLength, DisableCommentsParsing: agent.CommentsParsingDisabled, diff --git a/managed/services/agents/proxysql.go b/managed/services/agents/proxysql.go index 383a27c0c6..9d66bbb906 100644 --- a/managed/services/agents/proxysql.go +++ b/managed/services/agents/proxysql.go @@ -73,7 +73,7 @@ func proxysqlExporterConfig(node *models.Node, service *models.Service, exporter TemplateRightDelim: tdp.Right, Args: args, Env: []string{ - fmt.Sprintf("DATA_SOURCE_NAME=%s", exporter.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: ""}, nil)), + fmt.Sprintf("DATA_SOURCE_NAME=%s", exporter.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: ""}, nil, pmmAgentVersion)), fmt.Sprintf("HTTP_AUTH=pmm:%s", exporter.GetAgentPassword()), }, } diff --git a/managed/services/agents/service_info_broker.go b/managed/services/agents/service_info_broker.go index 6f0041656a..890a647109 100644 --- a/managed/services/agents/service_info_broker.go +++ b/managed/services/agents/service_info_broker.go @@ -52,12 +52,14 @@ func NewServiceInfoBroker(r *Registry) *ServiceInfoBroker { // ServiceInfoRequest creates a ServiceInfoRequest for a given service. func serviceInfoRequest(q *reform.Querier, service *models.Service, agent *models.Agent) (*agentpb.ServiceInfoRequest, error) { var request *agentpb.ServiceInfoRequest + + pmmAgentVersion := models.ExtractPmmAgentVersionFromAgent(q, agent) switch service.ServiceType { case models.MySQLServiceType: tdp := agent.TemplateDelimiters(service) request = &agentpb.ServiceInfoRequest{ Type: inventorypb.ServiceType_MYSQL_SERVICE, - Dsn: agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: service.DatabaseName}, nil), + Dsn: agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: service.DatabaseName}, nil, pmmAgentVersion), Timeout: durationpb.New(3 * time.Second), TextFiles: &agentpb.TextFiles{ Files: agent.Files(), @@ -73,8 +75,9 @@ func serviceInfoRequest(q *reform.Querier, service *models.Service, agent *model return nil, err } request = &agentpb.ServiceInfoRequest{ - Type: inventorypb.ServiceType_POSTGRESQL_SERVICE, - Dsn: agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: service.DatabaseName, PostgreSQLSupportsSSLSNI: sqlSniSupported}, nil), + Type: inventorypb.ServiceType_POSTGRESQL_SERVICE, + Dsn: agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: service.DatabaseName, PostgreSQLSupportsSSLSNI: sqlSniSupported}, + nil, pmmAgentVersion), Timeout: durationpb.New(3 * time.Second), TextFiles: &agentpb.TextFiles{ Files: agent.Files(), @@ -86,7 +89,7 @@ func serviceInfoRequest(q *reform.Querier, service *models.Service, agent *model tdp := agent.TemplateDelimiters(service) request = &agentpb.ServiceInfoRequest{ Type: inventorypb.ServiceType_MONGODB_SERVICE, - Dsn: agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: service.DatabaseName}, nil), + Dsn: agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: service.DatabaseName}, nil, pmmAgentVersion), Timeout: durationpb.New(3 * time.Second), TextFiles: &agentpb.TextFiles{ Files: agent.Files(), @@ -97,7 +100,7 @@ func serviceInfoRequest(q *reform.Querier, service *models.Service, agent *model case models.ProxySQLServiceType: request = &agentpb.ServiceInfoRequest{ Type: inventorypb.ServiceType_PROXYSQL_SERVICE, - Dsn: agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: service.DatabaseName}, nil), + Dsn: agent.DSN(service, models.DSNParams{DialTimeout: time.Second, Database: service.DatabaseName}, nil, pmmAgentVersion), Timeout: durationpb.New(3 * time.Second), } case models.ExternalServiceType: diff --git a/managed/services/agents/state.go b/managed/services/agents/state.go index ed0c4e0ecd..0e55f14a97 100644 --- a/managed/services/agents/state.go +++ b/managed/services/agents/state.go @@ -245,11 +245,11 @@ func (u *StateUpdater) sendSetStateRequest(ctx context.Context, agent *pmmAgentI case models.ProxySQLExporterType: agentProcesses[row.AgentID] = proxysqlExporterConfig(node, service, row, redactMode, pmmAgentVersion) case models.QANMySQLPerfSchemaAgentType: - builtinAgents[row.AgentID] = qanMySQLPerfSchemaAgentConfig(service, row) + builtinAgents[row.AgentID] = qanMySQLPerfSchemaAgentConfig(service, row, pmmAgentVersion) case models.QANMySQLSlowlogAgentType: - builtinAgents[row.AgentID] = qanMySQLSlowlogAgentConfig(service, row) + builtinAgents[row.AgentID] = qanMySQLSlowlogAgentConfig(service, row, pmmAgentVersion) case models.QANMongoDBProfilerAgentType: - builtinAgents[row.AgentID] = qanMongoDBProfilerAgentConfig(service, row) + builtinAgents[row.AgentID] = qanMongoDBProfilerAgentConfig(service, row, pmmAgentVersion) case models.QANPostgreSQLPgStatementsAgentType: builtinAgents[row.AgentID] = qanPostgreSQLPgStatementsAgentConfig(service, row, pmmAgentVersion) case models.QANPostgreSQLPgStatMonitorAgentType: