From 7a4640e02663601a94f056de18aa70bd92f4931d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Nov 2023 14:45:12 +0200 Subject: [PATCH 01/15] Bump github.com/go-co-op/gocron from 1.35.1 to 1.36.0 (#2640) Bumps [github.com/go-co-op/gocron](https://github.com/go-co-op/gocron) from 1.35.1 to 1.36.0. - [Release notes](https://github.com/go-co-op/gocron/releases) - [Commits](https://github.com/go-co-op/gocron/compare/v1.35.1...v1.36.0) --- updated-dependencies: - dependency-name: github.com/go-co-op/gocron dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 6cfcb51155..acdbb3ee4f 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,7 @@ require ( github.com/docker/docker v24.0.6+incompatible github.com/docker/go-connections v0.4.0 github.com/envoyproxy/protoc-gen-validate v1.0.2 - github.com/go-co-op/gocron v1.35.1 + github.com/go-co-op/gocron v1.36.0 github.com/go-openapi/errors v0.20.4 github.com/go-openapi/runtime v0.26.0 github.com/go-openapi/strfmt v0.21.7 diff --git a/go.sum b/go.sum index 8b5205467b..833a7ededd 100644 --- a/go.sum +++ b/go.sum @@ -214,8 +214,8 @@ github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoD github.com/frankban/quicktest v1.5.0/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/go-co-op/gocron v1.35.1 h1:xi0tfAhxeAmGUKkjiA7bTIjh2VdBJpUYDJ+lPx/EPcM= -github.com/go-co-op/gocron v1.35.1/go.mod h1:NLi+bkm4rRSy1F8U7iacZOz0xPseMoIOnvabGoSe/no= +github.com/go-co-op/gocron v1.36.0 h1:sEmAwg57l4JWQgzaVWYfKZ+w13uHOqeOtwjo72Ll5Wc= +github.com/go-co-op/gocron v1.36.0/go.mod h1:3L/n6BkO7ABj+TrfSVXLRzsP26zmikL4ISkLQ0O8iNY= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-faster/city v1.0.1 h1:4WAxSZ3V2Ws4QRDrscLEDcibJY8uf41H6AhXDrNDcGw= @@ -403,7 +403,6 @@ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= From 66f6ecb43727650dd7a29dcca23e4303f00d6a61 Mon Sep 17 00:00:00 2001 From: Nurlan Moldomurov Date: Mon, 27 Nov 2023 13:32:01 +0300 Subject: [PATCH 02/15] PMM-12725 fix QAN pagination. (#2646) * PMM-12725 fix QAN pagination. * PMM-12725 Update clickhouse version. * PMM-12725 Fix QAN tests. * PMM-12725 Fix clickhouse tests. * PMM-12725 Remove debug info. --- qan-api2/Makefile | 4 ++-- qan-api2/db.go | 7 ++++--- qan-api2/db_test.go | 10 +++++----- qan-api2/main.go | 2 +- qan-api2/models/reporter.go | 2 +- qan-api2/services/analytics/profile_test.go | 16 ++++++---------- 6 files changed, 19 insertions(+), 22 deletions(-) diff --git a/qan-api2/Makefile b/qan-api2/Makefile index 8190540f7e..534b5828d0 100644 --- a/qan-api2/Makefile +++ b/qan-api2/Makefile @@ -31,11 +31,11 @@ install-race: ## Install qan-api2 binary with race detector go install -v -race ./... test-env-up: ## Start docker containers used for testing - docker run -d --platform=linux/amd64 --name pmm-clickhouse-test -p19000:9000 yandex/clickhouse-server:21.3.20 + docker run -d --platform=linux/amd64 --name pmm-clickhouse-test -p19000:9000 -p18123:8123 clickhouse/clickhouse-server:23.8.2.7 make test-env test-env-up-applem1: ## Start docker containers used for testing on Apple M1 and higher chips. - docker run -d --platform=linux/amd64 --name pmm-clickhouse-test -p19000:9000 clickhouse/clickhouse-server:head-alpine + docker run -d --platform=linux/arm64 --name pmm-clickhouse-test -p19000:9000 -p18123:8123 clickhouse/clickhouse-server:23.8.2.7 make test-env test-env: diff --git a/qan-api2/db.go b/qan-api2/db.go index d121ba6cb6..4e0f9d1646 100644 --- a/qan-api2/db.go +++ b/qan-api2/db.go @@ -123,17 +123,18 @@ func runMigrations(dsn string) error { } // DropOldPartition drops number of days old partitions of pmm.metrics in ClickHouse. -func DropOldPartition(db *sqlx.DB, days uint) { +func DropOldPartition(db *sqlx.DB, dbName string, days uint) { partitions := []string{} const query = ` SELECT DISTINCT partition FROM system.parts - WHERE toUInt32(partition) < toYYYYMMDD(now() - toIntervalDay(?)) ORDER BY partition + WHERE toUInt32(partition) < toYYYYMMDD(now() - toIntervalDay(?)) AND database = ? and visible = 1 ORDER BY partition ` err := db.Select( &partitions, query, - days) + days, + dbName) if err != nil { log.Printf("Select %d days old partitions of system.parts. Result: %v, Error: %v", days, partitions, err) return diff --git a/qan-api2/db_test.go b/qan-api2/db_test.go index fc06a5fd7d..841803965e 100644 --- a/qan-api2/db_test.go +++ b/qan-api2/db_test.go @@ -73,7 +73,7 @@ func cleanup() { func TestDropOldPartition(t *testing.T) { db := setup() - const query = `SELECT DISTINCT partition FROM system.parts WHERE database = 'pmm_test_parts' ORDER BY partition` + const query = `SELECT DISTINCT partition FROM system.parts WHERE database = 'pmm_test_parts' and visible = 1 ORDER BY partition` start := time.Now() // fixtures have two partition 20190101 and 20190102 @@ -85,12 +85,12 @@ func TestDropOldPartition(t *testing.T) { t.Run("no so old partition", func(t *testing.T) { partitions := []string{} days := daysNewestPartition + 1 - DropOldPartition(db, days) + DropOldPartition(db, "pmm_test_parts", days) err := db.Select( &partitions, query) require.NoError(t, err, "Unexpected error in selecting metrics partition") - require.Equal(t, 2, len(partitions), "No one patrition were truncated. Partition %+v, days %d", partitions, days) + require.Equal(t, 2, len(partitions), "No one partition were truncated. Partition %+v, days %d", partitions, days) assert.Equal(t, "20190101", partitions[0], "Newest partition was not truncated") assert.Equal(t, "20190102", partitions[1], "Oldest partition was not truncated") }) @@ -98,12 +98,12 @@ func TestDropOldPartition(t *testing.T) { t.Run("delete one day old partition", func(t *testing.T) { partitions := []string{} days := daysNewestPartition - DropOldPartition(db, days) + DropOldPartition(db, "pmm_test_parts", days) err := db.Select( &partitions, query) require.NoError(t, err, "Unexpected error in selecting metrics partition") - require.Equal(t, 1, len(partitions), "Only one partition left. Partition %+v, days %d", partitions, days) + require.Equal(t, 1, len(partitions), "Only one partition should left. Partition %+v, days %d", partitions, days) assert.Equal(t, "20190102", partitions[0], "Newest partition was not truncated") }) cleanup() diff --git a/qan-api2/main.go b/qan-api2/main.go index 5b151202e4..df5457964d 100644 --- a/qan-api2/main.go +++ b/qan-api2/main.go @@ -355,7 +355,7 @@ func main() { defer wg.Done() for { // Drop old partitions once in 24h. - DropOldPartition(db, *dataRetentionF) + DropOldPartition(db, *clickHouseDatabaseF, *dataRetentionF) select { case <-ctx.Done(): return diff --git a/qan-api2/models/reporter.go b/qan-api2/models/reporter.go index 4a5e463626..5091a00b52 100644 --- a/qan-api2/models/reporter.go +++ b/qan-api2/models/reporter.go @@ -77,7 +77,7 @@ SUM(num_queries) AS num_queries, SUM({{ $col }}) AS {{ $col }}, {{ end }} {{ end }} -rowNumberInAllBlocks() AS total_rows +count(DISTINCT dimension) AS total_rows FROM metrics WHERE period_start >= :period_start_from AND period_start <= :period_start_to {{ if .Search }} diff --git a/qan-api2/services/analytics/profile_test.go b/qan-api2/services/analytics/profile_test.go index 895a7f4dfd..95e22f1f81 100644 --- a/qan-api2/services/analytics/profile_test.go +++ b/qan-api2/services/analytics/profile_test.go @@ -175,13 +175,11 @@ func TestService_GetReport_Mix(t *testing.T) { mm models.Metrics } test := struct { - name string fields fields in *qanpb.ReportRequest want *qanpb.ReportReply wantErr bool }{ - "reverce_order", fields{rm: rm, mm: mm}, &qanpb.ReportRequest{ PeriodStartFrom: ×tamp.Timestamp{Seconds: t1.Unix()}, @@ -205,7 +203,7 @@ func TestService_GetReport_Mix(t *testing.T) { &want, false, } - t.Run(test.name, func(t *testing.T) { + t.Run("reverce_order", func(t *testing.T) { s := &Service{ rm: test.fields.rm, mm: test.fields.mm, @@ -216,7 +214,7 @@ func TestService_GetReport_Mix(t *testing.T) { t.Errorf("Service.GetReport() error = %v, wantErr %v", err, test.wantErr) return } - expectedJSON := getExpectedJSON(t, got, "../../test_data/TestService_GetReport_Mix_"+test.name+".json") + expectedJSON := getExpectedJSON(t, got, "../../test_data/TestService_GetReport_Mix_reverce_order.json") marshaler := jsonpb.Marshaler{Indent: "\t"} gotJSON, err := marshaler.MarshalToString(got) if err != nil { @@ -225,8 +223,7 @@ func TestService_GetReport_Mix(t *testing.T) { assert.JSONEq(t, string(expectedJSON), gotJSON) }) - test.name = "correct_load" - t.Run(test.name, func(t *testing.T) { + t.Run("correct_load", func(t *testing.T) { s := &Service{ rm: test.fields.rm, mm: test.fields.mm, @@ -236,7 +233,7 @@ func TestService_GetReport_Mix(t *testing.T) { t.Errorf("Service.GetReport() error = %v, wantErr %v", err, test.wantErr) return } - expectedJSON := getExpectedJSON(t, got, "../../test_data/TestService_GetReport_Mix_"+test.name+".json") + expectedJSON := getExpectedJSON(t, got, "../../test_data/TestService_GetReport_Mix_correct_load.json") marshaler := jsonpb.Marshaler{Indent: "\t"} gotJSON, err := marshaler.MarshalToString(got) if err != nil { @@ -245,8 +242,7 @@ func TestService_GetReport_Mix(t *testing.T) { assert.JSONEq(t, string(expectedJSON), string(gotJSON)) //nolint:unconvert //keeps converting automatically }) - test.name = "correct_latency" - t.Run(test.name, func(t *testing.T) { + t.Run("correct_latency", func(t *testing.T) { s := &Service{ rm: test.fields.rm, mm: test.fields.mm, @@ -257,7 +253,7 @@ func TestService_GetReport_Mix(t *testing.T) { t.Errorf("Service.GetReport() error = %v, wantErr %v", err, test.wantErr) return } - expectedJSON := getExpectedJSON(t, got, "../../test_data/TestService_GetReport_Mix_"+test.name+".json") + expectedJSON := getExpectedJSON(t, got, "../../test_data/TestService_GetReport_Mix_correct_latency.json") marshaler := jsonpb.Marshaler{Indent: "\t"} gotJSON, err := marshaler.MarshalToString(got) if err != nil { From 01d08b4c259f8d411a420d2032d6d7f3a24f659e Mon Sep 17 00:00:00 2001 From: Michael Okoko <10512379+idoqo@users.noreply.github.com> Date: Mon, 27 Nov 2023 15:20:32 +0100 Subject: [PATCH 03/15] PMM-12422 use public IP for exporters in pull mode (#2642) * use pmm agent for node IP lookup * re-order switch cases * make address selection more explicit * always expose in pull mode * update tests --- managed/services/agents/agents.go | 6 ++--- managed/services/agents/agents_test.go | 26 +++++++++++++++------- managed/services/agents/mongodb_test.go | 22 +++++++++--------- managed/services/agents/mysql_test.go | 6 ++--- managed/services/agents/node_test.go | 6 ++--- managed/services/agents/postgresql_test.go | 12 +++++----- managed/services/agents/proxysql_test.go | 8 +++---- managed/services/agents/state.go | 2 +- 8 files changed, 49 insertions(+), 39 deletions(-) diff --git a/managed/services/agents/agents.go b/managed/services/agents/agents.go index 0e89aca0ed..7d71672f7d 100644 --- a/managed/services/agents/agents.go +++ b/managed/services/agents/agents.go @@ -145,11 +145,11 @@ func ensureAuthParams(exporter *models.Agent, params *agentpb.SetStateRequest_Ag // getExporterListenAddress returns the appropriate listen address to use for a given exporter. func getExporterListenAddress(node *models.Node, exporter *models.Agent) string { switch { - case !exporter.PushMetrics && node != nil: - return node.Address case exporter.ExposeExporter: return "0.0.0.0" - default: + case exporter.PushMetrics: return "127.0.0.1" + default: + return "0.0.0.0" } } diff --git a/managed/services/agents/agents_test.go b/managed/services/agents/agents_test.go index a99612691e..d3cae56a65 100644 --- a/managed/services/agents/agents_test.go +++ b/managed/services/agents/agents_test.go @@ -51,33 +51,43 @@ func TestPathsBaseForDifferentVersions(t *testing.T) { } func TestGetExporterListenAddress(t *testing.T) { - t.Run("uses node address in pull mode", func(t *testing.T) { + t.Run("uses 127.0.0.1 in push mode", func(t *testing.T) { node := &models.Node{ Address: "1.2.3.4", } - exporter := &models.Agent{} + exporter := &models.Agent{ + PushMetrics: true, + } - assert.Equal(t, "1.2.3.4", getExporterListenAddress(node, exporter)) + assert.Equal(t, "127.0.0.1", getExporterListenAddress(node, exporter)) }) - t.Run("uses 127.0.0.1 in push mode", func(t *testing.T) { + t.Run("exposes exporter address when enabled in push mode", func(t *testing.T) { node := &models.Node{ Address: "1.2.3.4", } exporter := &models.Agent{ - PushMetrics: true, + PushMetrics: true, + ExposeExporter: true, } - assert.Equal(t, "127.0.0.1", getExporterListenAddress(node, exporter)) + assert.Equal(t, "0.0.0.0", getExporterListenAddress(node, exporter)) }) - t.Run("exposes exporter address when enabled", func(t *testing.T) { + t.Run("exposes exporter address when enabled in pull mode", func(t *testing.T) { node := &models.Node{ Address: "1.2.3.4", } exporter := &models.Agent{ - PushMetrics: true, + PushMetrics: false, ExposeExporter: true, } assert.Equal(t, "0.0.0.0", getExporterListenAddress(node, exporter)) }) + t.Run("exposes exporter address if node IP is unavailable in pull mode", func(t *testing.T) { + exporter := &models.Agent{ + PushMetrics: false, + } + + assert.Equal(t, "0.0.0.0", getExporterListenAddress(nil, exporter)) + }) } diff --git a/managed/services/agents/mongodb_test.go b/managed/services/agents/mongodb_test.go index 4c8ae1d56c..2f11b0940f 100644 --- a/managed/services/agents/mongodb_test.go +++ b/managed/services/agents/mongodb_test.go @@ -53,7 +53,7 @@ func TestMongodbExporterConfig225(t *testing.T) { "--compatible-mode", "--discovering-mode", "--mongodb.global-conn-pool", - "--web.listen-address=1.2.3.4:{{ .listen_port }}", + "--web.listen-address=0.0.0.0:{{ .listen_port }}", }, Env: []string{ "MONGODB_URI=mongodb://username:s3cur3%20p%40$$w0r4.@1.2.3.4:27017/?connectTimeoutMS=1000&directConnection=true&serverSelectionTimeoutMS=1000", @@ -78,7 +78,7 @@ func TestMongodbExporterConfig225(t *testing.T) { "--discovering-mode", "--mongodb.collstats-colls=col1,col2,col3", "--mongodb.global-conn-pool", - "--web.listen-address=1.2.3.4:{{ .listen_port }}", + "--web.listen-address=0.0.0.0:{{ .listen_port }}", } actual, err := mongodbExporterConfig(node, mongodb, exporter, exposeSecrets, pmmAgentVersion) require.NoError(t, err) @@ -113,7 +113,7 @@ func TestMongodbExporterConfig226(t *testing.T) { "--compatible-mode", "--discovering-mode", "--mongodb.global-conn-pool", - "--web.listen-address=1.2.3.4:{{ .listen_port }}", + "--web.listen-address=0.0.0.0:{{ .listen_port }}", }, Env: []string{ "MONGODB_URI=mongodb://username:s3cur3%20p%40$$w0r4.@1.2.3.4:27017/?connectTimeoutMS=1000&directConnection=true&serverSelectionTimeoutMS=1000", @@ -141,7 +141,7 @@ func TestMongodbExporterConfig226(t *testing.T) { "--mongodb.collstats-colls=col1,col2,col3", "--mongodb.global-conn-pool", "--mongodb.indexstats-colls=col1,col2,col3", - "--web.listen-address=1.2.3.4:{{ .listen_port }}", + "--web.listen-address=0.0.0.0:{{ .listen_port }}", } actual, err := mongodbExporterConfig(node, mongodb, exporter, exposeSecrets, pmmAgentVersion) require.NoError(t, err) @@ -168,7 +168,7 @@ func TestMongodbExporterConfig226(t *testing.T) { "--mongodb.collstats-colls=col1,col2,col3", "--mongodb.global-conn-pool", "--mongodb.indexstats-colls=col1,col2,col3", - "--web.listen-address=1.2.3.4:{{ .listen_port }}", + "--web.listen-address=0.0.0.0:{{ .listen_port }}", } actual, err := mongodbExporterConfig(node, mongodb, exporter, exposeSecrets, pmmAgentVersion) require.NoError(t, err) @@ -196,7 +196,7 @@ func TestMongodbExporterConfig226(t *testing.T) { "--mongodb.collstats-colls=db1.col1.one,db2.col2,db3", "--mongodb.global-conn-pool", "--mongodb.indexstats-colls=db1.col1.one,db2.col2,db3", - "--web.listen-address=1.2.3.4:{{ .listen_port }}", + "--web.listen-address=0.0.0.0:{{ .listen_port }}", } actual, err := mongodbExporterConfig(node, mongodb, exporter, exposeSecrets, pmmAgentVersion) require.NoError(t, err) @@ -223,7 +223,7 @@ func TestMongodbExporterConfig226(t *testing.T) { "--mongodb.collstats-colls=db1.col1.one,db2.col2,db3", "--mongodb.global-conn-pool", "--mongodb.indexstats-colls=db1.col1.one,db2.col2,db3", - "--web.listen-address=1.2.3.4:{{ .listen_port }}", + "--web.listen-address=0.0.0.0:{{ .listen_port }}", } actual, err := mongodbExporterConfig(node, mongodb, exporter, exposeSecrets, pmmAgentVersion) require.NoError(t, err) @@ -258,7 +258,7 @@ func TestMongodbExporterConfig(t *testing.T) { "--collect.topmetrics", "--no-collect.connpoolstats", "--no-collect.indexusage", - "--web.listen-address=1.2.3.4:{{ .listen_port }}", + "--web.listen-address=0.0.0.0:{{ .listen_port }}", }, Env: []string{ "MONGODB_URI=mongodb://username:s3cur3%20p%40$$w0r4.@1.2.3.4:27017/?connectTimeoutMS=1000&directConnection=true&serverSelectionTimeoutMS=1000", @@ -343,7 +343,7 @@ func TestMongodbExporterConfig(t *testing.T) { "--collect.database", "--no-collect.connpoolstats", "--no-collect.indexusage", - "--web.listen-address=1.2.3.4:{{ .listen_port }}", + "--web.listen-address=0.0.0.0:{{ .listen_port }}", }, } require.NoError(t, err) @@ -376,7 +376,7 @@ func TestNewMongodbExporterConfig(t *testing.T) { Args: []string{ "--compatible-mode", "--mongodb.global-conn-pool", - "--web.listen-address=1.2.3.4:{{ .listen_port }}", + "--web.listen-address=0.0.0.0:{{ .listen_port }}", }, Env: []string{ "MONGODB_URI=mongodb://username:s3cur3%20p%40$$w0r4.@1.2.3.4:27017/?connectTimeoutMS=1000&directConnection=true&serverSelectionTimeoutMS=1000", @@ -430,7 +430,7 @@ func TestMongodbExporterConfig228_WebConfigAuth(t *testing.T) { "--compatible-mode", "--discovering-mode", "--mongodb.global-conn-pool", - "--web.listen-address=1.2.3.4:{{ .listen_port }}", + "--web.listen-address=0.0.0.0:{{ .listen_port }}", "--web.config={{ .TextFiles.webConfigPlaceholder }}", } diff --git a/managed/services/agents/mysql_test.go b/managed/services/agents/mysql_test.go index 86c7af3f2c..c9ccd1801c 100644 --- a/managed/services/agents/mysql_test.go +++ b/managed/services/agents/mysql_test.go @@ -88,7 +88,7 @@ func TestMySQLdExporterConfig(t *testing.T) { "--exporter.global-conn-pool", "--exporter.max-idle-conns=3", "--exporter.max-open-conns=3", - "--web.listen-address=1.2.3.4:{{ .listen_port }}", + "--web.listen-address=0.0.0.0:{{ .listen_port }}", }, Env: []string{ "DATA_SOURCE_NAME=username:s3cur3 p@$$w0r4.@tcp(1.2.3.4:3306)/?timeout=1s", @@ -193,7 +193,7 @@ func TestMySQLdExporterConfigTablestatsGroupDisabled(t *testing.T) { "--mysql.ssl-ca-file={{ .TextFiles.tlsCa }}", "--mysql.ssl-cert-file={{ .TextFiles.tlsCert }}", "--mysql.ssl-key-file={{ .TextFiles.tlsKey }}", - "--web.listen-address=1.2.3.4:{{ .listen_port }}", + "--web.listen-address=0.0.0.0:{{ .listen_port }}", }, Env: []string{ "DATA_SOURCE_NAME=username:s3cur3 p@$$w0r4.@tcp(1.2.3.4:3306)/?timeout=1s&tls=custom", @@ -292,7 +292,7 @@ func TestMySQLdExporterConfigDisabledCollectors(t *testing.T) { "--exporter.global-conn-pool", "--exporter.max-idle-conns=3", "--exporter.max-open-conns=3", - "--web.listen-address=1.2.3.4:{{ .listen_port }}", + "--web.listen-address=0.0.0.0:{{ .listen_port }}", }, Env: []string{ "DATA_SOURCE_NAME=username:s3cur3 p@$$w0r4.@tcp(1.2.3.4:3306)/?timeout=1s", diff --git a/managed/services/agents/node_test.go b/managed/services/agents/node_test.go index d82350e0ec..9d0889b631 100644 --- a/managed/services/agents/node_test.go +++ b/managed/services/agents/node_test.go @@ -164,7 +164,7 @@ func TestNodeExporterConfig(t *testing.T) { "--no-collector.xfs", "--no-collector.zfs", "--web.disable-exporter-metrics", - "--web.listen-address=1.2.3.4:{{ .listen_port }}", + "--web.listen-address=0.0.0.0:{{ .listen_port }}", }, Env: []string{ "HTTP_AUTH=pmm:agent-id", @@ -247,7 +247,7 @@ func TestNodeExporterConfig(t *testing.T) { "--no-collector.xfs", "--no-collector.zfs", "--web.disable-exporter-metrics", - "--web.listen-address=:{{ .listen_port }}", + "--web.listen-address=0.0.0.0:{{ .listen_port }}", }, Env: []string{ "HTTP_AUTH=pmm:agent-id", @@ -282,7 +282,7 @@ func TestNodeExporterConfig(t *testing.T) { "--collector.textfile.directory.lr=" + pathsBase(agentVersion, "{{", "}}") + "/collectors/textfile-collector/low-resolution", "--collector.textfile.directory.mr=" + pathsBase(agentVersion, "{{", "}}") + "/collectors/textfile-collector/medium-resolution", "--web.disable-exporter-metrics", - "--web.listen-address=:{{ .listen_port }}", + "--web.listen-address=0.0.0.0:{{ .listen_port }}", }, Env: []string{ "HTTP_AUTH=pmm:agent-id", diff --git a/managed/services/agents/postgresql_test.go b/managed/services/agents/postgresql_test.go index 442f808458..d2206ccd8a 100644 --- a/managed/services/agents/postgresql_test.go +++ b/managed/services/agents/postgresql_test.go @@ -66,7 +66,7 @@ func (s *PostgresExporterConfigTestSuite) SetupTest() { "--collect.custom_query.lr.directory=" + pathsBase(s.pmmAgentVersion, "{{", "}}") + "/collectors/custom-queries/postgresql/low-resolution", "--collect.custom_query.mr", "--collect.custom_query.mr.directory=" + pathsBase(s.pmmAgentVersion, "{{", "}}") + "/collectors/custom-queries/postgresql/medium-resolution", - "--web.listen-address=1.2.3.4:{{ .listen_port }}", + "--web.listen-address=0.0.0.0:{{ .listen_port }}", }, Env: []string{ "DATA_SOURCE_NAME=postgres://username:s3cur3%20p%40$$w0r4.@1.2.3.4:5432/postgres?connect_timeout=1&sslmode=disable", @@ -167,7 +167,7 @@ func (s *PostgresExporterConfigTestSuite) TestDisabledCollectors() { "--collect.custom_query.lr.directory=" + pathsBase(s.pmmAgentVersion, "{{", "}}") + "/collectors/custom-queries/postgresql/low-resolution", "--collect.custom_query.mr", "--collect.custom_query.mr.directory=" + pathsBase(s.pmmAgentVersion, "{{", "}}") + "/collectors/custom-queries/postgresql/medium-resolution", - "--web.listen-address=1.2.3.4:{{ .listen_port }}", + "--web.listen-address=0.0.0.0:{{ .listen_port }}", }, } requireNoDuplicateFlags(s.T(), actual.Args) @@ -205,7 +205,7 @@ func TestAutoDiscovery(t *testing.T) { "--collect.custom_query.lr.directory=" + pathsBase(pmmAgentVersion, "{{", "}}") + "/collectors/custom-queries/postgresql/low-resolution", "--collect.custom_query.mr", "--collect.custom_query.mr.directory=" + pathsBase(pmmAgentVersion, "{{", "}}") + "/collectors/custom-queries/postgresql/medium-resolution", - "--web.listen-address=1.2.3.4:{{ .listen_port }}", + "--web.listen-address=0.0.0.0:{{ .listen_port }}", }, Env: []string{ "DATA_SOURCE_NAME=postgres://username:s3cur3%20p%40$$w0r4.@1.2.3.4:5432/postgres?connect_timeout=1&sslmode=disable", @@ -324,7 +324,7 @@ func (s *PostgresExporterConfigTestSuite) TestAzureTimeout() { "--collect.custom_query.mr", "--collect.custom_query.mr.directory=" + pathsBase(s.pmmAgentVersion, "{{", "}}") + "/collectors/custom-queries/postgresql/medium-resolution", "--exclude-databases=template0,template1,postgres,cloudsqladmin,pmm-managed-dev,azure_maintenance,rdsadmin", - "--web.listen-address=1.2.3.4:{{ .listen_port }}", + "--web.listen-address=0.0.0.0:{{ .listen_port }}", }, Env: []string{ "DATA_SOURCE_NAME=postgres://username:s3cur3%20p%40$$w0r4.@1.2.3.4:5432/postgres?connect_timeout=5&sslmode=disable", @@ -370,7 +370,7 @@ func (s *PostgresExporterConfigTestSuite) TestPrometheusWebConfig() { "--collect.custom_query.mr", "--collect.custom_query.mr.directory=" + pathsBase(s.pmmAgentVersion, "{{", "}}") + "/collectors/custom-queries/postgresql/medium-resolution", "--exclude-databases=template0,template1,postgres,cloudsqladmin,pmm-managed-dev,azure_maintenance,rdsadmin", - "--web.listen-address=1.2.3.4:{{ .listen_port }}", + "--web.listen-address=0.0.0.0:{{ .listen_port }}", "--web.config={{ .TextFiles.webConfigPlaceholder }}", }, Env: []string{ @@ -419,7 +419,7 @@ func (s *PostgresExporterConfigTestSuite) TestSSLSni() { "--collect.custom_query.mr", "--collect.custom_query.mr.directory=" + pathsBase(s.pmmAgentVersion, "{{", "}}") + "/collectors/custom-queries/postgresql/medium-resolution", "--exclude-databases=template0,template1,postgres,cloudsqladmin,pmm-managed-dev,azure_maintenance,rdsadmin", - "--web.listen-address=1.2.3.4:{{ .listen_port }}", + "--web.listen-address=0.0.0.0:{{ .listen_port }}", "--web.config={{ .TextFiles.webConfigPlaceholder }}", }, Env: []string{ diff --git a/managed/services/agents/proxysql_test.go b/managed/services/agents/proxysql_test.go index 5c979dfac5..7e608169ba 100644 --- a/managed/services/agents/proxysql_test.go +++ b/managed/services/agents/proxysql_test.go @@ -55,7 +55,7 @@ func TestProxySQLExporterConfig(t *testing.T) { "-collect.mysql_connection_pool", "-collect.mysql_status", "-collect.stats_memory_metrics", - "-web.listen-address=1.2.3.4:{{ .listen_port }}", + "-web.listen-address=0.0.0.0:{{ .listen_port }}", }, Env: []string{ "DATA_SOURCE_NAME=username:s3cur3 p@$$w0r4.@tcp(1.2.3.4:3306)/?timeout=1s", @@ -89,7 +89,7 @@ func TestProxySQLExporterConfig(t *testing.T) { Args: []string{ "-collect.mysql_connection_pool", "-collect.mysql_status", - "-web.listen-address=1.2.3.4:{{ .listen_port }}", + "-web.listen-address=0.0.0.0:{{ .listen_port }}", }, } require.Equal(t, expected.Args, actual.Args) @@ -120,7 +120,7 @@ func TestProxySQLExporterConfig(t *testing.T) { "-collect.mysql_status", "-collect.stats_command_counter", "-collect.stats_memory_metrics", - "-web.listen-address=1.2.3.4:{{ .listen_port }}", + "-web.listen-address=0.0.0.0:{{ .listen_port }}", }, Env: []string{ "DATA_SOURCE_NAME=username:s3cur3 p@$$w0r4.@tcp(1.2.3.4:3306)/?timeout=1s", @@ -161,7 +161,7 @@ func TestProxySQLExporterConfig(t *testing.T) { "-collect.runtime_mysql_servers", "-collect.stats_command_counter", "-collect.stats_memory_metrics", - "-web.listen-address=1.2.3.4:{{ .listen_port }}", + "-web.listen-address=0.0.0.0:{{ .listen_port }}", }, Env: []string{ "DATA_SOURCE_NAME=username:s3cur3 p@$$w0r4.@tcp(1.2.3.4:3306)/?timeout=1s", diff --git a/managed/services/agents/state.go b/managed/services/agents/state.go index 49ad75ecbb..f2d840a6a5 100644 --- a/managed/services/agents/state.go +++ b/managed/services/agents/state.go @@ -226,7 +226,7 @@ func (u *StateUpdater) sendSetStateRequest(ctx context.Context, agent *pmmAgentI if err != nil { return err } - node, _ := models.FindNodeByID(u.db.Querier, pointer.GetString(row.NodeID)) + node, _ := models.FindNodeByID(u.db.Querier, pointer.GetString(pmmAgent.RunsOnNodeID)) switch row.AgentType { //nolint:exhaustive case models.MySQLdExporterType: agentProcesses[row.AgentID] = mysqldExporterConfig(node, service, row, redactMode, pmmAgentVersion) From cde90c96ea0270daf48f808ec321e238472e29e4 Mon Sep 17 00:00:00 2001 From: Nurlan Moldomurov Date: Mon, 27 Nov 2023 19:23:17 +0300 Subject: [PATCH 04/15] PMM-12733 Fix backup support for old PMM clients. (#2651) --- managed/services/backup/backup_service.go | 2 +- managed/services/backup/compatibility_service.go | 2 +- managed/services/management/backup/backups_service.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/managed/services/backup/backup_service.go b/managed/services/backup/backup_service.go index 2eaa58ad40..f94549ed26 100644 --- a/managed/services/backup/backup_service.go +++ b/managed/services/backup/backup_service.go @@ -226,7 +226,7 @@ func (s *Service) PerformBackup(ctx context.Context, params PerformBackupParams) err = status.Errorf(codes.Unknown, "Unknown service: %s", svc.ServiceType) } if err != nil { - var target *models.AgentNotSupportedError + var target models.AgentNotSupportedError if errors.As(err, &target) { _, dbErr := models.UpdateArtifact(s.db.Querier, artifact.ID, models.UpdateArtifactParams{ Status: models.ErrorBackupStatus.Pointer(), diff --git a/managed/services/backup/compatibility_service.go b/managed/services/backup/compatibility_service.go index c37cf10cef..ffb0dbded7 100644 --- a/managed/services/backup/compatibility_service.go +++ b/managed/services/backup/compatibility_service.go @@ -161,7 +161,7 @@ func (s *CompatibilityService) CheckSoftwareCompatibilityForService(ctx context. if serviceModel.ServiceType == models.MongoDBServiceType { if err := models.PMMAgentSupported(s.db.Querier, agentModel.AgentID, "get mongodb backup software versions", pmmAgentMinVersionForMongoBackupSoftwareCheck); err != nil { - var agentNotSupportedError *models.AgentNotSupportedError + var agentNotSupportedError models.AgentNotSupportedError if errors.As(err, &agentNotSupportedError) { s.l.Warnf("Got versioner error message: %s.", err.Error()) return "", nil diff --git a/managed/services/management/backup/backups_service.go b/managed/services/management/backup/backups_service.go index cc91f1beb8..eb5492983e 100644 --- a/managed/services/management/backup/backups_service.go +++ b/managed/services/management/backup/backups_service.go @@ -652,7 +652,7 @@ func convertError(e error) error { return nil } - var unsupportedAgentErr *models.AgentNotSupportedError + var unsupportedAgentErr models.AgentNotSupportedError if errors.As(e, &unsupportedAgentErr) { return status.Error(codes.FailedPrecondition, e.Error()) } From 864289441842b297fd5d099042630c00cd140f9e Mon Sep 17 00:00:00 2001 From: Nurlan Moldomurov Date: Mon, 27 Nov 2023 19:24:04 +0300 Subject: [PATCH 05/15] PMM-12730 Fix docker-way upgrade for < 2.22.0. (#2650) * PMM-12730 Fix docker-way upgrade for < 2.22.0. * PMM-12730 Fix docker-way upgrade for < 2.22.0. * PMM-12730 Fix docker-way upgrade for < 2.22.0. --- .../tasks/roles/clickhouse/tasks/main.yml | 16 ---------- .../roles/dashboards_upgrade/tasks/main.yml | 7 +--- .../tasks/roles/initialization/tasks/main.yml | 32 +++++++++++++++++-- 3 files changed, 30 insertions(+), 25 deletions(-) diff --git a/update/ansible/playbook/tasks/roles/clickhouse/tasks/main.yml b/update/ansible/playbook/tasks/roles/clickhouse/tasks/main.yml index d5fa9f845e..aef5604d30 100644 --- a/update/ansible/playbook/tasks/roles/clickhouse/tasks/main.yml +++ b/update/ansible/playbook/tasks/roles/clickhouse/tasks/main.yml @@ -48,22 +48,6 @@ enabled: no ignore_errors: true -# This will implicitly create /srv/clickhouse -- name: Create clickhouse data directory - file: - path: "/srv/clickhouse/flags" - state: directory - owner: root - group: pmm - recurse: true - -- name: Create empty file to convert clickhouse databases from ordinary to atomic - file: - path: "/srv/clickhouse/flags/convert_ordinary_to_atomic" - state: touch - owner: root - group: pmm - - name: Add ClickHouse repository yum_repository: name: clickhouse diff --git a/update/ansible/playbook/tasks/roles/dashboards_upgrade/tasks/main.yml b/update/ansible/playbook/tasks/roles/dashboards_upgrade/tasks/main.yml index 7c5080cfa9..1a635faf8a 100644 --- a/update/ansible/playbook/tasks/roles/dashboards_upgrade/tasks/main.yml +++ b/update/ansible/playbook/tasks/roles/dashboards_upgrade/tasks/main.yml @@ -106,6 +106,7 @@ - name: Remove the old clickhouse plugin shell: grafana cli --pluginsDir /srv/grafana/plugins plugins remove vertamedia-clickhouse-datasource || true + when: not ansible_check_mode - name: Restart grafana with new plugins EL7 supervisorctl: @@ -127,9 +128,3 @@ ignore_errors: true # TODO: fix the race condition. # We generate grafana supervisor config in pmm-managed and it may not exist at this stage - -- name: Copy file with image version - copy: - src: /usr/share/percona-dashboards/VERSION - dest: /srv/grafana/PERCONA_DASHBOARDS_VERSION - remote_src: yes diff --git a/update/ansible/playbook/tasks/roles/initialization/tasks/main.yml b/update/ansible/playbook/tasks/roles/initialization/tasks/main.yml index 021b246b2b..b059f399eb 100644 --- a/update/ansible/playbook/tasks/roles/initialization/tasks/main.yml +++ b/update/ansible/playbook/tasks/roles/initialization/tasks/main.yml @@ -69,6 +69,29 @@ become: true changed_when: True +- name: Update (both) + block: + # This will implicitly create /srv/clickhouse + - name: Create clickhouse data directory + file: + path: "/srv/clickhouse/flags" + state: directory + owner: root + group: pmm + recurse: true + + - name: Create empty file to convert clickhouse databases from ordinary to atomic + file: + path: "/srv/clickhouse/flags/convert_ordinary_to_atomic" + state: touch + owner: root + group: pmm + + - name: Upgrade dashboards + include_role: + name: dashboards_upgrade + when: not pmm_current_version is version(pmm_image_version, '>=') + - name: Create backup directory file: path: /srv/backup @@ -172,9 +195,12 @@ when: docker_upgrade -- name: Check if we need an update or not - include_role: - name: dashboards_upgrade +- name: Copy file with image version + copy: + src: /usr/share/percona-dashboards/VERSION + dest: /srv/grafana/PERCONA_DASHBOARDS_VERSION + owner: grafana + remote_src: yes when: not pmm_current_version is version(pmm_image_version, '>=') - name: Finalization From ffcbe130ade8bf3797680c075c908d06e3207765 Mon Sep 17 00:00:00 2001 From: Nurlan Moldomurov Date: Mon, 27 Nov 2023 19:58:44 +0300 Subject: [PATCH 06/15] PMM-12078 HA phase 3 active active PoC (#2247) * PMM-9374 support of external victoria metrics. * PMM-9374 update version number of victoria metrics. * PMM-9374 vmalert. * PMM-9374 use pmm-clients vmagent instead of a separate one. * PMM-9374 Fix linters. * PMM-9374 Fix tests. * PMM-9374 Improve work with VM Proxy. * PMM-9374 Fix tests. * PMM-9374 Fix tests. * PMM-9374 Fix tests. * PMM-9374 Fix linters. * PMM-9374 Add new tests. * PMM-9374 use interfaces instead of real object. * PMM-9374 Fix the test. * PMM-9374 Fix the linter. * PMM-9374 Fix the linter. * PMM-11952 docker configuration for external PostgreSQL usage. * PMM-11952 docker configuration for external PostgreSQL usage. * PMM-11924 PMM HA Phase 3. * PMM-11924 PMM HA Phase 3 PoC. * PMM-11924 PMM HA Phase 3 PoC. * PMM-11924 PMM HA Phase 3 PoC. * PMM-11924 Fix tests. * PMM-11924 Fix tests and linters. * PMM-11924 Fix tests. * PMM-11924 Fix linter. * Update pmm_config.go * Update pmm-db_disabled.ini * PMM-12078 HA Phase 3 Active-Active PoC. * PMM-12078 HA Phase 3 Active-Active PoC. * PMM-12182 use interface. * PMM-12078 fix go.sum. * PMM-12078 fix build. * PMM-12078 Improve the code * PMM-12078 Fix docker-compose config * PMM-12078 HAProxy configuration * PMM-12078 Format * PMM-12078 haproxy certificates * PMM-12078 fix configuration * PMM-12078 fix configuration * PMM-12078 fix tests * PMM-12078 fix tests * PMM-12078 fix supervisord * PMM-12078 fix supervisord * PMM-12078 fix linters * PMM-12078 fix listen port for grafana gossip protocol * PMM-12078 Some cleanup. * PMM-12078 fix license headers. * PMM-12293 fix linters. * PMM-12078 go mod tidy * PMM-12078 Fix linters * PMM-12078 Fix linters. * PMM-12078 revert changes. * PMM-12078 don't run pmm-agent on passive nodes. * PMM-12078 Fix tests and linters. * PMM-12078 Fix tests and linters. * PMM-12078 Fix starlark test. * PMM-9374 fix supervisord for external VM. * PMM-9374 Fix linters. * PMM-9374 Don't run victoria metrics if it's external. * PMM-12078 revert changes made for active-active connection. * PMM-12078 fix linter. * Update log_level.go * Update managed/services/highavailability/highavailability.go Co-authored-by: Artem Gavrilov * Update managed/services/highavailability/leaderservice.go Co-authored-by: Artem Gavrilov * PMM-12078 address comments. * PMM-12078 fix linter. * PMM-12078 fix license headers. * PMM-12078 Some improvements regarding env variables. * PMM-12078 Some improvements regarding env variables. * PMM-12078 Some improvements regarding env variables. * PMM-12078 Don't create grafana DB for external DB. * PMM-12078 Update db versions. * PMM-12078 fix go mod. --------- Co-authored-by: Artem Gavrilov --- agent/cmd/pmm-agent-entrypoint/main.go | 4 +- .../server/leader_health_check_parameters.go | 146 +++ .../server/leader_health_check_responses.go | 369 ++++++ .../json/client/server/server_client.go | 41 + api/serverpb/json/serverpb.json | 62 + api/serverpb/server.pb.go | 1159 +++++++++-------- api/serverpb/server.pb.gw.go | 81 ++ api/serverpb/server.pb.validate.go | 204 +++ api/serverpb/server.proto | 17 + api/serverpb/server_grpc.pb.go | 40 + api/swagger/swagger-dev.json | 62 + api/swagger/swagger.json | 62 + docker-compose.yml | 314 ++++- go.mod | 10 +- go.sum | 21 +- managed/cmd/pmm-managed-init/main.go | 1 + managed/cmd/pmm-managed/main.go | 161 ++- managed/models/params.go | 35 + managed/services/agents/channel/channel.go | 12 +- .../services/agents/channel/channel_test.go | 2 +- managed/services/agents/handler.go | 4 +- managed/services/agents/registry.go | 16 +- managed/services/agents/state.go | 2 +- managed/services/grafana/auth_server.go | 5 +- managed/services/ha/highavailability.go | 318 +++++ managed/services/ha/leaderservice.go | 87 ++ managed/services/ha/services.go | 107 ++ managed/services/server/deps.go | 8 +- managed/services/server/server.go | 13 + .../services/supervisord/devcontainer_test.go | 4 +- managed/services/supervisord/logs.go | 1 - managed/services/supervisord/pmm_config.go | 4 +- .../services/supervisord/pmm_config_test.go | 57 + managed/services/supervisord/supervisord.go | 48 +- .../services/supervisord/supervisord_test.go | 46 +- managed/testdata/haproxy/haproxy.cfg | 39 + managed/testdata/haproxy/localhost.crt | 20 + managed/testdata/haproxy/localhost.csr | 17 + managed/testdata/haproxy/localhost.key | 27 + managed/testdata/haproxy/localhost.pem | 47 + managed/testdata/pg/Makefile | 26 +- managed/testdata/pg/conf/pg_hba.conf | 5 +- managed/testdata/supervisord.d/grafana.ini | 16 +- .../supervisord.d/pmm-db_disabled.ini | 2 +- .../testdata/supervisord.d/pmm-db_enabled.ini | 2 +- managed/utils/envvars/parser.go | 6 +- update/.ansible-lint | 1 + .../tasks/roles/initialization/tasks/main.yml | 36 +- 48 files changed, 3066 insertions(+), 701 deletions(-) create mode 100644 api/serverpb/json/client/server/leader_health_check_parameters.go create mode 100644 api/serverpb/json/client/server/leader_health_check_responses.go create mode 100644 managed/models/params.go create mode 100644 managed/services/ha/highavailability.go create mode 100644 managed/services/ha/leaderservice.go create mode 100644 managed/services/ha/services.go create mode 100644 managed/services/supervisord/pmm_config_test.go create mode 100644 managed/testdata/haproxy/haproxy.cfg create mode 100644 managed/testdata/haproxy/localhost.crt create mode 100644 managed/testdata/haproxy/localhost.csr create mode 100644 managed/testdata/haproxy/localhost.key create mode 100644 managed/testdata/haproxy/localhost.pem diff --git a/agent/cmd/pmm-agent-entrypoint/main.go b/agent/cmd/pmm-agent-entrypoint/main.go index 29205c30d4..a8d222673b 100644 --- a/agent/cmd/pmm-agent-entrypoint/main.go +++ b/agent/cmd/pmm-agent-entrypoint/main.go @@ -73,7 +73,7 @@ var ( var pmmAgentProcessID = 0 func runPmmAgent(ctx context.Context, commandLineArgs []string, restartPolicy restartPolicy, l *logrus.Entry, pmmAgentSidecarSleep int) int { - pmmAgentFullCommand := "pmm-admin " + strings.Join(commandLineArgs, " ") + pmmAgentFullCommand := "pmm-agent " + strings.Join(commandLineArgs, " ") for { select { case <-ctx.Done(): @@ -81,7 +81,7 @@ func runPmmAgent(ctx context.Context, commandLineArgs []string, restartPolicy re default: } var exitCode int - l.Infof("Starting 'pmm-admin %s'...", strings.Join(commandLineArgs, " ")) + l.Infof("Starting 'pmm-agent %s'...", strings.Join(commandLineArgs, " ")) cmd := commandPmmAgent(commandLineArgs) if err := cmd.Start(); err != nil { l.Errorf("Can't run: '%s', Error: %s", commandLineArgs, err) diff --git a/api/serverpb/json/client/server/leader_health_check_parameters.go b/api/serverpb/json/client/server/leader_health_check_parameters.go new file mode 100644 index 0000000000..595d3d650d --- /dev/null +++ b/api/serverpb/json/client/server/leader_health_check_parameters.go @@ -0,0 +1,146 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package server + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" +) + +// NewLeaderHealthCheckParams creates a new LeaderHealthCheckParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewLeaderHealthCheckParams() *LeaderHealthCheckParams { + return &LeaderHealthCheckParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewLeaderHealthCheckParamsWithTimeout creates a new LeaderHealthCheckParams object +// with the ability to set a timeout on a request. +func NewLeaderHealthCheckParamsWithTimeout(timeout time.Duration) *LeaderHealthCheckParams { + return &LeaderHealthCheckParams{ + timeout: timeout, + } +} + +// NewLeaderHealthCheckParamsWithContext creates a new LeaderHealthCheckParams object +// with the ability to set a context for a request. +func NewLeaderHealthCheckParamsWithContext(ctx context.Context) *LeaderHealthCheckParams { + return &LeaderHealthCheckParams{ + Context: ctx, + } +} + +// NewLeaderHealthCheckParamsWithHTTPClient creates a new LeaderHealthCheckParams object +// with the ability to set a custom HTTPClient for a request. +func NewLeaderHealthCheckParamsWithHTTPClient(client *http.Client) *LeaderHealthCheckParams { + return &LeaderHealthCheckParams{ + HTTPClient: client, + } +} + +/* +LeaderHealthCheckParams contains all the parameters to send to the API endpoint + + for the leader health check operation. + + Typically these are written to a http.Request. +*/ +type LeaderHealthCheckParams struct { + // Body. + Body interface{} + + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the leader health check params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *LeaderHealthCheckParams) WithDefaults() *LeaderHealthCheckParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the leader health check params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *LeaderHealthCheckParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the leader health check params +func (o *LeaderHealthCheckParams) WithTimeout(timeout time.Duration) *LeaderHealthCheckParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the leader health check params +func (o *LeaderHealthCheckParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the leader health check params +func (o *LeaderHealthCheckParams) WithContext(ctx context.Context) *LeaderHealthCheckParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the leader health check params +func (o *LeaderHealthCheckParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the leader health check params +func (o *LeaderHealthCheckParams) WithHTTPClient(client *http.Client) *LeaderHealthCheckParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the leader health check params +func (o *LeaderHealthCheckParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WithBody adds the body to the leader health check params +func (o *LeaderHealthCheckParams) WithBody(body interface{}) *LeaderHealthCheckParams { + o.SetBody(body) + return o +} + +// SetBody adds the body to the leader health check params +func (o *LeaderHealthCheckParams) SetBody(body interface{}) { + o.Body = body +} + +// WriteToRequest writes these params to a swagger request +func (o *LeaderHealthCheckParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + if o.Body != nil { + if err := r.SetBodyParam(o.Body); err != nil { + return err + } + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/api/serverpb/json/client/server/leader_health_check_responses.go b/api/serverpb/json/client/server/leader_health_check_responses.go new file mode 100644 index 0000000000..25152bbebd --- /dev/null +++ b/api/serverpb/json/client/server/leader_health_check_responses.go @@ -0,0 +1,369 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package server + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "fmt" + "io" + "strconv" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// LeaderHealthCheckReader is a Reader for the LeaderHealthCheck structure. +type LeaderHealthCheckReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *LeaderHealthCheckReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewLeaderHealthCheckOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + default: + result := NewLeaderHealthCheckDefault(response.Code()) + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + if response.Code()/100 == 2 { + return result, nil + } + return nil, result + } +} + +// NewLeaderHealthCheckOK creates a LeaderHealthCheckOK with default headers values +func NewLeaderHealthCheckOK() *LeaderHealthCheckOK { + return &LeaderHealthCheckOK{} +} + +/* +LeaderHealthCheckOK describes a response with status code 200, with default header values. + +A successful response. +*/ +type LeaderHealthCheckOK struct { + Payload interface{} +} + +func (o *LeaderHealthCheckOK) Error() string { + return fmt.Sprintf("[POST /v1/leaderHealthCheck][%d] leaderHealthCheckOk %+v", 200, o.Payload) +} + +func (o *LeaderHealthCheckOK) GetPayload() interface{} { + return o.Payload +} + +func (o *LeaderHealthCheckOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewLeaderHealthCheckDefault creates a LeaderHealthCheckDefault with default headers values +func NewLeaderHealthCheckDefault(code int) *LeaderHealthCheckDefault { + return &LeaderHealthCheckDefault{ + _statusCode: code, + } +} + +/* +LeaderHealthCheckDefault describes a response with status code -1, with default header values. + +An unexpected error response. +*/ +type LeaderHealthCheckDefault struct { + _statusCode int + + Payload *LeaderHealthCheckDefaultBody +} + +// Code gets the status code for the leader health check default response +func (o *LeaderHealthCheckDefault) Code() int { + return o._statusCode +} + +func (o *LeaderHealthCheckDefault) Error() string { + return fmt.Sprintf("[POST /v1/leaderHealthCheck][%d] LeaderHealthCheck default %+v", o._statusCode, o.Payload) +} + +func (o *LeaderHealthCheckDefault) GetPayload() *LeaderHealthCheckDefaultBody { + return o.Payload +} + +func (o *LeaderHealthCheckDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + o.Payload = new(LeaderHealthCheckDefaultBody) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +/* +LeaderHealthCheckDefaultBody leader health check default body +swagger:model LeaderHealthCheckDefaultBody +*/ +type LeaderHealthCheckDefaultBody struct { + // code + Code int32 `json:"code,omitempty"` + + // message + Message string `json:"message,omitempty"` + + // details + Details []*LeaderHealthCheckDefaultBodyDetailsItems0 `json:"details"` +} + +// Validate validates this leader health check default body +func (o *LeaderHealthCheckDefaultBody) Validate(formats strfmt.Registry) error { + var res []error + + if err := o.validateDetails(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (o *LeaderHealthCheckDefaultBody) validateDetails(formats strfmt.Registry) error { + if swag.IsZero(o.Details) { // not required + return nil + } + + for i := 0; i < len(o.Details); i++ { + if swag.IsZero(o.Details[i]) { // not required + continue + } + + if o.Details[i] != nil { + if err := o.Details[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("LeaderHealthCheck default" + "." + "details" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("LeaderHealthCheck default" + "." + "details" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// ContextValidate validate this leader health check default body based on the context it is used +func (o *LeaderHealthCheckDefaultBody) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := o.contextValidateDetails(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (o *LeaderHealthCheckDefaultBody) contextValidateDetails(ctx context.Context, formats strfmt.Registry) error { + for i := 0; i < len(o.Details); i++ { + if o.Details[i] != nil { + if err := o.Details[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("LeaderHealthCheck default" + "." + "details" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("LeaderHealthCheck default" + "." + "details" + "." + strconv.Itoa(i)) + } + return err + } + } + } + + return nil +} + +// MarshalBinary interface implementation +func (o *LeaderHealthCheckDefaultBody) MarshalBinary() ([]byte, error) { + if o == nil { + return nil, nil + } + return swag.WriteJSON(o) +} + +// UnmarshalBinary interface implementation +func (o *LeaderHealthCheckDefaultBody) UnmarshalBinary(b []byte) error { + var res LeaderHealthCheckDefaultBody + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *o = res + return nil +} + +/* +LeaderHealthCheckDefaultBodyDetailsItems0 `Any` contains an arbitrary serialized protocol buffer message along with a +// URL that describes the type of the serialized message. +// +// Protobuf library provides support to pack/unpack Any values in the form +// of utility functions or additional generated methods of the Any type. +// +// Example 1: Pack and unpack a message in C++. +// +// Foo foo = ...; +// Any any; +// any.PackFrom(foo); +// ... +// if (any.UnpackTo(&foo)) { +// ... +// } +// +// Example 2: Pack and unpack a message in Java. +// +// Foo foo = ...; +// Any any = Any.pack(foo); +// ... +// if (any.is(Foo.class)) { +// foo = any.unpack(Foo.class); +// } +// // or ... +// if (any.isSameTypeAs(Foo.getDefaultInstance())) { +// foo = any.unpack(Foo.getDefaultInstance()); +// } +// +// Example 3: Pack and unpack a message in Python. +// +// foo = Foo(...) +// any = Any() +// any.Pack(foo) +// ... +// if any.Is(Foo.DESCRIPTOR): +// any.Unpack(foo) +// ... +// +// Example 4: Pack and unpack a message in Go +// +// foo := &pb.Foo{...} +// any, err := anypb.New(foo) +// if err != nil { +// ... +// } +// ... +// foo := &pb.Foo{} +// if err := any.UnmarshalTo(foo); err != nil { +// ... +// } +// +// The pack methods provided by protobuf library will by default use +// 'type.googleapis.com/full.type.name' as the type URL and the unpack +// methods only use the fully qualified type name after the last '/' +// in the type URL, for example "foo.bar.com/x/y.z" will yield type +// name "y.z". +// +// JSON +// ==== +// The JSON representation of an `Any` value uses the regular +// representation of the deserialized, embedded message, with an +// additional field `@type` which contains the type URL. Example: +// +// package google.profile; +// message Person { +// string first_name = 1; +// string last_name = 2; +// } +// +// { +// "@type": "type.googleapis.com/google.profile.Person", +// "firstName": , +// "lastName": +// } +// +// If the embedded message type is well-known and has a custom JSON +// representation, that representation will be embedded adding a field +// `value` which holds the custom JSON in addition to the `@type` +// field. Example (for message [google.protobuf.Duration][]): +// +// { +// "@type": "type.googleapis.com/google.protobuf.Duration", +// "value": "1.212s" +// } +swagger:model LeaderHealthCheckDefaultBodyDetailsItems0 +*/ +type LeaderHealthCheckDefaultBodyDetailsItems0 struct { + // A URL/resource name that uniquely identifies the type of the serialized + // protocol buffer message. This string must contain at least + // one "/" character. The last segment of the URL's path must represent + // the fully qualified name of the type (as in + // `path/google.protobuf.Duration`). The name should be in a canonical form + // (e.g., leading "." is not accepted). + // + // In practice, teams usually precompile into the binary all types that they + // expect it to use in the context of Any. However, for URLs which use the + // scheme `http`, `https`, or no scheme, one can optionally set up a type + // server that maps type URLs to message definitions as follows: + // + // * If no scheme is provided, `https` is assumed. + // * An HTTP GET on the URL must yield a [google.protobuf.Type][] + // value in binary format, or produce an error. + // * Applications are allowed to cache lookup results based on the + // URL, or have them precompiled into a binary to avoid any + // lookup. Therefore, binary compatibility needs to be preserved + // on changes to types. (Use versioned type names to manage + // breaking changes.) + // + // Note: this functionality is not currently available in the official + // protobuf release, and it is not used for type URLs beginning with + // type.googleapis.com. As of May 2023, there are no widely used type server + // implementations and no plans to implement one. + // + // Schemes other than `http`, `https` (or the empty scheme) might be + // used with implementation specific semantics. + AtType string `json:"@type,omitempty"` +} + +// Validate validates this leader health check default body details items0 +func (o *LeaderHealthCheckDefaultBodyDetailsItems0) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this leader health check default body details items0 based on context it is used +func (o *LeaderHealthCheckDefaultBodyDetailsItems0) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (o *LeaderHealthCheckDefaultBodyDetailsItems0) MarshalBinary() ([]byte, error) { + if o == nil { + return nil, nil + } + return swag.WriteJSON(o) +} + +// UnmarshalBinary interface implementation +func (o *LeaderHealthCheckDefaultBodyDetailsItems0) UnmarshalBinary(b []byte) error { + var res LeaderHealthCheckDefaultBodyDetailsItems0 + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *o = res + return nil +} diff --git a/api/serverpb/json/client/server/server_client.go b/api/serverpb/json/client/server/server_client.go index fffdea9044..02e7fab28f 100644 --- a/api/serverpb/json/client/server/server_client.go +++ b/api/serverpb/json/client/server/server_client.go @@ -38,6 +38,8 @@ type ClientService interface { GetSettings(params *GetSettingsParams, opts ...ClientOption) (*GetSettingsOK, error) + LeaderHealthCheck(params *LeaderHealthCheckParams, opts ...ClientOption) (*LeaderHealthCheckOK, error) + Logs(params *LogsParams, writer io.Writer, opts ...ClientOption) (*LogsOK, error) Readiness(params *ReadinessParams, opts ...ClientOption) (*ReadinessOK, error) @@ -209,6 +211,45 @@ func (a *Client) GetSettings(params *GetSettingsParams, opts ...ClientOption) (* return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) } +/* +LeaderHealthCheck checks leadership + +Checks if the instance is the leader in a cluster. Returns an error if the instance isn't the leader. +*/ +func (a *Client) LeaderHealthCheck(params *LeaderHealthCheckParams, opts ...ClientOption) (*LeaderHealthCheckOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewLeaderHealthCheckParams() + } + op := &runtime.ClientOperation{ + ID: "LeaderHealthCheck", + Method: "POST", + PathPattern: "/v1/leaderHealthCheck", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json"}, + Schemes: []string{"http", "https"}, + Params: params, + Reader: &LeaderHealthCheckReader{formats: a.formats}, + Context: params.Context, + Client: params.HTTPClient, + } + for _, opt := range opts { + opt(op) + } + + result, err := a.transport.Submit(op) + if err != nil { + return nil, err + } + success, ok := result.(*LeaderHealthCheckOK) + if ok { + return success, nil + } + // unexpected success response + unexpectedSuccess := result.(*LeaderHealthCheckDefault) + return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) +} + /* Logs logs diff --git a/api/serverpb/json/serverpb.json b/api/serverpb/json/serverpb.json index b853fd2e1d..932f000ca7 100644 --- a/api/serverpb/json/serverpb.json +++ b/api/serverpb/json/serverpb.json @@ -1322,6 +1322,68 @@ } } }, + "/v1/leaderHealthCheck": { + "post": { + "description": "Checks if the instance is the leader in a cluster. Returns an error if the instance isn't the leader.", + "tags": [ + "Server" + ], + "summary": "Check Leadership", + "operationId": "LeaderHealthCheck", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object" + } + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "description": "This probe is available without authentication, so it should not contain any data.", + "type": "object" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32", + "x-order": 0 + }, + "details": { + "type": "array", + "items": { + "description": "`Any` contains an arbitrary serialized protocol buffer message along with a\nURL that describes the type of the serialized message.\n\nProtobuf library provides support to pack/unpack Any values in the form\nof utility functions or additional generated methods of the Any type.\n\nExample 1: Pack and unpack a message in C++.\n\n Foo foo = ...;\n Any any;\n any.PackFrom(foo);\n ...\n if (any.UnpackTo(\u0026foo)) {\n ...\n }\n\nExample 2: Pack and unpack a message in Java.\n\n Foo foo = ...;\n Any any = Any.pack(foo);\n ...\n if (any.is(Foo.class)) {\n foo = any.unpack(Foo.class);\n }\n // or ...\n if (any.isSameTypeAs(Foo.getDefaultInstance())) {\n foo = any.unpack(Foo.getDefaultInstance());\n }\n\n Example 3: Pack and unpack a message in Python.\n\n foo = Foo(...)\n any = Any()\n any.Pack(foo)\n ...\n if any.Is(Foo.DESCRIPTOR):\n any.Unpack(foo)\n ...\n\n Example 4: Pack and unpack a message in Go\n\n foo := \u0026pb.Foo{...}\n any, err := anypb.New(foo)\n if err != nil {\n ...\n }\n ...\n foo := \u0026pb.Foo{}\n if err := any.UnmarshalTo(foo); err != nil {\n ...\n }\n\nThe pack methods provided by protobuf library will by default use\n'type.googleapis.com/full.type.name' as the type URL and the unpack\nmethods only use the fully qualified type name after the last '/'\nin the type URL, for example \"foo.bar.com/x/y.z\" will yield type\nname \"y.z\".\n\nJSON\n====\nThe JSON representation of an `Any` value uses the regular\nrepresentation of the deserialized, embedded message, with an\nadditional field `@type` which contains the type URL. Example:\n\n package google.profile;\n message Person {\n string first_name = 1;\n string last_name = 2;\n }\n\n {\n \"@type\": \"type.googleapis.com/google.profile.Person\",\n \"firstName\": \u003cstring\u003e,\n \"lastName\": \u003cstring\u003e\n }\n\nIf the embedded message type is well-known and has a custom JSON\nrepresentation, that representation will be embedded adding a field\n`value` which holds the custom JSON in addition to the `@type`\nfield. Example (for message [google.protobuf.Duration][]):\n\n {\n \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n \"value\": \"1.212s\"\n }", + "type": "object", + "properties": { + "@type": { + "description": "A URL/resource name that uniquely identifies the type of the serialized\nprotocol buffer message. This string must contain at least\none \"/\" character. The last segment of the URL's path must represent\nthe fully qualified name of the type (as in\n`path/google.protobuf.Duration`). The name should be in a canonical form\n(e.g., leading \".\" is not accepted).\n\nIn practice, teams usually precompile into the binary all types that they\nexpect it to use in the context of Any. However, for URLs which use the\nscheme `http`, `https`, or no scheme, one can optionally set up a type\nserver that maps type URLs to message definitions as follows:\n\n* If no scheme is provided, `https` is assumed.\n* An HTTP GET on the URL must yield a [google.protobuf.Type][]\n value in binary format, or produce an error.\n* Applications are allowed to cache lookup results based on the\n URL, or have them precompiled into a binary to avoid any\n lookup. Therefore, binary compatibility needs to be preserved\n on changes to types. (Use versioned type names to manage\n breaking changes.)\n\nNote: this functionality is not currently available in the official\nprotobuf release, and it is not used for type URLs beginning with\ntype.googleapis.com. As of May 2023, there are no widely used type server\nimplementations and no plans to implement one.\n\nSchemes other than `http`, `https` (or the empty scheme) might be\nused with implementation specific semantics.", + "type": "string", + "x-order": 0 + } + }, + "additionalProperties": false + }, + "x-order": 2 + }, + "message": { + "type": "string", + "x-order": 1 + } + } + } + } + } + } + }, "/v1/readyz": { "get": { "description": "Returns an error when Server components being restarted are not ready yet. Use this API for checking the health of Docker containers and for probing Kubernetes readiness.", diff --git a/api/serverpb/server.pb.go b/api/serverpb/server.pb.go index 56f379d144..de00b6bce7 100644 --- a/api/serverpb/server.pb.go +++ b/api/serverpb/server.pb.go @@ -351,6 +351,82 @@ func (*ReadinessResponse) Descriptor() ([]byte, []int) { return file_serverpb_server_proto_rawDescGZIP(), []int{4} } +type LeaderHealthCheckRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *LeaderHealthCheckRequest) Reset() { + *x = LeaderHealthCheckRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_serverpb_server_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LeaderHealthCheckRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LeaderHealthCheckRequest) ProtoMessage() {} + +func (x *LeaderHealthCheckRequest) ProtoReflect() protoreflect.Message { + mi := &file_serverpb_server_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LeaderHealthCheckRequest.ProtoReflect.Descriptor instead. +func (*LeaderHealthCheckRequest) Descriptor() ([]byte, []int) { + return file_serverpb_server_proto_rawDescGZIP(), []int{5} +} + +type LeaderHealthCheckResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *LeaderHealthCheckResponse) Reset() { + *x = LeaderHealthCheckResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_serverpb_server_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LeaderHealthCheckResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LeaderHealthCheckResponse) ProtoMessage() {} + +func (x *LeaderHealthCheckResponse) ProtoReflect() protoreflect.Message { + mi := &file_serverpb_server_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LeaderHealthCheckResponse.ProtoReflect.Descriptor instead. +func (*LeaderHealthCheckResponse) Descriptor() ([]byte, []int) { + return file_serverpb_server_proto_rawDescGZIP(), []int{6} +} + type CheckUpdatesRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -365,7 +441,7 @@ type CheckUpdatesRequest struct { func (x *CheckUpdatesRequest) Reset() { *x = CheckUpdatesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_serverpb_server_proto_msgTypes[5] + mi := &file_serverpb_server_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -378,7 +454,7 @@ func (x *CheckUpdatesRequest) String() string { func (*CheckUpdatesRequest) ProtoMessage() {} func (x *CheckUpdatesRequest) ProtoReflect() protoreflect.Message { - mi := &file_serverpb_server_proto_msgTypes[5] + mi := &file_serverpb_server_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -391,7 +467,7 @@ func (x *CheckUpdatesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CheckUpdatesRequest.ProtoReflect.Descriptor instead. func (*CheckUpdatesRequest) Descriptor() ([]byte, []int) { - return file_serverpb_server_proto_rawDescGZIP(), []int{5} + return file_serverpb_server_proto_rawDescGZIP(), []int{7} } func (x *CheckUpdatesRequest) GetForce() bool { @@ -428,7 +504,7 @@ type CheckUpdatesResponse struct { func (x *CheckUpdatesResponse) Reset() { *x = CheckUpdatesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_serverpb_server_proto_msgTypes[6] + mi := &file_serverpb_server_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -441,7 +517,7 @@ func (x *CheckUpdatesResponse) String() string { func (*CheckUpdatesResponse) ProtoMessage() {} func (x *CheckUpdatesResponse) ProtoReflect() protoreflect.Message { - mi := &file_serverpb_server_proto_msgTypes[6] + mi := &file_serverpb_server_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -454,7 +530,7 @@ func (x *CheckUpdatesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CheckUpdatesResponse.ProtoReflect.Descriptor instead. func (*CheckUpdatesResponse) Descriptor() ([]byte, []int) { - return file_serverpb_server_proto_rawDescGZIP(), []int{6} + return file_serverpb_server_proto_rawDescGZIP(), []int{8} } func (x *CheckUpdatesResponse) GetInstalled() *VersionInfo { @@ -501,7 +577,7 @@ type StartUpdateRequest struct { func (x *StartUpdateRequest) Reset() { *x = StartUpdateRequest{} if protoimpl.UnsafeEnabled { - mi := &file_serverpb_server_proto_msgTypes[7] + mi := &file_serverpb_server_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -514,7 +590,7 @@ func (x *StartUpdateRequest) String() string { func (*StartUpdateRequest) ProtoMessage() {} func (x *StartUpdateRequest) ProtoReflect() protoreflect.Message { - mi := &file_serverpb_server_proto_msgTypes[7] + mi := &file_serverpb_server_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -527,7 +603,7 @@ func (x *StartUpdateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use StartUpdateRequest.ProtoReflect.Descriptor instead. func (*StartUpdateRequest) Descriptor() ([]byte, []int) { - return file_serverpb_server_proto_rawDescGZIP(), []int{7} + return file_serverpb_server_proto_rawDescGZIP(), []int{9} } type StartUpdateResponse struct { @@ -544,7 +620,7 @@ type StartUpdateResponse struct { func (x *StartUpdateResponse) Reset() { *x = StartUpdateResponse{} if protoimpl.UnsafeEnabled { - mi := &file_serverpb_server_proto_msgTypes[8] + mi := &file_serverpb_server_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -557,7 +633,7 @@ func (x *StartUpdateResponse) String() string { func (*StartUpdateResponse) ProtoMessage() {} func (x *StartUpdateResponse) ProtoReflect() protoreflect.Message { - mi := &file_serverpb_server_proto_msgTypes[8] + mi := &file_serverpb_server_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -570,7 +646,7 @@ func (x *StartUpdateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use StartUpdateResponse.ProtoReflect.Descriptor instead. func (*StartUpdateResponse) Descriptor() ([]byte, []int) { - return file_serverpb_server_proto_rawDescGZIP(), []int{8} + return file_serverpb_server_proto_rawDescGZIP(), []int{10} } func (x *StartUpdateResponse) GetAuthToken() string { @@ -601,7 +677,7 @@ type UpdateStatusRequest struct { func (x *UpdateStatusRequest) Reset() { *x = UpdateStatusRequest{} if protoimpl.UnsafeEnabled { - mi := &file_serverpb_server_proto_msgTypes[9] + mi := &file_serverpb_server_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -614,7 +690,7 @@ func (x *UpdateStatusRequest) String() string { func (*UpdateStatusRequest) ProtoMessage() {} func (x *UpdateStatusRequest) ProtoReflect() protoreflect.Message { - mi := &file_serverpb_server_proto_msgTypes[9] + mi := &file_serverpb_server_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -627,7 +703,7 @@ func (x *UpdateStatusRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateStatusRequest.ProtoReflect.Descriptor instead. func (*UpdateStatusRequest) Descriptor() ([]byte, []int) { - return file_serverpb_server_proto_rawDescGZIP(), []int{9} + return file_serverpb_server_proto_rawDescGZIP(), []int{11} } func (x *UpdateStatusRequest) GetAuthToken() string { @@ -660,7 +736,7 @@ type UpdateStatusResponse struct { func (x *UpdateStatusResponse) Reset() { *x = UpdateStatusResponse{} if protoimpl.UnsafeEnabled { - mi := &file_serverpb_server_proto_msgTypes[10] + mi := &file_serverpb_server_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -673,7 +749,7 @@ func (x *UpdateStatusResponse) String() string { func (*UpdateStatusResponse) ProtoMessage() {} func (x *UpdateStatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_serverpb_server_proto_msgTypes[10] + mi := &file_serverpb_server_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -686,7 +762,7 @@ func (x *UpdateStatusResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateStatusResponse.ProtoReflect.Descriptor instead. func (*UpdateStatusResponse) Descriptor() ([]byte, []int) { - return file_serverpb_server_proto_rawDescGZIP(), []int{10} + return file_serverpb_server_proto_rawDescGZIP(), []int{12} } func (x *UpdateStatusResponse) GetLogLines() []string { @@ -727,7 +803,7 @@ type MetricsResolutions struct { func (x *MetricsResolutions) Reset() { *x = MetricsResolutions{} if protoimpl.UnsafeEnabled { - mi := &file_serverpb_server_proto_msgTypes[11] + mi := &file_serverpb_server_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -740,7 +816,7 @@ func (x *MetricsResolutions) String() string { func (*MetricsResolutions) ProtoMessage() {} func (x *MetricsResolutions) ProtoReflect() protoreflect.Message { - mi := &file_serverpb_server_proto_msgTypes[11] + mi := &file_serverpb_server_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -753,7 +829,7 @@ func (x *MetricsResolutions) ProtoReflect() protoreflect.Message { // Deprecated: Use MetricsResolutions.ProtoReflect.Descriptor instead. func (*MetricsResolutions) Descriptor() ([]byte, []int) { - return file_serverpb_server_proto_rawDescGZIP(), []int{11} + return file_serverpb_server_proto_rawDescGZIP(), []int{13} } func (x *MetricsResolutions) GetHr() *durationpb.Duration { @@ -804,7 +880,7 @@ type EmailAlertingSettings struct { func (x *EmailAlertingSettings) Reset() { *x = EmailAlertingSettings{} if protoimpl.UnsafeEnabled { - mi := &file_serverpb_server_proto_msgTypes[12] + mi := &file_serverpb_server_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -817,7 +893,7 @@ func (x *EmailAlertingSettings) String() string { func (*EmailAlertingSettings) ProtoMessage() {} func (x *EmailAlertingSettings) ProtoReflect() protoreflect.Message { - mi := &file_serverpb_server_proto_msgTypes[12] + mi := &file_serverpb_server_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -830,7 +906,7 @@ func (x *EmailAlertingSettings) ProtoReflect() protoreflect.Message { // Deprecated: Use EmailAlertingSettings.ProtoReflect.Descriptor instead. func (*EmailAlertingSettings) Descriptor() ([]byte, []int) { - return file_serverpb_server_proto_rawDescGZIP(), []int{12} + return file_serverpb_server_proto_rawDescGZIP(), []int{14} } func (x *EmailAlertingSettings) GetFrom() string { @@ -902,7 +978,7 @@ type SlackAlertingSettings struct { func (x *SlackAlertingSettings) Reset() { *x = SlackAlertingSettings{} if protoimpl.UnsafeEnabled { - mi := &file_serverpb_server_proto_msgTypes[13] + mi := &file_serverpb_server_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -915,7 +991,7 @@ func (x *SlackAlertingSettings) String() string { func (*SlackAlertingSettings) ProtoMessage() {} func (x *SlackAlertingSettings) ProtoReflect() protoreflect.Message { - mi := &file_serverpb_server_proto_msgTypes[13] + mi := &file_serverpb_server_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -928,7 +1004,7 @@ func (x *SlackAlertingSettings) ProtoReflect() protoreflect.Message { // Deprecated: Use SlackAlertingSettings.ProtoReflect.Descriptor instead. func (*SlackAlertingSettings) Descriptor() ([]byte, []int) { - return file_serverpb_server_proto_rawDescGZIP(), []int{13} + return file_serverpb_server_proto_rawDescGZIP(), []int{15} } func (x *SlackAlertingSettings) GetUrl() string { @@ -955,7 +1031,7 @@ type STTCheckIntervals struct { func (x *STTCheckIntervals) Reset() { *x = STTCheckIntervals{} if protoimpl.UnsafeEnabled { - mi := &file_serverpb_server_proto_msgTypes[14] + mi := &file_serverpb_server_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -968,7 +1044,7 @@ func (x *STTCheckIntervals) String() string { func (*STTCheckIntervals) ProtoMessage() {} func (x *STTCheckIntervals) ProtoReflect() protoreflect.Message { - mi := &file_serverpb_server_proto_msgTypes[14] + mi := &file_serverpb_server_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -981,7 +1057,7 @@ func (x *STTCheckIntervals) ProtoReflect() protoreflect.Message { // Deprecated: Use STTCheckIntervals.ProtoReflect.Descriptor instead. func (*STTCheckIntervals) Descriptor() ([]byte, []int) { - return file_serverpb_server_proto_rawDescGZIP(), []int{14} + return file_serverpb_server_proto_rawDescGZIP(), []int{16} } func (x *STTCheckIntervals) GetStandardInterval() *durationpb.Duration { @@ -1056,7 +1132,7 @@ type Settings struct { func (x *Settings) Reset() { *x = Settings{} if protoimpl.UnsafeEnabled { - mi := &file_serverpb_server_proto_msgTypes[15] + mi := &file_serverpb_server_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1069,7 +1145,7 @@ func (x *Settings) String() string { func (*Settings) ProtoMessage() {} func (x *Settings) ProtoReflect() protoreflect.Message { - mi := &file_serverpb_server_proto_msgTypes[15] + mi := &file_serverpb_server_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1082,7 +1158,7 @@ func (x *Settings) ProtoReflect() protoreflect.Message { // Deprecated: Use Settings.ProtoReflect.Descriptor instead. func (*Settings) Descriptor() ([]byte, []int) { - return file_serverpb_server_proto_rawDescGZIP(), []int{15} + return file_serverpb_server_proto_rawDescGZIP(), []int{17} } func (x *Settings) GetUpdatesDisabled() bool { @@ -1248,7 +1324,7 @@ type GetSettingsRequest struct { func (x *GetSettingsRequest) Reset() { *x = GetSettingsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_serverpb_server_proto_msgTypes[16] + mi := &file_serverpb_server_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1261,7 +1337,7 @@ func (x *GetSettingsRequest) String() string { func (*GetSettingsRequest) ProtoMessage() {} func (x *GetSettingsRequest) ProtoReflect() protoreflect.Message { - mi := &file_serverpb_server_proto_msgTypes[16] + mi := &file_serverpb_server_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1274,7 +1350,7 @@ func (x *GetSettingsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSettingsRequest.ProtoReflect.Descriptor instead. func (*GetSettingsRequest) Descriptor() ([]byte, []int) { - return file_serverpb_server_proto_rawDescGZIP(), []int{16} + return file_serverpb_server_proto_rawDescGZIP(), []int{18} } type GetSettingsResponse struct { @@ -1288,7 +1364,7 @@ type GetSettingsResponse struct { func (x *GetSettingsResponse) Reset() { *x = GetSettingsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_serverpb_server_proto_msgTypes[17] + mi := &file_serverpb_server_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1301,7 +1377,7 @@ func (x *GetSettingsResponse) String() string { func (*GetSettingsResponse) ProtoMessage() {} func (x *GetSettingsResponse) ProtoReflect() protoreflect.Message { - mi := &file_serverpb_server_proto_msgTypes[17] + mi := &file_serverpb_server_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1314,7 +1390,7 @@ func (x *GetSettingsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSettingsResponse.ProtoReflect.Descriptor instead. func (*GetSettingsResponse) Descriptor() ([]byte, []int) { - return file_serverpb_server_proto_rawDescGZIP(), []int{17} + return file_serverpb_server_proto_rawDescGZIP(), []int{19} } func (x *GetSettingsResponse) GetSettings() *Settings { @@ -1388,7 +1464,7 @@ type ChangeSettingsRequest struct { func (x *ChangeSettingsRequest) Reset() { *x = ChangeSettingsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_serverpb_server_proto_msgTypes[18] + mi := &file_serverpb_server_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1401,7 +1477,7 @@ func (x *ChangeSettingsRequest) String() string { func (*ChangeSettingsRequest) ProtoMessage() {} func (x *ChangeSettingsRequest) ProtoReflect() protoreflect.Message { - mi := &file_serverpb_server_proto_msgTypes[18] + mi := &file_serverpb_server_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1414,7 +1490,7 @@ func (x *ChangeSettingsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ChangeSettingsRequest.ProtoReflect.Descriptor instead. func (*ChangeSettingsRequest) Descriptor() ([]byte, []int) { - return file_serverpb_server_proto_rawDescGZIP(), []int{18} + return file_serverpb_server_proto_rawDescGZIP(), []int{20} } func (x *ChangeSettingsRequest) GetEnableUpdates() bool { @@ -1645,7 +1721,7 @@ type ChangeSettingsResponse struct { func (x *ChangeSettingsResponse) Reset() { *x = ChangeSettingsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_serverpb_server_proto_msgTypes[19] + mi := &file_serverpb_server_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1658,7 +1734,7 @@ func (x *ChangeSettingsResponse) String() string { func (*ChangeSettingsResponse) ProtoMessage() {} func (x *ChangeSettingsResponse) ProtoReflect() protoreflect.Message { - mi := &file_serverpb_server_proto_msgTypes[19] + mi := &file_serverpb_server_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1671,7 +1747,7 @@ func (x *ChangeSettingsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ChangeSettingsResponse.ProtoReflect.Descriptor instead. func (*ChangeSettingsResponse) Descriptor() ([]byte, []int) { - return file_serverpb_server_proto_rawDescGZIP(), []int{19} + return file_serverpb_server_proto_rawDescGZIP(), []int{21} } func (x *ChangeSettingsResponse) GetSettings() *Settings { @@ -1695,7 +1771,7 @@ type TestEmailAlertingSettingsRequest struct { func (x *TestEmailAlertingSettingsRequest) Reset() { *x = TestEmailAlertingSettingsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_serverpb_server_proto_msgTypes[20] + mi := &file_serverpb_server_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1708,7 +1784,7 @@ func (x *TestEmailAlertingSettingsRequest) String() string { func (*TestEmailAlertingSettingsRequest) ProtoMessage() {} func (x *TestEmailAlertingSettingsRequest) ProtoReflect() protoreflect.Message { - mi := &file_serverpb_server_proto_msgTypes[20] + mi := &file_serverpb_server_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1721,7 +1797,7 @@ func (x *TestEmailAlertingSettingsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use TestEmailAlertingSettingsRequest.ProtoReflect.Descriptor instead. func (*TestEmailAlertingSettingsRequest) Descriptor() ([]byte, []int) { - return file_serverpb_server_proto_rawDescGZIP(), []int{20} + return file_serverpb_server_proto_rawDescGZIP(), []int{22} } func (x *TestEmailAlertingSettingsRequest) GetEmailAlertingSettings() *EmailAlertingSettings { @@ -1747,7 +1823,7 @@ type TestEmailAlertingSettingsResponse struct { func (x *TestEmailAlertingSettingsResponse) Reset() { *x = TestEmailAlertingSettingsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_serverpb_server_proto_msgTypes[21] + mi := &file_serverpb_server_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1760,7 +1836,7 @@ func (x *TestEmailAlertingSettingsResponse) String() string { func (*TestEmailAlertingSettingsResponse) ProtoMessage() {} func (x *TestEmailAlertingSettingsResponse) ProtoReflect() protoreflect.Message { - mi := &file_serverpb_server_proto_msgTypes[21] + mi := &file_serverpb_server_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1773,7 +1849,7 @@ func (x *TestEmailAlertingSettingsResponse) ProtoReflect() protoreflect.Message // Deprecated: Use TestEmailAlertingSettingsResponse.ProtoReflect.Descriptor instead. func (*TestEmailAlertingSettingsResponse) Descriptor() ([]byte, []int) { - return file_serverpb_server_proto_rawDescGZIP(), []int{21} + return file_serverpb_server_proto_rawDescGZIP(), []int{23} } type AWSInstanceCheckRequest struct { @@ -1788,7 +1864,7 @@ type AWSInstanceCheckRequest struct { func (x *AWSInstanceCheckRequest) Reset() { *x = AWSInstanceCheckRequest{} if protoimpl.UnsafeEnabled { - mi := &file_serverpb_server_proto_msgTypes[22] + mi := &file_serverpb_server_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1801,7 +1877,7 @@ func (x *AWSInstanceCheckRequest) String() string { func (*AWSInstanceCheckRequest) ProtoMessage() {} func (x *AWSInstanceCheckRequest) ProtoReflect() protoreflect.Message { - mi := &file_serverpb_server_proto_msgTypes[22] + mi := &file_serverpb_server_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1814,7 +1890,7 @@ func (x *AWSInstanceCheckRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AWSInstanceCheckRequest.ProtoReflect.Descriptor instead. func (*AWSInstanceCheckRequest) Descriptor() ([]byte, []int) { - return file_serverpb_server_proto_rawDescGZIP(), []int{22} + return file_serverpb_server_proto_rawDescGZIP(), []int{24} } func (x *AWSInstanceCheckRequest) GetInstanceId() string { @@ -1833,7 +1909,7 @@ type AWSInstanceCheckResponse struct { func (x *AWSInstanceCheckResponse) Reset() { *x = AWSInstanceCheckResponse{} if protoimpl.UnsafeEnabled { - mi := &file_serverpb_server_proto_msgTypes[23] + mi := &file_serverpb_server_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1846,7 +1922,7 @@ func (x *AWSInstanceCheckResponse) String() string { func (*AWSInstanceCheckResponse) ProtoMessage() {} func (x *AWSInstanceCheckResponse) ProtoReflect() protoreflect.Message { - mi := &file_serverpb_server_proto_msgTypes[23] + mi := &file_serverpb_server_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1859,7 +1935,7 @@ func (x *AWSInstanceCheckResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AWSInstanceCheckResponse.ProtoReflect.Descriptor instead. func (*AWSInstanceCheckResponse) Descriptor() ([]byte, []int) { - return file_serverpb_server_proto_rawDescGZIP(), []int{23} + return file_serverpb_server_proto_rawDescGZIP(), []int{25} } var File_serverpb_server_proto protoreflect.FileDescriptor @@ -1904,181 +1980,101 @@ var file_serverpb_server_proto_rawDesc = []byte{ 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x22, 0x12, 0x0a, 0x10, 0x52, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x13, 0x0a, 0x11, 0x52, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x61, 0x0a, 0x13, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, - 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, - 0x72, 0x63, 0x65, 0x12, 0x34, 0x0a, 0x16, 0x6f, 0x6e, 0x6c, 0x79, 0x5f, 0x69, 0x6e, 0x73, 0x74, - 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x14, 0x6f, 0x6e, 0x6c, 0x79, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, - 0x65, 0x64, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x84, 0x02, 0x0a, 0x14, 0x43, 0x68, - 0x65, 0x63, 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x31, 0x0a, 0x09, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x56, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x09, 0x69, 0x6e, 0x73, 0x74, - 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x12, 0x2b, 0x0a, 0x06, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x56, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x6c, 0x61, 0x74, 0x65, - 0x73, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x76, 0x61, - 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x75, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x26, 0x0a, - 0x0f, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x6e, 0x65, 0x77, 0x73, 0x5f, 0x75, 0x72, 0x6c, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x4e, 0x65, - 0x77, 0x73, 0x55, 0x72, 0x6c, 0x12, 0x39, 0x0a, 0x0a, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x63, 0x68, - 0x65, 0x63, 0x6b, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x43, 0x68, 0x65, 0x63, 0x6b, - 0x22, 0x14, 0x0a, 0x12, 0x53, 0x74, 0x61, 0x72, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x53, 0x0a, 0x13, 0x53, 0x74, 0x61, 0x72, 0x74, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, - 0x0a, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x61, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, - 0x6c, 0x6f, 0x67, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x09, 0x6c, 0x6f, 0x67, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x53, 0x0a, 0x13, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, - 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x6f, 0x67, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x6c, 0x6f, 0x67, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, - 0x22, 0x66, 0x0a, 0x14, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x6f, 0x67, 0x5f, - 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x6f, 0x67, - 0x4c, 0x69, 0x6e, 0x65, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x6f, 0x67, 0x5f, 0x6f, 0x66, 0x66, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x0a, 0x18, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x22, 0x1b, 0x0a, 0x19, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, 0x65, 0x61, 0x6c, + 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x61, 0x0a, 0x13, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x12, 0x34, 0x0a, 0x16, + 0x6f, 0x6e, 0x6c, 0x79, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x5f, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x6f, 0x6e, + 0x6c, 0x79, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x56, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x22, 0x84, 0x02, 0x0a, 0x14, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x09, 0x69, + 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, + 0x6e, 0x66, 0x6f, 0x52, 0x09, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x12, 0x2b, + 0x0a, 0x06, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, + 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x76, 0x61, + 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, + 0x5f, 0x6e, 0x65, 0x77, 0x73, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0d, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x4e, 0x65, 0x77, 0x73, 0x55, 0x72, 0x6c, 0x12, 0x39, + 0x0a, 0x0a, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, + 0x6c, 0x61, 0x73, 0x74, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x22, 0x14, 0x0a, 0x12, 0x53, 0x74, 0x61, + 0x72, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, + 0x53, 0x0a, 0x13, 0x53, 0x74, 0x61, 0x72, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, + 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x75, 0x74, 0x68, + 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x6f, 0x67, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x6c, 0x6f, 0x67, 0x4f, 0x66, - 0x66, 0x73, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x6f, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x04, 0x64, 0x6f, 0x6e, 0x65, 0x22, 0x95, 0x01, 0x0a, 0x12, 0x4d, 0x65, 0x74, - 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, - 0x29, 0x0a, 0x02, 0x68, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x02, 0x68, 0x72, 0x12, 0x29, 0x0a, 0x02, 0x6d, 0x72, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x02, 0x6d, 0x72, 0x12, 0x29, 0x0a, 0x02, 0x6c, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x02, 0x6c, 0x72, - 0x22, 0xfe, 0x01, 0x0a, 0x15, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x69, - 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x1b, 0x0a, 0x04, 0x66, 0x72, - 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, - 0x01, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x25, 0x0a, 0x09, 0x73, 0x6d, 0x61, 0x72, 0x74, - 0x68, 0x6f, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, - 0x02, 0x10, 0x01, 0x52, 0x09, 0x73, 0x6d, 0x61, 0x72, 0x74, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x14, - 0x0a, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x68, - 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1a, 0x0a, 0x08, - 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, - 0x65, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, - 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x5f, 0x74, 0x6c, 0x73, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x54, 0x6c, - 0x73, 0x22, 0x32, 0x0a, 0x15, 0x53, 0x6c, 0x61, 0x63, 0x6b, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x69, - 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x19, 0x0a, 0x03, 0x75, 0x72, - 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, - 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, 0xe3, 0x01, 0x0a, 0x11, 0x53, 0x54, 0x54, 0x43, 0x68, 0x65, - 0x63, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x46, 0x0a, 0x11, 0x73, - 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x10, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x49, 0x6e, 0x74, 0x65, 0x72, - 0x76, 0x61, 0x6c, 0x12, 0x3e, 0x0a, 0x0d, 0x72, 0x61, 0x72, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x76, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, + 0x66, 0x73, 0x65, 0x74, 0x22, 0x53, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x61, + 0x75, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x61, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x6f, + 0x67, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, + 0x6c, 0x6f, 0x67, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x66, 0x0a, 0x14, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x6f, 0x67, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x73, 0x12, 0x1d, + 0x0a, 0x0a, 0x6c, 0x6f, 0x67, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x09, 0x6c, 0x6f, 0x67, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x12, 0x0a, + 0x04, 0x64, 0x6f, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x64, 0x6f, 0x6e, + 0x65, 0x22, 0x95, 0x01, 0x0a, 0x12, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, + 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x29, 0x0a, 0x02, 0x68, 0x72, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x02, 0x68, 0x72, 0x12, 0x29, 0x0a, 0x02, 0x6d, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x02, 0x6d, 0x72, 0x12, 0x29, + 0x0a, 0x02, 0x6c, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x72, 0x61, 0x72, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, - 0x76, 0x61, 0x6c, 0x12, 0x46, 0x0a, 0x11, 0x66, 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x5f, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x66, 0x72, 0x65, 0x71, 0x75, - 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x22, 0xfe, 0x08, 0x0a, 0x08, - 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x75, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x73, 0x5f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x0f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x44, 0x69, 0x73, 0x61, 0x62, - 0x6c, 0x65, 0x64, 0x12, 0x2b, 0x0a, 0x11, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, - 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, - 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, - 0x12, 0x4b, 0x0a, 0x13, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x72, 0x65, 0x73, 0x6f, - 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, - 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x12, 0x6d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x40, 0x0a, - 0x0e, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x0d, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x17, 0x0a, 0x07, 0x73, 0x73, 0x68, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x06, 0x73, 0x73, 0x68, 0x4b, 0x65, 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x77, 0x73, 0x5f, - 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x0d, 0x61, 0x77, 0x73, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, - 0x2a, 0x0a, 0x11, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, - 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x61, 0x6c, 0x65, 0x72, - 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x55, 0x72, 0x6c, 0x12, 0x2e, 0x0a, 0x13, 0x61, - 0x6c, 0x65, 0x72, 0x74, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x5f, 0x72, 0x75, 0x6c, - 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x4d, - 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x73, - 0x74, 0x74, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x0a, 0x73, 0x74, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x25, 0x0a, 0x0e, - 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x0a, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x45, 0x6d, - 0x61, 0x69, 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x62, 0x61, 0x61, 0x73, 0x5f, 0x65, 0x6e, 0x61, - 0x62, 0x6c, 0x65, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x64, 0x62, 0x61, 0x61, - 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x61, 0x6c, 0x65, 0x72, - 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x0c, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x0f, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x61, 0x62, - 0x6c, 0x65, 0x64, 0x12, 0x55, 0x0a, 0x17, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x5f, 0x61, 0x6c, 0x65, - 0x72, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x0d, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x02, 0x6c, 0x72, 0x22, 0xfe, 0x01, 0x0a, 0x15, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, - 0x6e, 0x67, 0x73, 0x52, 0x15, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x69, - 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x55, 0x0a, 0x17, 0x73, 0x6c, - 0x61, 0x63, 0x6b, 0x5f, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x65, 0x74, - 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x6c, 0x61, 0x63, 0x6b, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x69, - 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x15, 0x73, 0x6c, 0x61, 0x63, - 0x6b, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, - 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x70, 0x6d, 0x6d, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, - 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x70, - 0x6d, 0x6d, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, - 0x49, 0x0a, 0x13, 0x73, 0x74, 0x74, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x54, 0x54, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x52, 0x11, 0x73, 0x74, 0x74, 0x43, 0x68, 0x65, 0x63, - 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x3a, 0x0a, 0x19, 0x62, 0x61, - 0x63, 0x6b, 0x75, 0x70, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, - 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x62, - 0x61, 0x63, 0x6b, 0x75, 0x70, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x45, - 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x33, 0x0a, 0x15, 0x61, 0x7a, 0x75, 0x72, 0x65, 0x64, - 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, - 0x11, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x61, 0x7a, 0x75, 0x72, 0x65, 0x64, 0x69, 0x73, 0x63, - 0x6f, 0x76, 0x65, 0x72, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x32, 0x0a, 0x15, 0x63, - 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x70, 0x6c, 0x61, 0x74, - 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x13, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x63, 0x6f, 0x6e, 0x6e, - 0x65, 0x63, 0x74, 0x65, 0x64, 0x54, 0x6f, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, - 0x2f, 0x0a, 0x13, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x73, 0x75, 0x6d, - 0x6d, 0x61, 0x72, 0x69, 0x65, 0x73, 0x18, 0x14, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x74, 0x65, - 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x69, 0x65, 0x73, - 0x12, 0x32, 0x0a, 0x15, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x18, 0x15, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x13, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x12, 0x26, 0x0a, 0x0f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, - 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x64, - 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x49, 0x64, 0x22, 0x14, 0x0a, 0x12, - 0x47, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x22, 0x43, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x08, 0x73, 0x65, 0x74, - 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x08, 0x73, - 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0xf0, 0x0c, 0x0a, 0x15, 0x43, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x75, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x73, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x65, 0x6e, 0x61, 0x62, 0x6c, - 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x69, 0x73, 0x61, - 0x62, 0x6c, 0x65, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x1d, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x0e, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x73, 0x12, 0x29, 0x0a, 0x10, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x74, 0x65, 0x6c, 0x65, - 0x6d, 0x65, 0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x65, 0x6e, 0x61, - 0x62, 0x6c, 0x65, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x12, 0x2b, 0x0a, 0x11, - 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, - 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, - 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x12, 0x4b, 0x0a, 0x13, 0x6d, 0x65, 0x74, + 0x6e, 0x67, 0x73, 0x12, 0x1b, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, + 0x12, 0x25, 0x0a, 0x09, 0x73, 0x6d, 0x61, 0x72, 0x74, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x73, 0x6d, + 0x61, 0x72, 0x74, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x1a, 0x0a, + 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, + 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, + 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x71, + 0x75, 0x69, 0x72, 0x65, 0x5f, 0x74, 0x6c, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, + 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x54, 0x6c, 0x73, 0x22, 0x32, 0x0a, 0x15, 0x53, 0x6c, + 0x61, 0x63, 0x6b, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, + 0x6e, 0x67, 0x73, 0x12, 0x19, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, 0xe3, + 0x01, 0x0a, 0x11, 0x53, 0x54, 0x54, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x76, 0x61, 0x6c, 0x73, 0x12, 0x46, 0x0a, 0x11, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, + 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x73, 0x74, 0x61, 0x6e, + 0x64, 0x61, 0x72, 0x64, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x3e, 0x0a, 0x0d, + 0x72, 0x61, 0x72, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, + 0x72, 0x61, 0x72, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x46, 0x0a, 0x11, + 0x66, 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, + 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x10, 0x66, 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x76, 0x61, 0x6c, 0x22, 0xfe, 0x08, 0x0a, 0x08, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, + 0x73, 0x12, 0x29, 0x0a, 0x10, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x5f, 0x64, 0x69, 0x73, + 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x73, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x2b, 0x0a, 0x11, + 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, + 0x72, 0x79, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x4b, 0x0a, 0x13, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, @@ -2094,222 +2090,321 @@ var file_serverpb_server_proto_rawDesc = []byte{ 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, - 0x72, 0x55, 0x72, 0x6c, 0x12, 0x37, 0x0a, 0x18, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x5f, 0x61, - 0x6c, 0x65, 0x72, 0x74, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x5f, 0x75, 0x72, 0x6c, - 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x41, 0x6c, - 0x65, 0x72, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x55, 0x72, 0x6c, 0x12, 0x2e, 0x0a, - 0x13, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x5f, 0x72, - 0x75, 0x6c, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x61, 0x6c, 0x65, 0x72, - 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x3b, 0x0a, - 0x1a, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x5f, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x5f, 0x6d, 0x61, - 0x6e, 0x61, 0x67, 0x65, 0x72, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x17, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x4d, 0x61, - 0x6e, 0x61, 0x67, 0x65, 0x72, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x6e, - 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x74, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, - 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x74, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x69, 0x73, - 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x74, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, - 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x74, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x6e, - 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x0d, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x0e, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x6c, 0x65, 0x72, 0x74, - 0x69, 0x6e, 0x67, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x61, - 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x64, - 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x55, - 0x0a, 0x17, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x5f, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, - 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x41, 0x6c, - 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x15, - 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, - 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x43, 0x0a, 0x1e, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x5f, + 0x72, 0x55, 0x72, 0x6c, 0x12, 0x2e, 0x0a, 0x13, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x5f, 0x6d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x72, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x11, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x52, + 0x75, 0x6c, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x74, 0x74, 0x5f, 0x65, 0x6e, 0x61, 0x62, + 0x6c, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x73, 0x74, 0x74, 0x45, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, + 0x6d, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, + 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x23, 0x0a, 0x0d, + 0x64, 0x62, 0x61, 0x61, 0x73, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x0b, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0c, 0x64, 0x62, 0x61, 0x61, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, + 0x64, 0x12, 0x29, 0x0a, 0x10, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x61, 0x6c, 0x65, + 0x72, 0x74, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x55, 0x0a, 0x17, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x5f, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x73, - 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1b, 0x72, - 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x69, - 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x55, 0x0a, 0x17, 0x73, 0x6c, - 0x61, 0x63, 0x6b, 0x5f, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x65, 0x74, - 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x6c, 0x61, 0x63, 0x6b, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x69, - 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x15, 0x73, 0x6c, 0x61, 0x63, - 0x6b, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, - 0x73, 0x12, 0x43, 0x0a, 0x1e, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x5f, 0x73, 0x6c, 0x61, 0x63, - 0x6b, 0x5f, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, - 0x6e, 0x67, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1b, 0x72, 0x65, 0x6d, 0x6f, 0x76, - 0x65, 0x53, 0x6c, 0x61, 0x63, 0x6b, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, - 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x70, 0x6d, 0x6d, 0x5f, 0x70, 0x75, - 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x13, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x10, 0x70, 0x6d, 0x6d, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x41, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x12, 0x39, 0x0a, 0x19, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x5f, 0x70, - 0x6d, 0x6d, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, - 0x6d, 0x6d, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, - 0x49, 0x0a, 0x13, 0x73, 0x74, 0x74, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x54, 0x54, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x52, 0x11, 0x73, 0x74, 0x74, 0x43, 0x68, 0x65, 0x63, - 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x31, 0x0a, 0x14, 0x65, 0x6e, - 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x61, 0x7a, 0x75, 0x72, 0x65, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, - 0x65, 0x72, 0x18, 0x16, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, - 0x41, 0x7a, 0x75, 0x72, 0x65, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x12, 0x33, 0x0a, - 0x15, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x61, 0x7a, 0x75, 0x72, 0x65, 0x64, 0x69, - 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x18, 0x17, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x64, 0x69, - 0x73, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, - 0x65, 0x72, 0x12, 0x38, 0x0a, 0x18, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x62, 0x61, 0x63, - 0x6b, 0x75, 0x70, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x18, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x61, 0x63, 0x6b, - 0x75, 0x70, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x3a, 0x0a, 0x19, - 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x5f, 0x6d, - 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x19, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x17, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x4d, 0x61, - 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x65, 0x6e, 0x61, 0x62, - 0x6c, 0x65, 0x5f, 0x64, 0x62, 0x61, 0x61, 0x73, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, - 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x44, 0x62, 0x61, 0x61, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x64, - 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x64, 0x62, 0x61, 0x61, 0x73, 0x18, 0x1b, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x0c, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x44, 0x62, 0x61, 0x61, 0x73, - 0x12, 0x32, 0x0a, 0x15, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x13, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x12, 0x34, 0x0a, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, - 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x18, 0x1f, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x22, 0x46, 0x0a, 0x16, 0x43, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, - 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, - 0x67, 0x73, 0x22, 0x94, 0x01, 0x0a, 0x20, 0x54, 0x65, 0x73, 0x74, 0x45, 0x6d, 0x61, 0x69, 0x6c, - 0x41, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x55, 0x0a, 0x17, 0x65, 0x6d, 0x61, 0x69, 0x6c, + 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x41, 0x6c, 0x65, 0x72, + 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x15, 0x65, 0x6d, + 0x61, 0x69, 0x6c, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, + 0x6e, 0x67, 0x73, 0x12, 0x55, 0x0a, 0x17, 0x73, 0x6c, 0x61, 0x63, 0x6b, 0x5f, 0x61, 0x6c, 0x65, + 0x72, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x0e, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x6c, + 0x61, 0x63, 0x6b, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, + 0x6e, 0x67, 0x73, 0x52, 0x15, 0x73, 0x6c, 0x61, 0x63, 0x6b, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x69, + 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x70, 0x6d, + 0x6d, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x70, 0x6d, 0x6d, 0x50, 0x75, 0x62, 0x6c, 0x69, + 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x49, 0x0a, 0x13, 0x73, 0x74, 0x74, 0x5f, + 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x18, + 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, + 0x54, 0x54, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, + 0x52, 0x11, 0x73, 0x74, 0x74, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, + 0x61, 0x6c, 0x73, 0x12, 0x3a, 0x0a, 0x19, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x5f, 0x6d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, + 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x4d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, + 0x33, 0x0a, 0x15, 0x61, 0x7a, 0x75, 0x72, 0x65, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, + 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, + 0x61, 0x7a, 0x75, 0x72, 0x65, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x45, 0x6e, 0x61, + 0x62, 0x6c, 0x65, 0x64, 0x12, 0x32, 0x0a, 0x15, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, + 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x13, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x13, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x54, 0x6f, + 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x2f, 0x0a, 0x13, 0x74, 0x65, 0x6c, 0x65, + 0x6d, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x69, 0x65, 0x73, 0x18, + 0x14, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, + 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x69, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x65, 0x6e, 0x61, + 0x62, 0x6c, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x18, 0x15, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, + 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x12, 0x26, 0x0a, + 0x0f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, + 0x18, 0x16, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, + 0x6f, 0x6c, 0x65, 0x49, 0x64, 0x22, 0x14, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, + 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x43, 0x0a, 0x13, 0x47, + 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x65, + 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, + 0x22, 0xf0, 0x0c, 0x0a, 0x15, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, + 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x65, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x1c, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0d, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x73, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x64, 0x69, 0x73, 0x61, + 0x62, 0x6c, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x65, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x65, 0x6c, 0x65, + 0x6d, 0x65, 0x74, 0x72, 0x79, 0x12, 0x2b, 0x0a, 0x11, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, + 0x5f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x10, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, + 0x72, 0x79, 0x12, 0x4b, 0x0a, 0x13, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x72, 0x65, + 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, + 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x12, 0x6d, 0x65, 0x74, + 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, + 0x40, 0x0a, 0x0e, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x0d, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x17, 0x0a, 0x07, 0x73, 0x73, 0x68, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x73, 0x73, 0x68, 0x4b, 0x65, 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x77, + 0x73, 0x5f, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x0d, 0x61, 0x77, 0x73, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, + 0x65, 0x72, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x61, 0x6c, + 0x65, 0x72, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x55, 0x72, 0x6c, 0x12, 0x37, 0x0a, + 0x18, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x5f, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x5f, 0x6d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x72, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x15, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x4d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x72, 0x55, 0x72, 0x6c, 0x12, 0x2e, 0x0a, 0x13, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x5f, + 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x11, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x72, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x3b, 0x0a, 0x1a, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, + 0x5f, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x5f, 0x72, + 0x75, 0x6c, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x72, 0x65, 0x6d, 0x6f, + 0x76, 0x65, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x52, 0x75, + 0x6c, 0x65, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x74, + 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x53, + 0x74, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x74, + 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, + 0x53, 0x74, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x61, 0x6c, + 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x65, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x29, 0x0a, 0x10, + 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, + 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x41, + 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x55, 0x0a, 0x17, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x5f, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, - 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x67, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x15, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x41, 0x6c, - 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x19, - 0x0a, 0x08, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x5f, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x54, 0x6f, 0x22, 0x23, 0x0a, 0x21, 0x54, 0x65, 0x73, - 0x74, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, - 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x43, - 0x0a, 0x17, 0x41, 0x57, 0x53, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, - 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a, 0x0b, 0x69, 0x6e, 0x73, - 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, - 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, - 0x65, 0x49, 0x64, 0x22, 0x1a, 0x0a, 0x18, 0x41, 0x57, 0x53, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, - 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, - 0x66, 0x0a, 0x12, 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x4d, - 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1f, 0x0a, 0x1b, 0x44, 0x49, 0x53, 0x54, 0x52, 0x49, 0x42, - 0x55, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x49, 0x4e, 0x56, - 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x4f, 0x43, 0x4b, 0x45, 0x52, - 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x4f, 0x56, 0x46, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x41, - 0x4d, 0x49, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x5a, 0x55, 0x52, 0x45, 0x10, 0x04, 0x12, - 0x06, 0x0a, 0x02, 0x44, 0x4f, 0x10, 0x05, 0x32, 0xef, 0x0c, 0x0a, 0x06, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x12, 0x79, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x56, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3d, - 0x92, 0x41, 0x27, 0x12, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x1a, 0x1c, 0x52, 0x65, - 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x50, 0x4d, 0x4d, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, - 0x12, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x9e, 0x02, - 0x0a, 0x09, 0x52, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x12, 0x18, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x52, - 0x65, 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0xdb, 0x01, 0x92, 0x41, 0xc5, 0x01, 0x12, 0x16, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x20, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x72, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x1a, - 0xaa, 0x01, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x65, 0x72, 0x72, - 0x6f, 0x72, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x63, - 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x62, 0x65, 0x69, 0x6e, 0x67, 0x20, - 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x20, 0x61, 0x72, 0x65, 0x20, 0x6e, 0x6f, - 0x74, 0x20, 0x72, 0x65, 0x61, 0x64, 0x79, 0x20, 0x79, 0x65, 0x74, 0x2e, 0x20, 0x55, 0x73, 0x65, - 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x41, 0x50, 0x49, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x63, 0x68, - 0x65, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x68, 0x65, 0x61, 0x6c, 0x74, - 0x68, 0x20, 0x6f, 0x66, 0x20, 0x44, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x20, 0x63, 0x6f, 0x6e, 0x74, - 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x70, - 0x72, 0x6f, 0x62, 0x69, 0x6e, 0x67, 0x20, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, - 0x73, 0x20, 0x72, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x2e, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x0c, 0x12, 0x0a, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x61, 0x64, 0x79, 0x7a, 0x12, 0xa3, - 0x01, 0x0a, 0x0c, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x12, - 0x1b, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x58, 0x92, 0x41, 0x39, 0x12, - 0x0d, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x1a, 0x28, - 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, - 0x61, 0x62, 0x6c, 0x65, 0x20, 0x50, 0x4d, 0x4d, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, - 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x3a, 0x01, - 0x2a, 0x22, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x43, - 0x68, 0x65, 0x63, 0x6b, 0x12, 0x90, 0x01, 0x0a, 0x0b, 0x53, 0x74, 0x61, 0x72, 0x74, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, - 0x61, 0x72, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1b, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x48, 0x92, - 0x41, 0x29, 0x12, 0x0c, 0x53, 0x74, 0x61, 0x72, 0x74, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x1a, 0x19, 0x53, 0x74, 0x61, 0x72, 0x74, 0x73, 0x20, 0x50, 0x4d, 0x4d, 0x20, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x16, 0x3a, 0x01, 0x2a, 0x22, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x73, 0x2f, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x9d, 0x01, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1b, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x52, 0x92, 0x41, 0x32, 0x12, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x20, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x1a, 0x21, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, - 0x20, 0x50, 0x4d, 0x4d, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x75, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x20, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, - 0x3a, 0x01, 0x2a, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, - 0x2f, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x9a, 0x01, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x53, - 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x1a, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, - 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x52, 0x92, 0x41, 0x34, 0x12, 0x0c, 0x47, 0x65, 0x74, 0x20, 0x73, 0x65, 0x74, 0x74, 0x69, - 0x6e, 0x67, 0x73, 0x1a, 0x24, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x63, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x74, 0x20, 0x50, 0x4d, 0x4d, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, - 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x3a, - 0x01, 0x2a, 0x22, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, - 0x2f, 0x47, 0x65, 0x74, 0x12, 0xa1, 0x01, 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, - 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x1d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, - 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x50, 0x92, 0x41, 0x2f, 0x12, 0x0f, 0x43, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x20, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x1a, 0x1c, 0x43, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x73, 0x20, 0x50, 0x4d, 0x4d, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x20, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, - 0x3a, 0x01, 0x2a, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, - 0x73, 0x2f, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x81, 0x02, 0x0a, 0x19, 0x54, 0x65, 0x73, - 0x74, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, - 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x28, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, + 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x43, + 0x0a, 0x1e, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x5f, 0x61, + 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, + 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1b, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, 0x6d, + 0x61, 0x69, 0x6c, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, + 0x6e, 0x67, 0x73, 0x12, 0x55, 0x0a, 0x17, 0x73, 0x6c, 0x61, 0x63, 0x6b, 0x5f, 0x61, 0x6c, 0x65, + 0x72, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x11, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x6c, + 0x61, 0x63, 0x6b, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, + 0x6e, 0x67, 0x73, 0x52, 0x15, 0x73, 0x6c, 0x61, 0x63, 0x6b, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x69, + 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x43, 0x0a, 0x1e, 0x72, 0x65, + 0x6d, 0x6f, 0x76, 0x65, 0x5f, 0x73, 0x6c, 0x61, 0x63, 0x6b, 0x5f, 0x61, 0x6c, 0x65, 0x72, 0x74, + 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x12, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x1b, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x53, 0x6c, 0x61, 0x63, 0x6b, 0x41, + 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, + 0x2c, 0x0a, 0x12, 0x70, 0x6d, 0x6d, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x13, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x70, 0x6d, 0x6d, + 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x39, 0x0a, + 0x19, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x5f, 0x70, 0x6d, 0x6d, 0x5f, 0x70, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x16, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6d, 0x6d, 0x50, 0x75, 0x62, 0x6c, 0x69, + 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x49, 0x0a, 0x13, 0x73, 0x74, 0x74, 0x5f, + 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x18, + 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, + 0x54, 0x54, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, + 0x52, 0x11, 0x73, 0x74, 0x74, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, + 0x61, 0x6c, 0x73, 0x12, 0x31, 0x0a, 0x14, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x61, 0x7a, + 0x75, 0x72, 0x65, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x18, 0x16, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x13, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x64, 0x69, + 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x12, 0x33, 0x0a, 0x15, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, + 0x65, 0x5f, 0x61, 0x7a, 0x75, 0x72, 0x65, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x18, + 0x17, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x7a, + 0x75, 0x72, 0x65, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x12, 0x38, 0x0a, 0x18, 0x65, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x5f, 0x6d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x18, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x65, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x4d, 0x61, 0x6e, 0x61, 0x67, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x3a, 0x0a, 0x19, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, + 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x18, 0x19, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, + 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x64, 0x62, 0x61, 0x61, + 0x73, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x44, + 0x62, 0x61, 0x61, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, + 0x64, 0x62, 0x61, 0x61, 0x73, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x64, 0x69, 0x73, + 0x61, 0x62, 0x6c, 0x65, 0x44, 0x62, 0x61, 0x61, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x65, 0x6e, 0x61, + 0x62, 0x6c, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, + 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x12, 0x34, 0x0a, + 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x64, + 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x22, 0x46, 0x0a, 0x16, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x65, 0x74, + 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, + 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x10, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, + 0x73, 0x52, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x94, 0x01, 0x0a, 0x20, 0x54, 0x65, 0x73, 0x74, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x29, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x45, 0x6d, - 0x61, 0x69, 0x6c, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, - 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x8e, 0x01, 0x92, 0x41, - 0x5a, 0x12, 0x13, 0x54, 0x65, 0x73, 0x74, 0x20, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x20, 0x61, 0x6c, - 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x1a, 0x43, 0x53, 0x65, 0x6e, 0x64, 0x73, 0x20, 0x74, 0x65, - 0x73, 0x74, 0x20, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x68, 0x65, 0x63, - 0x6b, 0x20, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x53, 0x4d, 0x54, 0x50, 0x20, 0x73, - 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x65, 0x6d, 0x61, 0x69, - 0x6c, 0x20, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x2b, 0x3a, 0x01, 0x2a, 0x22, 0x26, 0x2f, 0x76, 0x31, 0x2f, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, - 0x67, 0x73, 0x2f, 0x54, 0x65, 0x73, 0x74, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x41, 0x6c, 0x65, 0x72, - 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0xaa, 0x01, 0x0a, - 0x10, 0x41, 0x57, 0x53, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, - 0x6b, 0x12, 0x1f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x41, 0x57, 0x53, 0x49, 0x6e, + 0x12, 0x55, 0x0a, 0x17, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x5f, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x69, + 0x6e, 0x67, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x61, 0x69, 0x6c, + 0x41, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, + 0x52, 0x15, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, + 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6d, 0x61, 0x69, 0x6c, + 0x5f, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6d, 0x61, 0x69, 0x6c, + 0x54, 0x6f, 0x22, 0x23, 0x0a, 0x21, 0x54, 0x65, 0x73, 0x74, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x41, + 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x43, 0x0a, 0x17, 0x41, 0x57, 0x53, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x41, 0x57, 0x53, 0x49, - 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x53, 0x92, 0x41, 0x31, 0x12, 0x12, 0x41, 0x57, 0x53, 0x20, 0x69, - 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x1a, 0x1b, 0x43, - 0x68, 0x65, 0x63, 0x6b, 0x73, 0x20, 0x41, 0x57, 0x53, 0x20, 0x45, 0x43, 0x32, 0x20, 0x69, 0x6e, - 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x49, 0x44, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, - 0x3a, 0x01, 0x2a, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x41, 0x57, 0x53, 0x49, 0x6e, 0x73, 0x74, - 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x42, 0x76, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, - 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x42, 0x0b, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, - 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x23, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x70, 0x65, 0x72, 0x63, 0x6f, 0x6e, 0x61, 0x2f, 0x70, 0x6d, 0x6d, 0x2f, 0x61, - 0x70, 0x69, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x70, 0x62, 0xa2, 0x02, 0x03, 0x53, 0x58, - 0x58, 0xaa, 0x02, 0x06, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0xca, 0x02, 0x06, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0xe2, 0x02, 0x12, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5c, 0x47, 0x50, 0x42, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x06, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x74, 0x12, 0x28, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, + 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x22, 0x1a, 0x0a, 0x18, + 0x41, 0x57, 0x53, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x66, 0x0a, 0x12, 0x44, 0x69, 0x73, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1f, + 0x0a, 0x1b, 0x44, 0x49, 0x53, 0x54, 0x52, 0x49, 0x42, 0x55, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4d, + 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, + 0x0a, 0x0a, 0x06, 0x44, 0x4f, 0x43, 0x4b, 0x45, 0x52, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x4f, + 0x56, 0x46, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x4d, 0x49, 0x10, 0x03, 0x12, 0x09, 0x0a, + 0x05, 0x41, 0x5a, 0x55, 0x52, 0x45, 0x10, 0x04, 0x12, 0x06, 0x0a, 0x02, 0x44, 0x4f, 0x10, 0x05, + 0x32, 0xe9, 0x0e, 0x0a, 0x06, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x79, 0x0a, 0x07, 0x56, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3d, 0x92, 0x41, 0x27, 0x12, 0x07, 0x56, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x1a, 0x1c, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x50, + 0x4d, 0x4d, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x73, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, 0x12, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x9e, 0x02, 0x0a, 0x09, 0x52, 0x65, 0x61, 0x64, 0x69, + 0x6e, 0x65, 0x73, 0x73, 0x12, 0x18, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x52, 0x65, + 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xdb, 0x01, 0x92, 0x41, 0xc5, 0x01, + 0x12, 0x16, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x72, + 0x65, 0x61, 0x64, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x1a, 0xaa, 0x01, 0x52, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x77, 0x68, 0x65, 0x6e, + 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, + 0x74, 0x73, 0x20, 0x62, 0x65, 0x69, 0x6e, 0x67, 0x20, 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x65, 0x64, 0x20, 0x61, 0x72, 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x72, 0x65, 0x61, 0x64, 0x79, + 0x20, 0x79, 0x65, 0x74, 0x2e, 0x20, 0x55, 0x73, 0x65, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x41, + 0x50, 0x49, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x20, 0x6f, 0x66, 0x20, 0x44, 0x6f, + 0x63, 0x6b, 0x65, 0x72, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x20, + 0x61, 0x6e, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x70, 0x72, 0x6f, 0x62, 0x69, 0x6e, 0x67, 0x20, + 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x20, 0x72, 0x65, 0x61, 0x64, 0x69, + 0x6e, 0x65, 0x73, 0x73, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0c, 0x12, 0x0a, 0x2f, 0x76, 0x31, + 0x2f, 0x72, 0x65, 0x61, 0x64, 0x79, 0x7a, 0x12, 0xf7, 0x01, 0x0a, 0x11, 0x4c, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x20, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, 0x65, 0x61, + 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x21, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, + 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x9c, 0x01, 0x92, 0x41, 0x79, 0x12, 0x10, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x20, + 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x1a, 0x65, 0x43, 0x68, 0x65, 0x63, + 0x6b, 0x73, 0x20, 0x69, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x20, 0x69, 0x6e, 0x20, 0x61, 0x20, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x20, 0x52, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, + 0x69, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, + 0x69, 0x73, 0x6e, 0x27, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x3a, 0x01, 0x2a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, + 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, + 0x6b, 0x12, 0xa3, 0x01, 0x0a, 0x0c, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x43, 0x68, 0x65, 0x63, + 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1c, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x58, 0x92, + 0x41, 0x39, 0x12, 0x0d, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x73, 0x1a, 0x28, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x76, + 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x50, 0x4d, 0x4d, 0x20, 0x53, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x16, 0x3a, 0x01, 0x2a, 0x22, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x73, 0x2f, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x90, 0x01, 0x0a, 0x0b, 0x53, 0x74, 0x61, 0x72, + 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x61, + 0x72, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x48, 0x92, 0x41, 0x29, 0x12, 0x0c, 0x53, 0x74, 0x61, 0x72, 0x74, 0x20, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x1a, 0x19, 0x53, 0x74, 0x61, 0x72, 0x74, 0x73, 0x20, 0x50, 0x4d, 0x4d, 0x20, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x16, 0x3a, 0x01, 0x2a, 0x22, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x73, 0x2f, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x9d, 0x01, 0x0a, 0x0c, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1b, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x52, 0x92, 0x41, 0x32, 0x12, 0x0d, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x20, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x1a, 0x21, 0x52, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x73, 0x20, 0x50, 0x4d, 0x4d, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x17, 0x3a, 0x01, 0x2a, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x73, 0x2f, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x9a, 0x01, 0x0a, 0x0b, 0x47, + 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x1a, 0x2e, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, + 0x47, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x52, 0x92, 0x41, 0x34, 0x12, 0x0c, 0x47, 0x65, 0x74, 0x20, 0x73, 0x65, + 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x1a, 0x24, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, + 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x50, 0x4d, 0x4d, 0x20, 0x53, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x20, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x15, 0x3a, 0x01, 0x2a, 0x22, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x53, 0x65, 0x74, 0x74, 0x69, + 0x6e, 0x67, 0x73, 0x2f, 0x47, 0x65, 0x74, 0x12, 0xa1, 0x01, 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x1d, 0x2e, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x50, 0x92, 0x41, 0x2f, 0x12, 0x0f, + 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x20, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x1a, + 0x1c, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x20, 0x50, 0x4d, 0x4d, 0x20, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x20, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x18, 0x3a, 0x01, 0x2a, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x53, 0x65, 0x74, 0x74, + 0x69, 0x6e, 0x67, 0x73, 0x2f, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x81, 0x02, 0x0a, 0x19, + 0x54, 0x65, 0x73, 0x74, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, + 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x28, 0x2e, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x41, 0x6c, 0x65, 0x72, + 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, + 0x74, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x41, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, + 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x8e, + 0x01, 0x92, 0x41, 0x5a, 0x12, 0x13, 0x54, 0x65, 0x73, 0x74, 0x20, 0x65, 0x6d, 0x61, 0x69, 0x6c, + 0x20, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x1a, 0x43, 0x53, 0x65, 0x6e, 0x64, 0x73, + 0x20, 0x74, 0x65, 0x73, 0x74, 0x20, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x20, 0x74, 0x6f, 0x20, 0x63, + 0x68, 0x65, 0x63, 0x6b, 0x20, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x53, 0x4d, 0x54, + 0x50, 0x20, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x65, + 0x6d, 0x61, 0x69, 0x6c, 0x20, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x2b, 0x3a, 0x01, 0x2a, 0x22, 0x26, 0x2f, 0x76, 0x31, 0x2f, 0x53, 0x65, 0x74, + 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2f, 0x54, 0x65, 0x73, 0x74, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x41, + 0x6c, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, + 0xaa, 0x01, 0x0a, 0x10, 0x41, 0x57, 0x53, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, + 0x68, 0x65, 0x63, 0x6b, 0x12, 0x1f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x41, 0x57, + 0x53, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x41, + 0x57, 0x53, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x53, 0x92, 0x41, 0x31, 0x12, 0x12, 0x41, 0x57, + 0x53, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x63, 0x68, 0x65, 0x63, 0x6b, + 0x1a, 0x1b, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x20, 0x41, 0x57, 0x53, 0x20, 0x45, 0x43, 0x32, + 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x49, 0x44, 0x2e, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x19, 0x3a, 0x01, 0x2a, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x41, 0x57, 0x53, 0x49, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x42, 0x76, 0x0a, 0x0a, + 0x63, 0x6f, 0x6d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x42, 0x0b, 0x53, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x23, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x65, 0x72, 0x63, 0x6f, 0x6e, 0x61, 0x2f, 0x70, 0x6d, + 0x6d, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x70, 0x62, 0xa2, 0x02, + 0x03, 0x53, 0x58, 0x58, 0xaa, 0x02, 0x06, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0xca, 0x02, 0x06, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0xe2, 0x02, 0x12, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5c, + 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x06, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2326,7 +2421,7 @@ func file_serverpb_server_proto_rawDescGZIP() []byte { var ( file_serverpb_server_proto_enumTypes = make([]protoimpl.EnumInfo, 1) - file_serverpb_server_proto_msgTypes = make([]protoimpl.MessageInfo, 24) + file_serverpb_server_proto_msgTypes = make([]protoimpl.MessageInfo, 26) file_serverpb_server_proto_goTypes = []interface{}{ (DistributionMethod)(0), // 0: server.DistributionMethod (*VersionInfo)(nil), // 1: server.VersionInfo @@ -2334,77 +2429,81 @@ var ( (*VersionResponse)(nil), // 3: server.VersionResponse (*ReadinessRequest)(nil), // 4: server.ReadinessRequest (*ReadinessResponse)(nil), // 5: server.ReadinessResponse - (*CheckUpdatesRequest)(nil), // 6: server.CheckUpdatesRequest - (*CheckUpdatesResponse)(nil), // 7: server.CheckUpdatesResponse - (*StartUpdateRequest)(nil), // 8: server.StartUpdateRequest - (*StartUpdateResponse)(nil), // 9: server.StartUpdateResponse - (*UpdateStatusRequest)(nil), // 10: server.UpdateStatusRequest - (*UpdateStatusResponse)(nil), // 11: server.UpdateStatusResponse - (*MetricsResolutions)(nil), // 12: server.MetricsResolutions - (*EmailAlertingSettings)(nil), // 13: server.EmailAlertingSettings - (*SlackAlertingSettings)(nil), // 14: server.SlackAlertingSettings - (*STTCheckIntervals)(nil), // 15: server.STTCheckIntervals - (*Settings)(nil), // 16: server.Settings - (*GetSettingsRequest)(nil), // 17: server.GetSettingsRequest - (*GetSettingsResponse)(nil), // 18: server.GetSettingsResponse - (*ChangeSettingsRequest)(nil), // 19: server.ChangeSettingsRequest - (*ChangeSettingsResponse)(nil), // 20: server.ChangeSettingsResponse - (*TestEmailAlertingSettingsRequest)(nil), // 21: server.TestEmailAlertingSettingsRequest - (*TestEmailAlertingSettingsResponse)(nil), // 22: server.TestEmailAlertingSettingsResponse - (*AWSInstanceCheckRequest)(nil), // 23: server.AWSInstanceCheckRequest - (*AWSInstanceCheckResponse)(nil), // 24: server.AWSInstanceCheckResponse - (*timestamppb.Timestamp)(nil), // 25: google.protobuf.Timestamp - (*durationpb.Duration)(nil), // 26: google.protobuf.Duration + (*LeaderHealthCheckRequest)(nil), // 6: server.LeaderHealthCheckRequest + (*LeaderHealthCheckResponse)(nil), // 7: server.LeaderHealthCheckResponse + (*CheckUpdatesRequest)(nil), // 8: server.CheckUpdatesRequest + (*CheckUpdatesResponse)(nil), // 9: server.CheckUpdatesResponse + (*StartUpdateRequest)(nil), // 10: server.StartUpdateRequest + (*StartUpdateResponse)(nil), // 11: server.StartUpdateResponse + (*UpdateStatusRequest)(nil), // 12: server.UpdateStatusRequest + (*UpdateStatusResponse)(nil), // 13: server.UpdateStatusResponse + (*MetricsResolutions)(nil), // 14: server.MetricsResolutions + (*EmailAlertingSettings)(nil), // 15: server.EmailAlertingSettings + (*SlackAlertingSettings)(nil), // 16: server.SlackAlertingSettings + (*STTCheckIntervals)(nil), // 17: server.STTCheckIntervals + (*Settings)(nil), // 18: server.Settings + (*GetSettingsRequest)(nil), // 19: server.GetSettingsRequest + (*GetSettingsResponse)(nil), // 20: server.GetSettingsResponse + (*ChangeSettingsRequest)(nil), // 21: server.ChangeSettingsRequest + (*ChangeSettingsResponse)(nil), // 22: server.ChangeSettingsResponse + (*TestEmailAlertingSettingsRequest)(nil), // 23: server.TestEmailAlertingSettingsRequest + (*TestEmailAlertingSettingsResponse)(nil), // 24: server.TestEmailAlertingSettingsResponse + (*AWSInstanceCheckRequest)(nil), // 25: server.AWSInstanceCheckRequest + (*AWSInstanceCheckResponse)(nil), // 26: server.AWSInstanceCheckResponse + (*timestamppb.Timestamp)(nil), // 27: google.protobuf.Timestamp + (*durationpb.Duration)(nil), // 28: google.protobuf.Duration } ) var file_serverpb_server_proto_depIdxs = []int32{ - 25, // 0: server.VersionInfo.timestamp:type_name -> google.protobuf.Timestamp + 27, // 0: server.VersionInfo.timestamp:type_name -> google.protobuf.Timestamp 1, // 1: server.VersionResponse.server:type_name -> server.VersionInfo 1, // 2: server.VersionResponse.managed:type_name -> server.VersionInfo 0, // 3: server.VersionResponse.distribution_method:type_name -> server.DistributionMethod 1, // 4: server.CheckUpdatesResponse.installed:type_name -> server.VersionInfo 1, // 5: server.CheckUpdatesResponse.latest:type_name -> server.VersionInfo - 25, // 6: server.CheckUpdatesResponse.last_check:type_name -> google.protobuf.Timestamp - 26, // 7: server.MetricsResolutions.hr:type_name -> google.protobuf.Duration - 26, // 8: server.MetricsResolutions.mr:type_name -> google.protobuf.Duration - 26, // 9: server.MetricsResolutions.lr:type_name -> google.protobuf.Duration - 26, // 10: server.STTCheckIntervals.standard_interval:type_name -> google.protobuf.Duration - 26, // 11: server.STTCheckIntervals.rare_interval:type_name -> google.protobuf.Duration - 26, // 12: server.STTCheckIntervals.frequent_interval:type_name -> google.protobuf.Duration - 12, // 13: server.Settings.metrics_resolutions:type_name -> server.MetricsResolutions - 26, // 14: server.Settings.data_retention:type_name -> google.protobuf.Duration - 13, // 15: server.Settings.email_alerting_settings:type_name -> server.EmailAlertingSettings - 14, // 16: server.Settings.slack_alerting_settings:type_name -> server.SlackAlertingSettings - 15, // 17: server.Settings.stt_check_intervals:type_name -> server.STTCheckIntervals - 16, // 18: server.GetSettingsResponse.settings:type_name -> server.Settings - 12, // 19: server.ChangeSettingsRequest.metrics_resolutions:type_name -> server.MetricsResolutions - 26, // 20: server.ChangeSettingsRequest.data_retention:type_name -> google.protobuf.Duration - 13, // 21: server.ChangeSettingsRequest.email_alerting_settings:type_name -> server.EmailAlertingSettings - 14, // 22: server.ChangeSettingsRequest.slack_alerting_settings:type_name -> server.SlackAlertingSettings - 15, // 23: server.ChangeSettingsRequest.stt_check_intervals:type_name -> server.STTCheckIntervals - 16, // 24: server.ChangeSettingsResponse.settings:type_name -> server.Settings - 13, // 25: server.TestEmailAlertingSettingsRequest.email_alerting_settings:type_name -> server.EmailAlertingSettings + 27, // 6: server.CheckUpdatesResponse.last_check:type_name -> google.protobuf.Timestamp + 28, // 7: server.MetricsResolutions.hr:type_name -> google.protobuf.Duration + 28, // 8: server.MetricsResolutions.mr:type_name -> google.protobuf.Duration + 28, // 9: server.MetricsResolutions.lr:type_name -> google.protobuf.Duration + 28, // 10: server.STTCheckIntervals.standard_interval:type_name -> google.protobuf.Duration + 28, // 11: server.STTCheckIntervals.rare_interval:type_name -> google.protobuf.Duration + 28, // 12: server.STTCheckIntervals.frequent_interval:type_name -> google.protobuf.Duration + 14, // 13: server.Settings.metrics_resolutions:type_name -> server.MetricsResolutions + 28, // 14: server.Settings.data_retention:type_name -> google.protobuf.Duration + 15, // 15: server.Settings.email_alerting_settings:type_name -> server.EmailAlertingSettings + 16, // 16: server.Settings.slack_alerting_settings:type_name -> server.SlackAlertingSettings + 17, // 17: server.Settings.stt_check_intervals:type_name -> server.STTCheckIntervals + 18, // 18: server.GetSettingsResponse.settings:type_name -> server.Settings + 14, // 19: server.ChangeSettingsRequest.metrics_resolutions:type_name -> server.MetricsResolutions + 28, // 20: server.ChangeSettingsRequest.data_retention:type_name -> google.protobuf.Duration + 15, // 21: server.ChangeSettingsRequest.email_alerting_settings:type_name -> server.EmailAlertingSettings + 16, // 22: server.ChangeSettingsRequest.slack_alerting_settings:type_name -> server.SlackAlertingSettings + 17, // 23: server.ChangeSettingsRequest.stt_check_intervals:type_name -> server.STTCheckIntervals + 18, // 24: server.ChangeSettingsResponse.settings:type_name -> server.Settings + 15, // 25: server.TestEmailAlertingSettingsRequest.email_alerting_settings:type_name -> server.EmailAlertingSettings 2, // 26: server.Server.Version:input_type -> server.VersionRequest 4, // 27: server.Server.Readiness:input_type -> server.ReadinessRequest - 6, // 28: server.Server.CheckUpdates:input_type -> server.CheckUpdatesRequest - 8, // 29: server.Server.StartUpdate:input_type -> server.StartUpdateRequest - 10, // 30: server.Server.UpdateStatus:input_type -> server.UpdateStatusRequest - 17, // 31: server.Server.GetSettings:input_type -> server.GetSettingsRequest - 19, // 32: server.Server.ChangeSettings:input_type -> server.ChangeSettingsRequest - 21, // 33: server.Server.TestEmailAlertingSettings:input_type -> server.TestEmailAlertingSettingsRequest - 23, // 34: server.Server.AWSInstanceCheck:input_type -> server.AWSInstanceCheckRequest - 3, // 35: server.Server.Version:output_type -> server.VersionResponse - 5, // 36: server.Server.Readiness:output_type -> server.ReadinessResponse - 7, // 37: server.Server.CheckUpdates:output_type -> server.CheckUpdatesResponse - 9, // 38: server.Server.StartUpdate:output_type -> server.StartUpdateResponse - 11, // 39: server.Server.UpdateStatus:output_type -> server.UpdateStatusResponse - 18, // 40: server.Server.GetSettings:output_type -> server.GetSettingsResponse - 20, // 41: server.Server.ChangeSettings:output_type -> server.ChangeSettingsResponse - 22, // 42: server.Server.TestEmailAlertingSettings:output_type -> server.TestEmailAlertingSettingsResponse - 24, // 43: server.Server.AWSInstanceCheck:output_type -> server.AWSInstanceCheckResponse - 35, // [35:44] is the sub-list for method output_type - 26, // [26:35] is the sub-list for method input_type + 6, // 28: server.Server.LeaderHealthCheck:input_type -> server.LeaderHealthCheckRequest + 8, // 29: server.Server.CheckUpdates:input_type -> server.CheckUpdatesRequest + 10, // 30: server.Server.StartUpdate:input_type -> server.StartUpdateRequest + 12, // 31: server.Server.UpdateStatus:input_type -> server.UpdateStatusRequest + 19, // 32: server.Server.GetSettings:input_type -> server.GetSettingsRequest + 21, // 33: server.Server.ChangeSettings:input_type -> server.ChangeSettingsRequest + 23, // 34: server.Server.TestEmailAlertingSettings:input_type -> server.TestEmailAlertingSettingsRequest + 25, // 35: server.Server.AWSInstanceCheck:input_type -> server.AWSInstanceCheckRequest + 3, // 36: server.Server.Version:output_type -> server.VersionResponse + 5, // 37: server.Server.Readiness:output_type -> server.ReadinessResponse + 7, // 38: server.Server.LeaderHealthCheck:output_type -> server.LeaderHealthCheckResponse + 9, // 39: server.Server.CheckUpdates:output_type -> server.CheckUpdatesResponse + 11, // 40: server.Server.StartUpdate:output_type -> server.StartUpdateResponse + 13, // 41: server.Server.UpdateStatus:output_type -> server.UpdateStatusResponse + 20, // 42: server.Server.GetSettings:output_type -> server.GetSettingsResponse + 22, // 43: server.Server.ChangeSettings:output_type -> server.ChangeSettingsResponse + 24, // 44: server.Server.TestEmailAlertingSettings:output_type -> server.TestEmailAlertingSettingsResponse + 26, // 45: server.Server.AWSInstanceCheck:output_type -> server.AWSInstanceCheckResponse + 36, // [36:46] is the sub-list for method output_type + 26, // [26:36] is the sub-list for method input_type 26, // [26:26] is the sub-list for extension type_name 26, // [26:26] is the sub-list for extension extendee 0, // [0:26] is the sub-list for field type_name @@ -2477,7 +2576,7 @@ func file_serverpb_server_proto_init() { } } file_serverpb_server_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CheckUpdatesRequest); i { + switch v := v.(*LeaderHealthCheckRequest); i { case 0: return &v.state case 1: @@ -2489,7 +2588,7 @@ func file_serverpb_server_proto_init() { } } file_serverpb_server_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CheckUpdatesResponse); i { + switch v := v.(*LeaderHealthCheckResponse); i { case 0: return &v.state case 1: @@ -2501,7 +2600,7 @@ func file_serverpb_server_proto_init() { } } file_serverpb_server_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StartUpdateRequest); i { + switch v := v.(*CheckUpdatesRequest); i { case 0: return &v.state case 1: @@ -2513,7 +2612,7 @@ func file_serverpb_server_proto_init() { } } file_serverpb_server_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StartUpdateResponse); i { + switch v := v.(*CheckUpdatesResponse); i { case 0: return &v.state case 1: @@ -2525,7 +2624,7 @@ func file_serverpb_server_proto_init() { } } file_serverpb_server_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UpdateStatusRequest); i { + switch v := v.(*StartUpdateRequest); i { case 0: return &v.state case 1: @@ -2537,7 +2636,7 @@ func file_serverpb_server_proto_init() { } } file_serverpb_server_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UpdateStatusResponse); i { + switch v := v.(*StartUpdateResponse); i { case 0: return &v.state case 1: @@ -2549,7 +2648,7 @@ func file_serverpb_server_proto_init() { } } file_serverpb_server_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MetricsResolutions); i { + switch v := v.(*UpdateStatusRequest); i { case 0: return &v.state case 1: @@ -2561,7 +2660,7 @@ func file_serverpb_server_proto_init() { } } file_serverpb_server_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*EmailAlertingSettings); i { + switch v := v.(*UpdateStatusResponse); i { case 0: return &v.state case 1: @@ -2573,7 +2672,7 @@ func file_serverpb_server_proto_init() { } } file_serverpb_server_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SlackAlertingSettings); i { + switch v := v.(*MetricsResolutions); i { case 0: return &v.state case 1: @@ -2585,7 +2684,7 @@ func file_serverpb_server_proto_init() { } } file_serverpb_server_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*STTCheckIntervals); i { + switch v := v.(*EmailAlertingSettings); i { case 0: return &v.state case 1: @@ -2597,7 +2696,7 @@ func file_serverpb_server_proto_init() { } } file_serverpb_server_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Settings); i { + switch v := v.(*SlackAlertingSettings); i { case 0: return &v.state case 1: @@ -2609,7 +2708,7 @@ func file_serverpb_server_proto_init() { } } file_serverpb_server_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetSettingsRequest); i { + switch v := v.(*STTCheckIntervals); i { case 0: return &v.state case 1: @@ -2621,7 +2720,7 @@ func file_serverpb_server_proto_init() { } } file_serverpb_server_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetSettingsResponse); i { + switch v := v.(*Settings); i { case 0: return &v.state case 1: @@ -2633,7 +2732,7 @@ func file_serverpb_server_proto_init() { } } file_serverpb_server_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChangeSettingsRequest); i { + switch v := v.(*GetSettingsRequest); i { case 0: return &v.state case 1: @@ -2645,7 +2744,7 @@ func file_serverpb_server_proto_init() { } } file_serverpb_server_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChangeSettingsResponse); i { + switch v := v.(*GetSettingsResponse); i { case 0: return &v.state case 1: @@ -2657,7 +2756,7 @@ func file_serverpb_server_proto_init() { } } file_serverpb_server_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TestEmailAlertingSettingsRequest); i { + switch v := v.(*ChangeSettingsRequest); i { case 0: return &v.state case 1: @@ -2669,7 +2768,7 @@ func file_serverpb_server_proto_init() { } } file_serverpb_server_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TestEmailAlertingSettingsResponse); i { + switch v := v.(*ChangeSettingsResponse); i { case 0: return &v.state case 1: @@ -2681,7 +2780,7 @@ func file_serverpb_server_proto_init() { } } file_serverpb_server_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AWSInstanceCheckRequest); i { + switch v := v.(*TestEmailAlertingSettingsRequest); i { case 0: return &v.state case 1: @@ -2693,6 +2792,30 @@ func file_serverpb_server_proto_init() { } } file_serverpb_server_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TestEmailAlertingSettingsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_serverpb_server_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AWSInstanceCheckRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_serverpb_server_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*AWSInstanceCheckResponse); i { case 0: return &v.state @@ -2711,7 +2834,7 @@ func file_serverpb_server_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_serverpb_server_proto_rawDesc, NumEnums: 1, - NumMessages: 24, + NumMessages: 26, NumExtensions: 0, NumServices: 1, }, diff --git a/api/serverpb/server.pb.gw.go b/api/serverpb/server.pb.gw.go index 6b7c5f0f11..4ffe35ac64 100644 --- a/api/serverpb/server.pb.gw.go +++ b/api/serverpb/server.pb.gw.go @@ -81,6 +81,38 @@ func local_request_Server_Readiness_0(ctx context.Context, marshaler runtime.Mar return msg, metadata, err } +func request_Server_LeaderHealthCheck_0(ctx context.Context, marshaler runtime.Marshaler, client ServerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq LeaderHealthCheckRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.LeaderHealthCheck(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err +} + +func local_request_Server_LeaderHealthCheck_0(ctx context.Context, marshaler runtime.Marshaler, server ServerServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq LeaderHealthCheckRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.LeaderHealthCheck(ctx, &protoReq) + return msg, metadata, err +} + func request_Server_CheckUpdates_0(ctx context.Context, marshaler runtime.Marshaler, client ServerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq CheckUpdatesRequest var metadata runtime.ServerMetadata @@ -358,6 +390,30 @@ func RegisterServerHandlerServer(ctx context.Context, mux *runtime.ServeMux, ser forward_Server_Readiness_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) + mux.Handle("POST", pattern_Server_LeaderHealthCheck_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/server.Server/LeaderHealthCheck", runtime.WithHTTPPathPattern("/v1/leaderHealthCheck")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Server_LeaderHealthCheck_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_Server_LeaderHealthCheck_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + mux.Handle("POST", pattern_Server_CheckUpdates_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -608,6 +664,27 @@ func RegisterServerHandlerClient(ctx context.Context, mux *runtime.ServeMux, cli forward_Server_Readiness_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) + mux.Handle("POST", pattern_Server_LeaderHealthCheck_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/server.Server/LeaderHealthCheck", runtime.WithHTTPPathPattern("/v1/leaderHealthCheck")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Server_LeaderHealthCheck_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_Server_LeaderHealthCheck_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + mux.Handle("POST", pattern_Server_CheckUpdates_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -763,6 +840,8 @@ var ( pattern_Server_Readiness_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "readyz"}, "")) + pattern_Server_LeaderHealthCheck_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "leaderHealthCheck"}, "")) + pattern_Server_CheckUpdates_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "Updates", "Check"}, "")) pattern_Server_StartUpdate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "Updates", "Start"}, "")) @@ -783,6 +862,8 @@ var ( forward_Server_Readiness_0 = runtime.ForwardResponseMessage + forward_Server_LeaderHealthCheck_0 = runtime.ForwardResponseMessage + forward_Server_CheckUpdates_0 = runtime.ForwardResponseMessage forward_Server_StartUpdate_0 = runtime.ForwardResponseMessage diff --git a/api/serverpb/server.pb.validate.go b/api/serverpb/server.pb.validate.go index de4247a314..15e62b9f2f 100644 --- a/api/serverpb/server.pb.validate.go +++ b/api/serverpb/server.pb.validate.go @@ -633,6 +633,210 @@ var _ interface { ErrorName() string } = ReadinessResponseValidationError{} +// Validate checks the field values on LeaderHealthCheckRequest with the rules +// defined in the proto definition for this message. If any rules are +// violated, the first error encountered is returned, or nil if there are no violations. +func (m *LeaderHealthCheckRequest) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on LeaderHealthCheckRequest with the +// rules defined in the proto definition for this message. If any rules are +// violated, the result is a list of violation errors wrapped in +// LeaderHealthCheckRequestMultiError, or nil if none found. +func (m *LeaderHealthCheckRequest) ValidateAll() error { + return m.validate(true) +} + +func (m *LeaderHealthCheckRequest) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + if len(errors) > 0 { + return LeaderHealthCheckRequestMultiError(errors) + } + + return nil +} + +// LeaderHealthCheckRequestMultiError is an error wrapping multiple validation +// errors returned by LeaderHealthCheckRequest.ValidateAll() if the designated +// constraints aren't met. +type LeaderHealthCheckRequestMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m LeaderHealthCheckRequestMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m LeaderHealthCheckRequestMultiError) AllErrors() []error { return m } + +// LeaderHealthCheckRequestValidationError is the validation error returned by +// LeaderHealthCheckRequest.Validate if the designated constraints aren't met. +type LeaderHealthCheckRequestValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e LeaderHealthCheckRequestValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e LeaderHealthCheckRequestValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e LeaderHealthCheckRequestValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e LeaderHealthCheckRequestValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e LeaderHealthCheckRequestValidationError) ErrorName() string { + return "LeaderHealthCheckRequestValidationError" +} + +// Error satisfies the builtin error interface +func (e LeaderHealthCheckRequestValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sLeaderHealthCheckRequest.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = LeaderHealthCheckRequestValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = LeaderHealthCheckRequestValidationError{} + +// Validate checks the field values on LeaderHealthCheckResponse with the rules +// defined in the proto definition for this message. If any rules are +// violated, the first error encountered is returned, or nil if there are no violations. +func (m *LeaderHealthCheckResponse) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on LeaderHealthCheckResponse with the +// rules defined in the proto definition for this message. If any rules are +// violated, the result is a list of violation errors wrapped in +// LeaderHealthCheckResponseMultiError, or nil if none found. +func (m *LeaderHealthCheckResponse) ValidateAll() error { + return m.validate(true) +} + +func (m *LeaderHealthCheckResponse) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + if len(errors) > 0 { + return LeaderHealthCheckResponseMultiError(errors) + } + + return nil +} + +// LeaderHealthCheckResponseMultiError is an error wrapping multiple validation +// errors returned by LeaderHealthCheckResponse.ValidateAll() if the +// designated constraints aren't met. +type LeaderHealthCheckResponseMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m LeaderHealthCheckResponseMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m LeaderHealthCheckResponseMultiError) AllErrors() []error { return m } + +// LeaderHealthCheckResponseValidationError is the validation error returned by +// LeaderHealthCheckResponse.Validate if the designated constraints aren't met. +type LeaderHealthCheckResponseValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e LeaderHealthCheckResponseValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e LeaderHealthCheckResponseValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e LeaderHealthCheckResponseValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e LeaderHealthCheckResponseValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e LeaderHealthCheckResponseValidationError) ErrorName() string { + return "LeaderHealthCheckResponseValidationError" +} + +// Error satisfies the builtin error interface +func (e LeaderHealthCheckResponseValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sLeaderHealthCheckResponse.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = LeaderHealthCheckResponseValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = LeaderHealthCheckResponseValidationError{} + // Validate checks the field values on CheckUpdatesRequest with the rules // defined in the proto definition for this message. If any rules are // violated, the first error encountered is returned, or nil if there are no violations. diff --git a/api/serverpb/server.proto b/api/serverpb/server.proto index 616b91fd43..4b254b34aa 100644 --- a/api/serverpb/server.proto +++ b/api/serverpb/server.proto @@ -53,6 +53,12 @@ message ReadinessResponse { // This probe is available without authentication, so it should not contain any data. } +message LeaderHealthCheckRequest {} + +message LeaderHealthCheckResponse { + // This probe is available without authentication, so it should not contain any data. +} + message CheckUpdatesRequest { // If false, cached information may be returned. bool force = 1; @@ -296,6 +302,17 @@ service Server { description: "Returns an error when Server components being restarted are not ready yet. Use this API for checking the health of Docker containers and for probing Kubernetes readiness." }; } + // LeaderHealthCheck checks if the instance is the leader in a cluster. + rpc LeaderHealthCheck(LeaderHealthCheckRequest) returns (LeaderHealthCheckResponse) { + option (google.api.http) = { + post: "/v1/leaderHealthCheck" + body: "*" + }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + summary: "Check Leadership" + description: "Checks if the instance is the leader in a cluster. Returns an error if the instance isn't the leader." + }; + } // CheckUpdates checks for available PMM Server updates. rpc CheckUpdates(CheckUpdatesRequest) returns (CheckUpdatesResponse) { option (google.api.http) = { diff --git a/api/serverpb/server_grpc.pb.go b/api/serverpb/server_grpc.pb.go index 63197e8537..de69a805f0 100644 --- a/api/serverpb/server_grpc.pb.go +++ b/api/serverpb/server_grpc.pb.go @@ -22,6 +22,7 @@ const _ = grpc.SupportPackageIsVersion7 const ( Server_Version_FullMethodName = "/server.Server/Version" Server_Readiness_FullMethodName = "/server.Server/Readiness" + Server_LeaderHealthCheck_FullMethodName = "/server.Server/LeaderHealthCheck" Server_CheckUpdates_FullMethodName = "/server.Server/CheckUpdates" Server_StartUpdate_FullMethodName = "/server.Server/StartUpdate" Server_UpdateStatus_FullMethodName = "/server.Server/UpdateStatus" @@ -40,6 +41,8 @@ type ServerClient interface { // Readiness returns an error when Server components being restarted are not ready yet. // Use this API for checking the health of Docker containers and for probing Kubernetes readiness. Readiness(ctx context.Context, in *ReadinessRequest, opts ...grpc.CallOption) (*ReadinessResponse, error) + // LeaderHealthCheck checks if the instance is the leader in a cluster. + LeaderHealthCheck(ctx context.Context, in *LeaderHealthCheckRequest, opts ...grpc.CallOption) (*LeaderHealthCheckResponse, error) // CheckUpdates checks for available PMM Server updates. CheckUpdates(ctx context.Context, in *CheckUpdatesRequest, opts ...grpc.CallOption) (*CheckUpdatesResponse, error) // StartUpdate starts PMM Server update. @@ -82,6 +85,15 @@ func (c *serverClient) Readiness(ctx context.Context, in *ReadinessRequest, opts return out, nil } +func (c *serverClient) LeaderHealthCheck(ctx context.Context, in *LeaderHealthCheckRequest, opts ...grpc.CallOption) (*LeaderHealthCheckResponse, error) { + out := new(LeaderHealthCheckResponse) + err := c.cc.Invoke(ctx, Server_LeaderHealthCheck_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *serverClient) CheckUpdates(ctx context.Context, in *CheckUpdatesRequest, opts ...grpc.CallOption) (*CheckUpdatesResponse, error) { out := new(CheckUpdatesResponse) err := c.cc.Invoke(ctx, Server_CheckUpdates_FullMethodName, in, out, opts...) @@ -154,6 +166,8 @@ type ServerServer interface { // Readiness returns an error when Server components being restarted are not ready yet. // Use this API for checking the health of Docker containers and for probing Kubernetes readiness. Readiness(context.Context, *ReadinessRequest) (*ReadinessResponse, error) + // LeaderHealthCheck checks if the instance is the leader in a cluster. + LeaderHealthCheck(context.Context, *LeaderHealthCheckRequest) (*LeaderHealthCheckResponse, error) // CheckUpdates checks for available PMM Server updates. CheckUpdates(context.Context, *CheckUpdatesRequest) (*CheckUpdatesResponse, error) // StartUpdate starts PMM Server update. @@ -182,6 +196,10 @@ func (UnimplementedServerServer) Readiness(context.Context, *ReadinessRequest) ( return nil, status.Errorf(codes.Unimplemented, "method Readiness not implemented") } +func (UnimplementedServerServer) LeaderHealthCheck(context.Context, *LeaderHealthCheckRequest) (*LeaderHealthCheckResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method LeaderHealthCheck not implemented") +} + func (UnimplementedServerServer) CheckUpdates(context.Context, *CheckUpdatesRequest) (*CheckUpdatesResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method CheckUpdates not implemented") } @@ -258,6 +276,24 @@ func _Server_Readiness_Handler(srv interface{}, ctx context.Context, dec func(in return interceptor(ctx, in, info, handler) } +func _Server_LeaderHealthCheck_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(LeaderHealthCheckRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ServerServer).LeaderHealthCheck(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Server_LeaderHealthCheck_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ServerServer).LeaderHealthCheck(ctx, req.(*LeaderHealthCheckRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _Server_CheckUpdates_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(CheckUpdatesRequest) if err := dec(in); err != nil { @@ -399,6 +435,10 @@ var Server_ServiceDesc = grpc.ServiceDesc{ MethodName: "Readiness", Handler: _Server_Readiness_Handler, }, + { + MethodName: "LeaderHealthCheck", + Handler: _Server_LeaderHealthCheck_Handler, + }, { MethodName: "CheckUpdates", Handler: _Server_CheckUpdates_Handler, diff --git a/api/swagger/swagger-dev.json b/api/swagger/swagger-dev.json index b42664e002..20fda6ab9d 100644 --- a/api/swagger/swagger-dev.json +++ b/api/swagger/swagger-dev.json @@ -17824,6 +17824,68 @@ } } }, + "/v1/leaderHealthCheck": { + "post": { + "description": "Checks if the instance is the leader in a cluster. Returns an error if the instance isn't the leader.", + "tags": [ + "Server" + ], + "summary": "Check Leadership", + "operationId": "LeaderHealthCheck", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object" + } + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "description": "This probe is available without authentication, so it should not contain any data.", + "type": "object" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32", + "x-order": 0 + }, + "message": { + "type": "string", + "x-order": 1 + }, + "details": { + "type": "array", + "items": { + "description": "`Any` contains an arbitrary serialized protocol buffer message along with a\nURL that describes the type of the serialized message.\n\nProtobuf library provides support to pack/unpack Any values in the form\nof utility functions or additional generated methods of the Any type.\n\nExample 1: Pack and unpack a message in C++.\n\n Foo foo = ...;\n Any any;\n any.PackFrom(foo);\n ...\n if (any.UnpackTo(\u0026foo)) {\n ...\n }\n\nExample 2: Pack and unpack a message in Java.\n\n Foo foo = ...;\n Any any = Any.pack(foo);\n ...\n if (any.is(Foo.class)) {\n foo = any.unpack(Foo.class);\n }\n // or ...\n if (any.isSameTypeAs(Foo.getDefaultInstance())) {\n foo = any.unpack(Foo.getDefaultInstance());\n }\n\n Example 3: Pack and unpack a message in Python.\n\n foo = Foo(...)\n any = Any()\n any.Pack(foo)\n ...\n if any.Is(Foo.DESCRIPTOR):\n any.Unpack(foo)\n ...\n\n Example 4: Pack and unpack a message in Go\n\n foo := \u0026pb.Foo{...}\n any, err := anypb.New(foo)\n if err != nil {\n ...\n }\n ...\n foo := \u0026pb.Foo{}\n if err := any.UnmarshalTo(foo); err != nil {\n ...\n }\n\nThe pack methods provided by protobuf library will by default use\n'type.googleapis.com/full.type.name' as the type URL and the unpack\nmethods only use the fully qualified type name after the last '/'\nin the type URL, for example \"foo.bar.com/x/y.z\" will yield type\nname \"y.z\".\n\nJSON\n====\nThe JSON representation of an `Any` value uses the regular\nrepresentation of the deserialized, embedded message, with an\nadditional field `@type` which contains the type URL. Example:\n\n package google.profile;\n message Person {\n string first_name = 1;\n string last_name = 2;\n }\n\n {\n \"@type\": \"type.googleapis.com/google.profile.Person\",\n \"firstName\": \u003cstring\u003e,\n \"lastName\": \u003cstring\u003e\n }\n\nIf the embedded message type is well-known and has a custom JSON\nrepresentation, that representation will be embedded adding a field\n`value` which holds the custom JSON in addition to the `@type`\nfield. Example (for message [google.protobuf.Duration][]):\n\n {\n \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n \"value\": \"1.212s\"\n }", + "type": "object", + "properties": { + "@type": { + "description": "A URL/resource name that uniquely identifies the type of the serialized\nprotocol buffer message. This string must contain at least\none \"/\" character. The last segment of the URL's path must represent\nthe fully qualified name of the type (as in\n`path/google.protobuf.Duration`). The name should be in a canonical form\n(e.g., leading \".\" is not accepted).\n\nIn practice, teams usually precompile into the binary all types that they\nexpect it to use in the context of Any. However, for URLs which use the\nscheme `http`, `https`, or no scheme, one can optionally set up a type\nserver that maps type URLs to message definitions as follows:\n\n* If no scheme is provided, `https` is assumed.\n* An HTTP GET on the URL must yield a [google.protobuf.Type][]\n value in binary format, or produce an error.\n* Applications are allowed to cache lookup results based on the\n URL, or have them precompiled into a binary to avoid any\n lookup. Therefore, binary compatibility needs to be preserved\n on changes to types. (Use versioned type names to manage\n breaking changes.)\n\nNote: this functionality is not currently available in the official\nprotobuf release, and it is not used for type URLs beginning with\ntype.googleapis.com. As of May 2023, there are no widely used type server\nimplementations and no plans to implement one.\n\nSchemes other than `http`, `https` (or the empty scheme) might be\nused with implementation specific semantics.", + "type": "string", + "x-order": 0 + } + }, + "additionalProperties": false + }, + "x-order": 2 + } + } + } + } + } + } + }, "/v1/management/Actions/Cancel": { "post": { "description": "Stops an Action.", diff --git a/api/swagger/swagger.json b/api/swagger/swagger.json index 03186de36b..9baf6b7a01 100644 --- a/api/swagger/swagger.json +++ b/api/swagger/swagger.json @@ -14980,6 +14980,68 @@ } } }, + "/v1/leaderHealthCheck": { + "post": { + "description": "Checks if the instance is the leader in a cluster. Returns an error if the instance isn't the leader.", + "tags": [ + "Server" + ], + "summary": "Check Leadership", + "operationId": "LeaderHealthCheck", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object" + } + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "description": "This probe is available without authentication, so it should not contain any data.", + "type": "object" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32", + "x-order": 0 + }, + "message": { + "type": "string", + "x-order": 1 + }, + "details": { + "type": "array", + "items": { + "description": "`Any` contains an arbitrary serialized protocol buffer message along with a\nURL that describes the type of the serialized message.\n\nProtobuf library provides support to pack/unpack Any values in the form\nof utility functions or additional generated methods of the Any type.\n\nExample 1: Pack and unpack a message in C++.\n\n Foo foo = ...;\n Any any;\n any.PackFrom(foo);\n ...\n if (any.UnpackTo(\u0026foo)) {\n ...\n }\n\nExample 2: Pack and unpack a message in Java.\n\n Foo foo = ...;\n Any any = Any.pack(foo);\n ...\n if (any.is(Foo.class)) {\n foo = any.unpack(Foo.class);\n }\n // or ...\n if (any.isSameTypeAs(Foo.getDefaultInstance())) {\n foo = any.unpack(Foo.getDefaultInstance());\n }\n\n Example 3: Pack and unpack a message in Python.\n\n foo = Foo(...)\n any = Any()\n any.Pack(foo)\n ...\n if any.Is(Foo.DESCRIPTOR):\n any.Unpack(foo)\n ...\n\n Example 4: Pack and unpack a message in Go\n\n foo := \u0026pb.Foo{...}\n any, err := anypb.New(foo)\n if err != nil {\n ...\n }\n ...\n foo := \u0026pb.Foo{}\n if err := any.UnmarshalTo(foo); err != nil {\n ...\n }\n\nThe pack methods provided by protobuf library will by default use\n'type.googleapis.com/full.type.name' as the type URL and the unpack\nmethods only use the fully qualified type name after the last '/'\nin the type URL, for example \"foo.bar.com/x/y.z\" will yield type\nname \"y.z\".\n\nJSON\n====\nThe JSON representation of an `Any` value uses the regular\nrepresentation of the deserialized, embedded message, with an\nadditional field `@type` which contains the type URL. Example:\n\n package google.profile;\n message Person {\n string first_name = 1;\n string last_name = 2;\n }\n\n {\n \"@type\": \"type.googleapis.com/google.profile.Person\",\n \"firstName\": \u003cstring\u003e,\n \"lastName\": \u003cstring\u003e\n }\n\nIf the embedded message type is well-known and has a custom JSON\nrepresentation, that representation will be embedded adding a field\n`value` which holds the custom JSON in addition to the `@type`\nfield. Example (for message [google.protobuf.Duration][]):\n\n {\n \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n \"value\": \"1.212s\"\n }", + "type": "object", + "properties": { + "@type": { + "description": "A URL/resource name that uniquely identifies the type of the serialized\nprotocol buffer message. This string must contain at least\none \"/\" character. The last segment of the URL's path must represent\nthe fully qualified name of the type (as in\n`path/google.protobuf.Duration`). The name should be in a canonical form\n(e.g., leading \".\" is not accepted).\n\nIn practice, teams usually precompile into the binary all types that they\nexpect it to use in the context of Any. However, for URLs which use the\nscheme `http`, `https`, or no scheme, one can optionally set up a type\nserver that maps type URLs to message definitions as follows:\n\n* If no scheme is provided, `https` is assumed.\n* An HTTP GET on the URL must yield a [google.protobuf.Type][]\n value in binary format, or produce an error.\n* Applications are allowed to cache lookup results based on the\n URL, or have them precompiled into a binary to avoid any\n lookup. Therefore, binary compatibility needs to be preserved\n on changes to types. (Use versioned type names to manage\n breaking changes.)\n\nNote: this functionality is not currently available in the official\nprotobuf release, and it is not used for type URLs beginning with\ntype.googleapis.com. As of May 2023, there are no widely used type server\nimplementations and no plans to implement one.\n\nSchemes other than `http`, `https` (or the empty scheme) might be\nused with implementation specific semantics.", + "type": "string", + "x-order": 0 + } + }, + "additionalProperties": false + }, + "x-order": 2 + } + } + } + } + } + } + }, "/v1/management/Actions/Cancel": { "post": { "description": "Stops an Action.", diff --git a/docker-compose.yml b/docker-compose.yml index bf01cf3649..dbfcbdc1d4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -104,19 +104,23 @@ services: # PMM with external DBs ch: profiles: - - pmm-external-dbs - image: ${CH_IMAGE:-clickhouse/clickhouse-server:22.6.9.11-alpine} + - pmm-ha + image: ${CH_IMAGE:-clickhouse/clickhouse-server:23.8.2.7-alpine} platform: linux/amd64 hostname: ${CH_HOSTNAME:-ch} ports: - ${CH_PORT:-9000}:9000 networks: - - ${NETWORK:-default} + ha: + ipv4_address: 172.20.0.7 + volumes: + - chdata:/var/lib/clickhouse # Volume for ClickHouse data + victoriametrics: profiles: - - pmm-external-dbs + - pmm-ha hostname: ${VM_HOSTNAME:-victoriametrics} - image: victoriametrics/victoria-metrics:v1.88.1 + image: victoriametrics/victoria-metrics:v1.93.4 ports: - 8428:8428 - 8089:8089 @@ -133,20 +137,79 @@ services: - "--httpListenAddr=:8428" - "--influxListenAddr=:8089" networks: - - ${NETWORK:-default} - pmm-managed-server-ch: + ha: + ipv4_address: 172.20.0.4 + + # PMM with external Postgres DB + pg: + profiles: + - pmm-ha + build: + context: ./managed/testdata/pg + args: + POSTGRES_IMAGE: ${POSTGRES_IMAGE:-postgres:14} + dockerfile: Dockerfile +# image: postgres:14 + container_name: pg + environment: + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-pmm-password} + ports: + - ${POSTGRES_PORT:-5432}:5432 + command: | + postgres + -c shared_preload_libraries=pg_stat_statements + -c pg_stat_statements.max=10000 + -c pg_stat_statements.track=all + -c pg_stat_statements.save=off + -c fsync=off +# -c hba_file=/conf/pg_hba.conf +# -c ssl=on +# -c ssl_ca_file=/certs/root.crt +# -c ssl_cert_file=/certs/server.crt +# -c ssl_key_file=/certs/server.key + networks: + ha: + ipv4_address: 172.20.0.3 + volumes: + - pgdata:/var/lib/postgresql/data # Volume for PostgreSQL data + - ./managed/testdata/pg/conf/:/conf/ + - ./managed/testdata/pg/queries/:/docker-entrypoint-initdb.d/ + + haproxy: + profiles: + - pmm-ha + image: haproxy:latest + container_name: haproxy + hostname: haproxy + networks: + ha: + ipv4_address: 172.20.0.10 + volumes: + - ./managed/testdata/haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg + - ./managed/testdata/haproxy/localhost.pem:/etc/ssl/private/localhost.pem + ports: + - 80:80 + - 443:443 + depends_on: + - pmm-server-active + - pmm-server-passive + - pmm-server-passive-2 + + pmm-server-active: profiles: - - pmm-external-dbs + - pmm-ha depends_on: - ch - victoriametrics + - pg image: ${PMM_CONTAINER:-perconalab/pmm-server:dev-container} - container_name: pmm-server - hostname: pmm-server + container_name: pmm-server-active + hostname: pmm-server-active networks: - - ${NETWORK:-default} + ha: + ipv4_address: 172.20.0.5 environment: - - PMM_RELEASE_PATH=/root/go/bin + - PMM_RELEASE_PATH=/root/go/src/github.com/percona/pmm/bin - REVIEWDOG_GITHUB_API_TOKEN=${REVIEWDOG_GITHUB_API_TOKEN} - ENABLE_DBAAS=${ENABLE_DBAAS:-0} - AWS_ACCESS_KEY=${AWS_ACCESS_KEY} @@ -162,9 +225,28 @@ services: - PERCONA_TEST_PMM_CLICKHOUSE_DATABASE=pmm - PERCONA_TEST_PMM_CLICKHOUSE_BLOCK_SIZE=10000 - PERCONA_TEST_PMM_CLICKHOUSE_POOL_SIZE=2 + - PERCONA_TEST_PMM_DISABLE_BUILTIN_CLICKHOUSE=1 + - PERCONA_TEST_POSTGRES_ADDR=pg:5432 + - PERCONA_TEST_POSTGRES_USERNAME=pmm-managed + - PERCONA_TEST_POSTGRES_DBPASSWORD=pmm-managed + - PERCONA_TEST_PMM_DISABLE_BUILTIN_POSTGRES=1 +# - PERCONA_TEST_POSTGRES_SSL_MODE=require +# - PERCONA_TEST_POSTGRES_SSL_CA_PATH=/certs/root.crt +# - PERCONA_TEST_POSTGRES_SSL_KEY_PATH=/certs/pmm-managed.key +# - PERCONA_TEST_POSTGRES_SSL_CERT_PATH=/certs/pmm-managed.crt + - GF_DATABASE_URL=postgres://grafana:grafana@pg:5432/grafana +# - GF_DATABASE_SSL_MODE=require +# - PMM_DEBUG=1 + - GO_VERSION=1.20 - PMM_VM_URL=${PMM_VM_URL:-http://victoriametrics:8428/} - - PMM_DEBUG=1 - PERCONA_TEST_DBAAS_PMM_CLIENT=perconalab/pmm-client:dev-latest + - PMM_TEST_HA_ENABLE=1 + - PMM_TEST_HA_BOOTSTRAP=1 + - PMM_TEST_HA_NODE_ID=pmm-server-active + - PMM_TEST_HA_ADVERTISE_ADDRESS=172.20.0.5 + - PMM_TEST_HA_PEERS=pmm-server-active,pmm-server-passive,pmm-server-passive-2 + - PMM_TEST_HA_GOSSIP_PORT=9096 + - PMM_TEST_HA_GRAFANA_GOSSIP_PORT=9094 extra_hosts: - host.docker.internal:host-gateway @@ -184,8 +266,8 @@ services: memlock: 67108864 ports: - - ${PMM_PORT_HTTP:-80}:80 - - ${PMM_PORT_HTTPS:-443}:443 + - ${PMM_PORT_HTTP:-8081}:80 + - ${PMM_PORT_HTTPS:-8441}:443 # For headless delve - ${PMM_PORT_DELVE:-2345}:2345 volumes: @@ -195,37 +277,189 @@ services: # caching - go-modules:/root/go/pkg/mod - root-cache:/root/.cache + - ./managed/testdata/pg/certs/:/certs/ + - ./update/ansible:/usr/share/pmm-update/ansible - # PMM with external Postgres DB - pg: + pmm-server-passive: profiles: - - pmm-pg - build: - context: ./managed/testdata/pg - args: - POSTGRES_IMAGE: ${POSTGRES_IMAGE:-postgres:14} - dockerfile: Dockerfile - container_name: pmm-pg + - pmm-ha + depends_on: + - ch + - pg + - victoriametrics + - pmm-server-active + image: ${PMM_CONTAINER:-perconalab/pmm-server:dev-container} + container_name: pmm-server-passive + hostname: pmm-server-passive + networks: + ha: + ipv4_address: 172.20.0.6 environment: - - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-pmm-password} + - PMM_RELEASE_PATH=/root/go/src/github.com/percona/pmm/bin + - REVIEWDOG_GITHUB_API_TOKEN=${REVIEWDOG_GITHUB_API_TOKEN} + - ENABLE_DBAAS=${ENABLE_DBAAS:-0} + - AWS_ACCESS_KEY=${AWS_ACCESS_KEY} + - AWS_SECRET_KEY=${AWS_SECRET_KEY} +# - PERCONA_TEST_PLATFORM_ADDRESS=https://check.localhost +# - PERCONA_TEST_PLATFORM_INSECURE=1 +# - PERCONA_TEST_PLATFORM_PUBLIC_KEY= +# - PERCONA_TEST_TELEMETRY_INTERVAL=10s +# - PERCONA_TEST_TELEMETRY_RETRY_BACKOFF=10s +# - PERCONA_TEST_TELEMETRY_DISABLE_START_DELAY=1 + - PERCONA_TEST_PMM_CLICKHOUSE_ADDR=${CH_HOSTNAME:-ch}:9000 + - PERCONA_TEST_PMM_CLICKHOUSE_DATABASE=pmm + - PERCONA_TEST_PMM_CLICKHOUSE_BLOCK_SIZE=10000 + - PERCONA_TEST_PMM_CLICKHOUSE_POOL_SIZE=2 + - PERCONA_TEST_PMM_DISABLE_BUILTIN_CLICKHOUSE=1 + - PERCONA_TEST_POSTGRES_ADDR=pg:5432 + - PERCONA_TEST_POSTGRES_USERNAME=pmm-managed + - PERCONA_TEST_POSTGRES_DBPASSWORD=pmm-managed + - PERCONA_TEST_PMM_DISABLE_BUILTIN_POSTGRES=1 +# - PERCONA_TEST_POSTGRES_SSL_MODE=require +# - PERCONA_TEST_POSTGRES_SSL_CA_PATH=/certs/root.crt +# - PERCONA_TEST_POSTGRES_SSL_KEY_PATH=/certs/pmm-managed.key +# - PERCONA_TEST_POSTGRES_SSL_CERT_PATH=/certs/pmm-managed.crt + - GF_DATABASE_URL=postgres://grafana:grafana@pg:5432/grafana +# - GF_DATABASE_SSL_MODE=require +# - PMM_DEBUG=1 + - GO_VERSION=1.20 + - PMM_VM_URL=${PMM_VM_URL:-http://victoriametrics:8428/} + - PERCONA_TEST_DBAAS_PMM_CLIENT=perconalab/pmm-client:dev-latest + - PMM_TEST_HA_ENABLE=1 + - PMM_TEST_HA_NODE_ID=pmm-server-passive + - PMM_TEST_HA_ADVERTISE_ADDRESS=172.20.0.6 + - PMM_TEST_HA_PEERS=pmm-server-active,pmm-server-passive,pmm-server-passive-2 + - PMM_TEST_HA_GOSSIP_PORT=9096 + - PMM_TEST_HA_GRAFANA_GOSSIP_PORT=9094 + + extra_hosts: + - host.docker.internal:host-gateway + # - portal.localhost:${PORTAL_HOST:-host-gateway} + # - check.localhost:${PORTAL_CHECK_HOST:-host-gateway} + # - pmm.localhost:${PORTAL_PMM_HOST:-host-gateway} + # - check-dev.percona.com:${PORTAL_PMM_HOST:-host-gateway} + + # for delve + cap_add: + - SYS_PTRACE + security_opt: + - seccomp:unconfined + + # see https://github.com/golang/go/wiki/LinuxKernelSignalVectorBug#what-to-do + ulimits: + memlock: 67108864 + ports: - - ${POSTGRES_PORT:-5432}:5432 - command: | - postgres - -c shared_preload_libraries=pg_stat_statements - -c pg_stat_statements.max=10000 - -c pg_stat_statements.track=all - -c pg_stat_statements.save=off - -c fsync=off - -c ssl=on - -c ssl_ca_file=/certs/root.crt - -c ssl_cert_file=/certs/server.crt - -c ssl_key_file=/certs/server.key - -c hba_file=/conf/pg_hba.conf + - ${PMM_PORT_HTTP:-8082}:80 + - ${PMM_PORT_HTTPS:-8432}:443 + # For headless delve + - ${PMM_PORT_DELVE:-12345}:2345 + volumes: + - ./:/root/go/src/github.com/percona/pmm +# - "../grafana/public:/usr/share/grafana/public" + - ./Makefile.devcontainer:/root/go/src/github.com/percona/pmm/Makefile:ro # change Makefile in devcontainer + # caching + - go-modules:/root/go/pkg/mod + - root-cache:/root/.cache + - ./managed/testdata/pg/certs/:/certs/ + - ./update/ansible:/usr/share/pmm-update/ansible + + pmm-server-passive-2: + profiles: + - pmm-ha + depends_on: + - ch + - pg + - victoriametrics + - pmm-server-active + image: ${PMM_CONTAINER:-perconalab/pmm-server:dev-container} + container_name: pmm-server-passive-2 + hostname: pmm-server-passive-2 networks: - - ${NETWORK:-default} + ha: + ipv4_address: 172.20.0.11 + environment: + - PMM_RELEASE_PATH=/root/go/src/github.com/percona/pmm/bin + - REVIEWDOG_GITHUB_API_TOKEN=${REVIEWDOG_GITHUB_API_TOKEN} + - ENABLE_DBAAS=${ENABLE_DBAAS:-0} + - AWS_ACCESS_KEY=${AWS_ACCESS_KEY} + - AWS_SECRET_KEY=${AWS_SECRET_KEY} + # - PERCONA_TEST_PLATFORM_ADDRESS=https://check.localhost + # - PERCONA_TEST_PLATFORM_INSECURE=1 + # - PERCONA_TEST_PLATFORM_PUBLIC_KEY= + # - PERCONA_TEST_TELEMETRY_INTERVAL=10s + # - PERCONA_TEST_TELEMETRY_RETRY_BACKOFF=10s + # - PERCONA_TEST_TELEMETRY_DISABLE_START_DELAY=1 + - PERCONA_TEST_PMM_CLICKHOUSE_ADDR=${CH_HOSTNAME:-ch}:9000 + - PERCONA_TEST_PMM_CLICKHOUSE_DATABASE=pmm + - PERCONA_TEST_PMM_CLICKHOUSE_BLOCK_SIZE=10000 + - PERCONA_TEST_PMM_CLICKHOUSE_POOL_SIZE=2 + - PERCONA_TEST_PMM_DISABLE_BUILTIN_CLICKHOUSE=1 + - PERCONA_TEST_POSTGRES_ADDR=pg:5432 + - PERCONA_TEST_POSTGRES_USERNAME=pmm-managed + - PERCONA_TEST_POSTGRES_DBPASSWORD=pmm-managed + - PERCONA_TEST_PMM_DISABLE_BUILTIN_POSTGRES=1 + # - PERCONA_TEST_POSTGRES_SSL_MODE=require + # - PERCONA_TEST_POSTGRES_SSL_CA_PATH=/certs/root.crt + # - PERCONA_TEST_POSTGRES_SSL_KEY_PATH=/certs/pmm-managed.key + # - PERCONA_TEST_POSTGRES_SSL_CERT_PATH=/certs/pmm-managed.crt + - GF_DATABASE_URL=postgres://grafana:grafana@pg:5432/grafana + # - GF_DATABASE_SSL_MODE=require + # - PMM_DEBUG=1 + - GO_VERSION=1.20 + - PMM_VM_URL=${PMM_VM_URL:-http://victoriametrics:8428/} + - PERCONA_TEST_DBAAS_PMM_CLIENT=perconalab/pmm-client:dev-latest + - PMM_TEST_HA_ENABLE=1 + - PMM_TEST_HA_NODE_ID=pmm-server-passive-2 + - PMM_TEST_HA_ADVERTISE_ADDRESS=172.20.0.11 + - PMM_TEST_HA_PEERS=pmm-server-active,pmm-server-passive,pmm-server-passive-2 + - PMM_TEST_HA_GOSSIP_PORT=9096 + - PMM_TEST_HA_GRAFANA_GOSSIP_PORT=9094 + + extra_hosts: + - host.docker.internal:host-gateway + # - portal.localhost:${PORTAL_HOST:-host-gateway} + # - check.localhost:${PORTAL_CHECK_HOST:-host-gateway} + # - pmm.localhost:${PORTAL_PMM_HOST:-host-gateway} + # - check-dev.percona.com:${PORTAL_PMM_HOST:-host-gateway} + + # for delve + cap_add: + - SYS_PTRACE + security_opt: + - seccomp:unconfined + + # see https://github.com/golang/go/wiki/LinuxKernelSignalVectorBug#what-to-do + ulimits: + memlock: 67108864 + + ports: + - ${PMM_PORT_HTTP:-8083}:80 + - ${PMM_PORT_HTTPS:-8433}:443 + # For headless delve +# - ${PMM_PORT_DELVE:-12345}:2345 + volumes: + - ./:/root/go/src/github.com/percona/pmm + # - "../grafana/public:/usr/share/grafana/public" + - ./Makefile.devcontainer:/root/go/src/github.com/percona/pmm/Makefile:ro # change Makefile in devcontainer + # caching + - go-modules:/root/go/pkg/mod + - root-cache:/root/.cache + - ./managed/testdata/pg/certs/:/certs/ + - ./update/ansible:/usr/share/pmm-update/ansible volumes: + chdata: # Volume for ClickHouse data + vmdata: # Volume for VictoriaMetrics data + pgdata: # Volume for PostgreSQL data go-modules: - vmdata: {} root-cache: + +networks: + minikube: + external: true + name: minikube + ha: + ipam: + config: + - subnet: 172.20.0.0/24 diff --git a/go.mod b/go.mod index acdbb3ee4f..7c0add89df 100644 --- a/go.mod +++ b/go.mod @@ -48,6 +48,7 @@ require ( github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 github.com/hashicorp/go-version v1.6.0 + github.com/hashicorp/raft v1.5.0 github.com/jhunters/bigqueue v1.2.7 github.com/jmoiron/sqlx v1.3.5 github.com/jotaen/kong-completion v0.0.5 @@ -107,6 +108,7 @@ require ( github.com/emicklei/go-restful/v3 v3.9.0 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect + github.com/fatih/color v1.13.0 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-ini/ini v1.67.0 // indirect github.com/go-logr/logr v1.2.4 // indirect @@ -117,10 +119,12 @@ require ( github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect + github.com/hashicorp/go-hclog v1.5.0 // indirect github.com/hashicorp/go-uuid v1.0.2 // indirect github.com/hashicorp/golang-lru/v2 v2.0.2 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/kr/fs v0.1.0 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-ieproxy v0.0.1 // indirect github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/minio/minio-go v6.0.14+incompatible // indirect @@ -163,7 +167,7 @@ require ( github.com/Microsoft/go-winio v0.6.1 // indirect github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect github.com/andybalholm/brotli v1.0.6 // indirect - github.com/armon/go-metrics v0.4.0 // indirect + github.com/armon/go-metrics v0.4.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect @@ -190,11 +194,11 @@ require ( github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect - github.com/hashicorp/go-msgpack v0.5.5 // indirect + github.com/hashicorp/go-msgpack v1.1.5 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-sockaddr v1.0.2 // indirect github.com/hashicorp/golang-lru v0.6.0 // indirect - github.com/hashicorp/memberlist v0.5.0 // indirect + github.com/hashicorp/memberlist v0.5.0 github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/jpillora/backoff v1.0.0 // indirect diff --git a/go.sum b/go.sum index 833a7ededd..c4285512b7 100644 --- a/go.sum +++ b/go.sum @@ -111,8 +111,8 @@ github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sx github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-metrics v0.4.0 h1:yCQqn7dwca4ITXb+CbubHmedzaQYHhNhrEXLYUeEe8Q= -github.com/armon/go-metrics v0.4.0/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= +github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= +github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= @@ -209,6 +209,8 @@ github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLi github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/frankban/quicktest v1.5.0/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o= @@ -422,12 +424,15 @@ github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brv github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= +github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-msgpack v1.1.5 h1:9byZdVjKTe5mce63pRVNP1L7UAmdHOTEMGehn6KvJWs= +github.com/hashicorp/go-msgpack v1.1.5/go.mod h1:gWVc3sv/wbDmR3rQsj1CAktEZzoz1YNK9NfGLXJ69/4= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= @@ -448,6 +453,8 @@ github.com/hashicorp/golang-lru/v2 v2.0.2 h1:Dwmkdr5Nc/oBiXgJS3CDHNhJtIHkuZ3DZF5 github.com/hashicorp/golang-lru/v2 v2.0.2/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM= github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= +github.com/hashicorp/raft v1.5.0 h1:uNs9EfJ4FwiArZRxxfd/dQ5d33nV31/CdCHArH89hT8= +github.com/hashicorp/raft v1.5.0/go.mod h1:pKHB2mf/Y25u3AHNSXVRv+yT+WAnmeTX0BwVppVQV+M= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -536,11 +543,15 @@ github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsI github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-ieproxy v0.0.1 h1:qiyop7gCflfhwCzGyeT0gro3sF9AIg9HU98JORTkqfI= github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= @@ -776,6 +787,7 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= @@ -994,7 +1006,9 @@ golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1048,6 +1062,7 @@ golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190424220101-1e8e1cfdf96b/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= diff --git a/managed/cmd/pmm-managed-init/main.go b/managed/cmd/pmm-managed-init/main.go index 1d52155db1..8aa0046e3b 100644 --- a/managed/cmd/pmm-managed-init/main.go +++ b/managed/cmd/pmm-managed-init/main.go @@ -54,6 +54,7 @@ func main() { pmmConfigParams := make(map[string]any) pmmConfigParams["DisableInternalDB"], _ = strconv.ParseBool(os.Getenv("PERCONA_TEST_PMM_DISABLE_BUILTIN_POSTGRES")) + pmmConfigParams["DisableInternalClickhouse"], _ = strconv.ParseBool(os.Getenv("PERCONA_TEST_PMM_DISABLE_BUILTIN_CLICKHOUSE")) if err := supervisord.SavePMMConfig(pmmConfigParams); err != nil { logrus.Errorf("PMM Server configuration error: %s.", err) os.Exit(1) diff --git a/managed/cmd/pmm-managed/main.go b/managed/cmd/pmm-managed/main.go index f2aa33e91a..f60a06239e 100644 --- a/managed/cmd/pmm-managed/main.go +++ b/managed/cmd/pmm-managed/main.go @@ -86,6 +86,7 @@ import ( "github.com/percona/pmm/managed/services/dbaas" "github.com/percona/pmm/managed/services/dump" "github.com/percona/pmm/managed/services/grafana" + "github.com/percona/pmm/managed/services/ha" "github.com/percona/pmm/managed/services/inventory" inventorygrpc "github.com/percona/pmm/managed/services/inventory/grpc" "github.com/percona/pmm/managed/services/management" @@ -188,6 +189,7 @@ func addLogsHandler(mux *http.ServeMux, logs *supervisord.Logs) { type gRPCServerDeps struct { db *reform.DB + ha *ha.Service vmdb *victoriametrics.Service platformClient *platformClient.Client server *server.Server @@ -315,7 +317,11 @@ func runGRPCServer(ctx context.Context, deps *gRPCServerDeps) { dumpv1beta1.RegisterDumpsServer(gRPCServer, managementdump.New(deps.db, deps.grafanaClient, deps.dumpService)) k8sServer := managementdbaas.NewKubernetesServer(deps.db, deps.dbaasClient, deps.versionServiceClient, deps.grafanaClient) - deps.dbaasInitializer.RegisterKubernetesServer(k8sServer) + + deps.ha.AddLeaderService(ha.NewContextService("dbaas-register", func(ctx context.Context) error { + deps.dbaasInitializer.RegisterKubernetesServer(k8sServer) + return nil + })) dbaasv1beta1.RegisterKubernetesServer(gRPCServer, k8sServer) dbaasv1beta1.RegisterDBClustersServer(gRPCServer, managementdbaas.NewDBClusterService(deps.db, deps.grafanaClient, deps.versionServiceClient)) dbaasv1beta1.RegisterPXCClustersServer(gRPCServer, managementdbaas.NewPXCClusterService(deps.db, deps.grafanaClient, deps.componentsService, deps.versionServiceClient.GetVersionServiceURL())) @@ -543,6 +549,7 @@ func runDebugServer(ctx context.Context) { type setupDeps struct { sqlDB *sql.DB + ha *ha.Service supervisord *supervisord.Service vmdb *victoriametrics.Service vmalert *vmalert.Service @@ -733,6 +740,34 @@ func main() { //nolint:cyclop,maintidx Envar("PERCONA_TEST_POSTGRES_SSL_CERT_PATH"). String() + haEnabled := kingpin.Flag("ha-enable", "Enable HA"). + Envar("PMM_TEST_HA_ENABLE"). + Bool() + haBootstrap := kingpin.Flag("ha-bootstrap", "Bootstrap HA cluster"). + Envar("PMM_TEST_HA_BOOTSTRAP"). + Bool() + haNodeID := kingpin.Flag("ha-node-id", "HA Node ID"). + Envar("PMM_TEST_HA_NODE_ID"). + String() + haAdvertiseAddress := kingpin.Flag("ha-advertise-address", "HA Advertise address"). + Envar("PMM_TEST_HA_ADVERTISE_ADDRESS"). + String() + haPeers := kingpin.Flag("ha-peers", "HA Peers"). + Envar("PMM_TEST_HA_PEERS"). + String() + haRaftPort := kingpin.Flag("ha-raft-port", "HA raft port"). + Envar("PMM_TEST_HA_RAFT_PORT"). + Default("9760"). + Int() + haGossipPort := kingpin.Flag("ha-gossip-port", "HA gossip port"). + Envar("PMM_TEST_HA_GOSSIP_PORT"). + Default("9761"). + Int() + haGrafanaGossipPort := kingpin.Flag("ha-grafana-gossip-port", "HA Grafana gossip port"). + Envar("PMM_TEST_HA_GRAFANA_GOSSIP_PORT"). + Default("9762"). + Int() + supervisordConfigDirF := kingpin.Flag("supervisord-config-dir", "Supervisord configuration directory").Required().String() logLevelF := kingpin.Flag("log-level", "Set logging level").Envar("PMM_LOG_LEVEL").Default("info").Enum("trace", "debug", "info", "warn", "error", "fatal") @@ -762,6 +797,22 @@ func main() { //nolint:cyclop,maintidx ctx = logger.Set(ctx, "main") defer l.Info("Done.") + var nodes []string + if *haPeers != "" { + nodes = strings.Split(*haPeers, ",") + } + haParams := &models.HAParams{ + Enabled: *haEnabled, + Bootstrap: *haBootstrap, + NodeID: *haNodeID, + AdvertiseAddress: *haAdvertiseAddress, + Nodes: nodes, + RaftPort: *haRaftPort, + GossipPort: *haGossipPort, + GrafanaGossipPort: *haGrafanaGossipPort, + } + haService := ha.New(haParams) + cfg := config.NewService() if err := cfg.Load(); err != nil { l.Panicf("Failed to load config: %+v", err) @@ -824,17 +875,21 @@ func main() { //nolint:cyclop,maintidx } defer sqlDB.Close() //nolint:errcheck - migrateDB(ctx, sqlDB, setupParams) + if haService.Bootstrap() { + migrateDB(ctx, sqlDB, setupParams) + } prom.MustRegister(sqlmetrics.NewCollector("postgres", *postgresDBNameF, sqlDB)) reformL := sqlmetrics.NewReform("postgres", *postgresDBNameF, logrus.WithField("component", "reform").Tracef) prom.MustRegister(reformL) db := reform.NewDB(sqlDB, postgresql.Dialect, reformL) - // Generate unique PMM Server ID if it's not already set. - err = models.SetPMMServerID(db) - if err != nil { - l.Panicf("failed to set PMM Server ID") + if haService.Bootstrap() { + // Generate unique PMM Server ID if it's not already set. + err = models.SetPMMServerID(db) + if err != nil { + l.Panicf("failed to set PMM Server ID") + } } cleaner := clean.New(db) @@ -854,6 +909,12 @@ func main() { //nolint:cyclop,maintidx qanClient := getQANClient(ctx, sqlDB, *postgresDBNameF, *qanAPIAddrF) agentsRegistry := agents.NewRegistry(db, vmParams) + + // TODO remove once PMM cluster will be Active-Active + haService.AddLeaderService(ha.NewStandardService("agentsRegistry", func(ctx context.Context) error { return nil }, func() { + agentsRegistry.KickAll(ctx) + })) + pbmPITRService := backup.NewPBMPITRService() backupRemovalService := backup.NewRemovalService(db, pbmPITRService) backupRetentionService := backup.NewRetentionService(db, backupRemovalService) @@ -878,19 +939,31 @@ func main() { //nolint:cyclop,maintidx supervisord := supervisord.New( *supervisordConfigDirF, pmmUpdateCheck, - vmParams, - models.PGParams{ - Addr: *postgresAddrF, - DBName: *postgresDBNameF, - DBUsername: *postgresDBUsernameF, - DBPassword: *postgresDBPasswordF, - SSLMode: *postgresSSLModeF, - SSLCAPath: *postgresSSLCAPathF, - SSLKeyPath: *postgresSSLKeyPathF, - SSLCertPath: *postgresSSLCertPathF, + &models.Params{ + VMParams: vmParams, + PGParams: &models.PGParams{ + Addr: *postgresAddrF, + DBName: *postgresDBNameF, + DBUsername: *postgresDBUsernameF, + DBPassword: *postgresDBPasswordF, + SSLMode: *postgresSSLModeF, + SSLCAPath: *postgresSSLCAPathF, + SSLKeyPath: *postgresSSLKeyPathF, + SSLCertPath: *postgresSSLCertPathF, + }, + HAParams: haParams, }, gRPCMessageMaxSize) + haService.AddLeaderService(ha.NewStandardService("pmm-agent-runner", func(ctx context.Context) error { + return supervisord.StartSupervisedService("pmm-agent") + }, func() { + err := supervisord.StopSupervisedService("pmm-agent") + if err != nil { + l.Warnf("couldn't stop pmm-agent: %q", err) + } + })) + platformAddress, err := envvars.GetPlatformAddress() if err != nil { l.Fatal(err) @@ -942,7 +1015,9 @@ func main() { //nolint:cyclop,maintidx l.Fatalf("Could not create templates service: %s", err) } // We should collect templates before rules service created, because it will regenerate rule files on startup. - templatesService.CollectTemplates(ctx) + if haService.Bootstrap() { + templatesService.CollectTemplates(ctx) + } rulesService := ia.NewRulesService(db, templatesService, vmalert, alertManager) alertsService := ia.NewAlertsService(db, alertManager, templatesService) @@ -984,6 +1059,7 @@ func main() { //nolint:cyclop,maintidx RulesService: rulesService, DBaaSInitializer: dbaasInitializer, Emailer: emailer, + HAService: haService, } server, err := server.NewServer(serverParams) @@ -1019,6 +1095,7 @@ func main() { //nolint:cyclop,maintidx // try synchronously once, then retry in the background deps := &setupDeps{ sqlDB: sqlDB, + ha: haService, supervisord: supervisord, vmdb: vmdb, vmalert: vmalert, @@ -1046,13 +1123,6 @@ func main() { //nolint:cyclop,maintidx }() } - // Set all agents status to unknown at startup. The ones that are alive - // will get their status updated after they connect to the pmm-managed. - err = agentsHandler.SetAllAgentsStatusUnknown(ctx) - if err != nil { - l.Errorf("Failed to set status of all agents to invalid at startup: %s", err) - } - settings, err := models.GetSettings(sqlDB) if err != nil { l.Fatalf("Failed to get settings: %+v.", err) @@ -1086,11 +1156,10 @@ func main() { //nolint:cyclop,maintidx alertManager.Run(ctx) }() - wg.Add(1) - go func() { - defer wg.Done() + haService.AddLeaderService(ha.NewContextService("checks", func(ctx context.Context) error { checksService.Run(ctx) - }() + return nil + })) wg.Add(1) go func() { @@ -1099,22 +1168,21 @@ func main() { //nolint:cyclop,maintidx }() wg.Add(1) - go func() { + haService.AddLeaderService(ha.NewContextService("telemetry", func(ctx context.Context) error { defer wg.Done() telemetry.Run(ctx) - }() + return nil + })) - wg.Add(1) - go func() { - defer wg.Done() + haService.AddLeaderService(ha.NewContextService("scheduler", func(ctx context.Context) error { schedulerService.Run(ctx) - }() + return nil + })) - wg.Add(1) - go func() { - defer wg.Done() + haService.AddLeaderService(ha.NewContextService("versionCache", func(ctx context.Context) error { versionCache.Run(ctx) - }() + return nil + })) wg.Add(1) go func() { @@ -1140,6 +1208,7 @@ func main() { //nolint:cyclop,maintidx dumpService: dumpService, grafanaClient: grafanaClient, handler: agentsHandler, + ha: haService, jobsService: jobsService, kubeStorage: kubeStorage, minioClient: minioClient, @@ -1176,11 +1245,10 @@ func main() { //nolint:cyclop,maintidx runDebugServer(ctx) }() - wg.Add(1) - go func() { - defer wg.Done() + haService.AddLeaderService(ha.NewContextService("cleaner", func(ctx context.Context) error { cleaner.Run(ctx, cleanInterval, cleanOlderThan) - }() + return nil + })) if settings.DBaaS.Enabled { err = supervisord.RestartSupervisedService("dbaas-controller") if err != nil { @@ -1200,6 +1268,15 @@ func main() { //nolint:cyclop,maintidx } } + wg.Add(1) + go func() { + defer wg.Done() + err := haService.Run(ctx) + if err != nil { + l.Panicf("cannot start high availability service: %+v", err) + } + }() + wg.Wait() } diff --git a/managed/models/params.go b/managed/models/params.go new file mode 100644 index 0000000000..8121686d70 --- /dev/null +++ b/managed/models/params.go @@ -0,0 +1,35 @@ +// Copyright (C) 2023 Percona LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package models + +// HAParams defines parameters related to High Availability. +type HAParams struct { + GrafanaGossipPort int + Enabled bool + Bootstrap bool + NodeID string + AdvertiseAddress string + Nodes []string + RaftPort int + GossipPort int +} + +// Params defines parameters for supervisor. +type Params struct { + HAParams *HAParams + VMParams *VictoriaMetricsParams + PGParams *PGParams +} diff --git a/managed/services/agents/channel/channel.go b/managed/services/agents/channel/channel.go index cb2b6a66b5..5e36142352 100644 --- a/managed/services/agents/channel/channel.go +++ b/managed/services/agents/channel/channel.go @@ -17,6 +17,7 @@ package channel import ( + "context" "sync" "sync/atomic" @@ -66,13 +67,18 @@ type Response struct { Error error } +type Stream interface { + Send(*agentpb.ServerMessage) error + Recv() (*agentpb.AgentMessage, error) +} + // Channel encapsulates two-way communication channel between pmm-managed and pmm-agent. // // All exported methods are thread-safe. // //nolint:maligned type Channel struct { - s agentpb.Agent_ConnectServer + s Stream mSent, mRecv uint32 @@ -94,7 +100,7 @@ type Channel struct { // New creates new two-way communication channel with given stream. // // Stream should not be used by the caller after channel is created. -func New(stream agentpb.Agent_ConnectServer) *Channel { +func New(ctx context.Context, stream Stream) *Channel { s := &Channel{ s: stream, @@ -103,7 +109,7 @@ func New(stream agentpb.Agent_ConnectServer) *Channel { closeWait: make(chan struct{}), - l: logger.Get(stream.Context()), + l: logger.Get(ctx), } go s.runReceiver() diff --git a/managed/services/agents/channel/channel_test.go b/managed/services/agents/channel/channel_test.go index 0944d85936..f6e3851f0f 100644 --- a/managed/services/agents/channel/channel_test.go +++ b/managed/services/agents/channel/channel_test.go @@ -70,7 +70,7 @@ func setup(t *testing.T, connect func(*Channel) error, expected ...error) (agent agentpb.RegisterAgentServer(server, &testServer{ connectFunc: func(stream agentpb.Agent_ConnectServer) error { - channel = New(stream) + channel = New(stream.Context(), stream) return connect(channel) }, }) diff --git a/managed/services/agents/handler.go b/managed/services/agents/handler.go index 011ce90a84..c174b340c3 100644 --- a/managed/services/agents/handler.go +++ b/managed/services/agents/handler.go @@ -90,7 +90,7 @@ func (h *Handler) Run(stream agentpb.Agent_ConnectServer) error { } // see unregister and Kick methods - case <-agent.kick: + case <-agent.kickChan: // already unregistered, no need to call unregister method l.Warn("Kicked.") disconnectReason = "kicked" @@ -105,7 +105,7 @@ func (h *Handler) Run(stream agentpb.Agent_ConnectServer) error { if err != nil { l.Error(errors.WithStack(err)) } - return h.updateAgentStatusForChildren(ctx, agent.id, inventorypb.AgentStatus_DONE) + return nil } switch p := req.Payload.(type) { diff --git a/managed/services/agents/registry.go b/managed/services/agents/registry.go index 9f381c8ed8..6db039248f 100644 --- a/managed/services/agents/registry.go +++ b/managed/services/agents/registry.go @@ -68,7 +68,7 @@ type pmmAgentInfo struct { channel *channel.Channel id string stateChangeChan chan struct{} - kick chan struct{} + kickChan chan struct{} } // Registry keeps track of all connected pmm-agents. @@ -199,10 +199,10 @@ func (r *Registry) register(stream agentpb.Agent_ConnectServer) (*pmmAgentInfo, defer r.rw.Unlock() agent := &pmmAgentInfo{ - channel: channel.New(stream), + channel: channel.New(ctx, stream), id: agentMD.ID, stateChangeChan: make(chan struct{}, 1), - kick: make(chan struct{}), + kickChan: make(chan struct{}), } r.agents[agentMD.ID] = agent return agent, nil @@ -371,10 +371,10 @@ func (r *Registry) Kick(ctx context.Context, pmmAgentID string) { l.Debugf("pmm-agent with ID %q will be kicked in a moment.", pmmAgentID) // see Run method - close(agent.kick) + close(agent.kickChan) // Do not close agent.stateChangeChan to avoid breaking RequestStateUpdate; - // closing agent.kick is enough to exit runStateChangeHandler goroutine. + // closing agent.kickChan is enough to exit runStateChangeHandler goroutine. } func (r *Registry) get(pmmAgentID string) (*pmmAgentInfo, error) { @@ -417,6 +417,12 @@ func (r *Registry) Collect(ch chan<- prom.Metric) { r.mClockDrift.Collect(ch) } +func (r *Registry) KickAll(ctx context.Context) { + for _, agentInfo := range r.agents { + r.Kick(ctx, agentInfo.id) + } +} + // check interfaces. var ( _ prom.Collector = (*Registry)(nil) diff --git a/managed/services/agents/state.go b/managed/services/agents/state.go index f2d840a6a5..d7ad55ea4c 100644 --- a/managed/services/agents/state.go +++ b/managed/services/agents/state.go @@ -113,7 +113,7 @@ func (u *StateUpdater) runStateChangeHandler(ctx context.Context, agent *pmmAgen case <-ctx.Done(): return - case <-agent.kick: + case <-agent.kickChan: return case <-agent.stateChangeChan: diff --git a/managed/services/grafana/auth_server.go b/managed/services/grafana/auth_server.go index 2a599805dc..0eb6842244 100644 --- a/managed/services/grafana/auth_server.go +++ b/managed/services/grafana/auth_server.go @@ -73,8 +73,9 @@ var rules = map[string]role{ "/v1/user": viewer, // must be available without authentication for health checking - "/v1/readyz": none, - "/ping": none, // PMM 1.x variant + "/v1/readyz": none, + "/v1/leaderHealthCheck": none, + "/ping": none, // PMM 1.x variant // must not be available without authentication as it can leak data "/v1/version": viewer, diff --git a/managed/services/ha/highavailability.go b/managed/services/ha/highavailability.go new file mode 100644 index 0000000000..cec144b4d8 --- /dev/null +++ b/managed/services/ha/highavailability.go @@ -0,0 +1,318 @@ +// Copyright (C) 2023 Percona LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +// Package ha contains everything related to high availability. +package ha + +import ( + "context" + "fmt" + "io" + "net" + "strconv" + "sync" + "time" + + "github.com/hashicorp/memberlist" + "github.com/hashicorp/raft" + "github.com/sirupsen/logrus" + + "github.com/percona/pmm/managed/models" +) + +type Service struct { + params *models.HAParams + bootstrapCluster bool + + services *services + + receivedMessages chan []byte + nodeCh chan memberlist.NodeEvent + leaderCh chan raft.Observation + + l *logrus.Entry + wg *sync.WaitGroup + + rw sync.RWMutex + raftNode *raft.Raft + memberlist *memberlist.Memberlist +} + +func (s *Service) Apply(logEntry *raft.Log) interface{} { + s.l.Printf("raft: got a message: %s", string(logEntry.Data)) + s.receivedMessages <- logEntry.Data + return nil +} + +func (s *Service) Snapshot() (raft.FSMSnapshot, error) { //nolint:ireturn + return nil, nil +} + +func (s *Service) Restore(_ io.ReadCloser) error { + return nil +} + +func New(params *models.HAParams) *Service { + return &Service{ + params: params, + bootstrapCluster: params.Bootstrap, + services: newServices(), + nodeCh: make(chan memberlist.NodeEvent, 5), + leaderCh: make(chan raft.Observation), + receivedMessages: make(chan []byte), + l: logrus.WithField("component", "ha"), + wg: &sync.WaitGroup{}, + } +} + +func (s *Service) Run(ctx context.Context) error { + s.wg.Add(1) + go func() { + defer s.wg.Done() + for { + select { + case <-s.services.Refresh(): + if s.IsLeader() { + s.services.StartAllServices(ctx) + } + case <-ctx.Done(): + s.services.StopRunningServices() + return + } + } + }() + + if !s.params.Enabled { + return nil + } + + s.l.Infoln("Starting...") + defer s.l.Infoln("Done.") + + // Create the Raft configuration + raftConfig := raft.DefaultConfig() + raftConfig.LocalID = raft.ServerID(s.params.NodeID) + raftConfig.LogLevel = "DEBUG" + + // Create a new Raft transport + raa, err := net.ResolveTCPAddr("", net.JoinHostPort(s.params.AdvertiseAddress, strconv.Itoa(s.params.RaftPort))) + if err != nil { + return err + } + raftTrans, err := raft.NewTCPTransport(net.JoinHostPort("0.0.0.0", strconv.Itoa(s.params.RaftPort)), raa, 3, 10*time.Second, nil) + if err != nil { + return err + } + + // Create a new Raft node + s.rw.Lock() + s.raftNode, err = raft.NewRaft(raftConfig, s, raft.NewInmemStore(), raft.NewInmemStore(), raft.NewInmemSnapshotStore(), raftTrans) + s.rw.Unlock() + if err != nil { + return err + } + defer func() { + if s.IsLeader() { + s.raftNode.LeadershipTransfer() + } + err := s.raftNode.Shutdown().Error() + if err != nil { + s.l.Errorf("error during the shutdown of raft node: %q", err) + } + }() + + // Create the memberlist configuration + memberlistConfig := memberlist.DefaultWANConfig() + memberlistConfig.Name = s.params.NodeID + memberlistConfig.BindAddr = "0.0.0.0" + memberlistConfig.BindPort = s.params.GossipPort + memberlistConfig.AdvertiseAddr = raa.IP.String() + memberlistConfig.AdvertisePort = s.params.GossipPort + memberlistConfig.Events = &memberlist.ChannelEventDelegate{Ch: s.nodeCh} + + // Create the memberlist + s.memberlist, err = memberlist.Create(memberlistConfig) + if err != nil { + return fmt.Errorf("failed to create memberlist: %w", err) + } + defer func() { + err := s.memberlist.Leave(5 * time.Second) + if err != nil { + s.l.Errorf("couldn't leave memberlist cluster: %q", err) + } + err = s.memberlist.Shutdown() + if err != nil { + s.l.Errorf("couldn't shutdown memberlist listeners: %q", err) + } + }() + + if s.bootstrapCluster { + // Start the Raft node + cfg := raft.Configuration{ + Servers: []raft.Server{ + { + Suffrage: raft.Voter, + ID: raft.ServerID(s.params.NodeID), + Address: raft.ServerAddress(raa.String()), + }, + }, + } + if err := s.raftNode.BootstrapCluster(cfg).Error(); err != nil { + return fmt.Errorf("failed to bootstrap Raft cluster: %w", err) + } + } + if len(s.params.Nodes) != 0 { + _, err := s.memberlist.Join(s.params.Nodes) + if err != nil { + return fmt.Errorf("failed to join memberlist cluster: %w", err) + } + } + s.wg.Add(1) + go func() { + defer s.wg.Done() + s.runLeaderObserver(ctx) + }() + + s.wg.Add(1) + go func() { + defer s.wg.Done() + s.runRaftNodesSynchronizer(ctx) + }() + + <-ctx.Done() + + s.services.Wait() + s.wg.Wait() + + return nil +} + +func (s *Service) runRaftNodesSynchronizer(ctx context.Context) { + t := time.NewTicker(5 * time.Second) + + for { + select { + case event := <-s.nodeCh: + if !s.IsLeader() { + continue + } + node := event.Node + switch event.Event { + case memberlist.NodeJoin: + s.addMemberlistNodeToRaft(node) + case memberlist.NodeLeave: + s.removeMemberlistNodeFromRaft(node) + case memberlist.NodeUpdate: + continue + } + case <-t.C: + if !s.IsLeader() { + continue + } + servers := s.raftNode.GetConfiguration().Configuration().Servers + raftServers := make(map[string]struct{}) + for _, server := range servers { + raftServers[string(server.ID)] = struct{}{} + } + members := s.memberlist.Members() + s.l.Infof("memberlist members: %v", members) + for _, node := range members { + if _, ok := raftServers[node.Name]; !ok { + s.addMemberlistNodeToRaft(node) + } + } + case <-ctx.Done(): + t.Stop() + return + } + } +} + +func (s *Service) removeMemberlistNodeFromRaft(node *memberlist.Node) { + s.rw.RLock() + defer s.rw.RUnlock() + err := s.raftNode.RemoveServer(raft.ServerID(node.Name), 0, 10*time.Second).Error() + if err != nil { + s.l.Errorln(err) + } +} + +func (s *Service) addMemberlistNodeToRaft(node *memberlist.Node) { + s.rw.RLock() + defer s.rw.RUnlock() + err := s.raftNode.AddVoter(raft.ServerID(node.Name), raft.ServerAddress(fmt.Sprintf("%s:%d", node.Addr.String(), s.params.RaftPort)), 0, 10*time.Second).Error() + if err != nil { + s.l.Errorf("couldn't add a server node %s: %q", node.Name, err) + } +} + +func (s *Service) runLeaderObserver(ctx context.Context) { + t := time.NewTicker(5 * time.Second) + for { + s.rw.RLock() + node := s.raftNode + s.rw.RUnlock() + select { + case isLeader := <-node.LeaderCh(): + if isLeader { + s.services.StartAllServices(ctx) + // This node is the leader + s.l.Printf("I am the leader!") + peers := s.memberlist.Members() + for _, peer := range peers { + if peer.Name == s.params.NodeID { + continue + } + s.addMemberlistNodeToRaft(peer) + } + } else { + s.l.Printf("I am not a leader!") + s.services.StopRunningServices() + } + case <-t.C: + address, serverID := s.raftNode.LeaderWithID() + s.l.Infof("Leader is %s on %s", serverID, address) + case <-ctx.Done(): + return + } + } +} + +func (s *Service) AddLeaderService(leaderService LeaderService) { + err := s.services.Add(leaderService) + if err != nil { + s.l.Errorf("couldn't add HA service: +%v", err) + } +} + +func (s *Service) BroadcastMessage(message []byte) { + if s.params.Enabled { + s.rw.RLock() + defer s.rw.RUnlock() + s.raftNode.Apply(message, 3*time.Second) + } else { + s.receivedMessages <- message + } +} + +func (s *Service) IsLeader() bool { + s.rw.RLock() + defer s.rw.RUnlock() + return (s.raftNode != nil && s.raftNode.State() == raft.Leader) || !s.params.Enabled +} + +func (s *Service) Bootstrap() bool { + return s.params.Bootstrap || !s.params.Enabled +} diff --git a/managed/services/ha/leaderservice.go b/managed/services/ha/leaderservice.go new file mode 100644 index 0000000000..9ec5185629 --- /dev/null +++ b/managed/services/ha/leaderservice.go @@ -0,0 +1,87 @@ +// Copyright (C) 2023 Percona LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package ha + +import ( + "context" + "sync" +) + +type LeaderService interface { + Start(ctx context.Context) error + Stop() + ID() string +} + +type StandardService struct { + id string + + startFunc func(context.Context) error + stopFunc func() +} + +func NewStandardService(id string, startFunc func(context.Context) error, stopFunc func()) *StandardService { + return &StandardService{ + id: id, + startFunc: startFunc, + stopFunc: stopFunc, + } +} + +func (s *StandardService) ID() string { + return s.id +} + +func (s *StandardService) Start(ctx context.Context) error { + return s.startFunc(ctx) +} + +func (s *StandardService) Stop() { + s.stopFunc() +} + +type ContextService struct { + id string + + startFunc func(context.Context) error + + m sync.Mutex + cancel context.CancelFunc +} + +func NewContextService(id string, startFunc func(context.Context) error) *ContextService { + return &ContextService{ + id: id, + startFunc: startFunc, + } +} + +func (s *ContextService) ID() string { + return s.id +} + +func (s *ContextService) Start(ctx context.Context) error { + s.m.Lock() + ctx, s.cancel = context.WithCancel(ctx) + s.m.Unlock() + return s.startFunc(ctx) +} + +func (s *ContextService) Stop() { + s.m.Lock() + defer s.m.Unlock() + s.cancel() +} diff --git a/managed/services/ha/services.go b/managed/services/ha/services.go new file mode 100644 index 0000000000..eabc233c8a --- /dev/null +++ b/managed/services/ha/services.go @@ -0,0 +1,107 @@ +// Copyright (C) 2023 Percona LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package ha + +import ( + "context" + "fmt" + "sync" + + "github.com/sirupsen/logrus" +) + +type services struct { + wg sync.WaitGroup + + rw sync.Mutex + all map[string]LeaderService + running map[string]LeaderService + + refresh chan struct{} + + l *logrus.Entry +} + +func newServices() *services { + return &services{ + all: make(map[string]LeaderService), + running: make(map[string]LeaderService), + refresh: make(chan struct{}), + l: logrus.WithField("component", "ha-services"), + } +} + +func (s *services) Add(service LeaderService) error { + s.rw.Lock() + defer s.rw.Unlock() + + id := service.ID() + if _, ok := s.all[id]; ok { + return fmt.Errorf("service with id %s is already exist", id) + } + s.all[id] = service + select { + case s.refresh <- struct{}{}: + default: + } + return nil +} + +func (s *services) StartAllServices(ctx context.Context) { + s.rw.Lock() + defer s.rw.Unlock() + + for id, service := range s.all { + if _, ok := s.running[id]; !ok { + s.wg.Add(1) + s.running[id] = service + ls := service + go func() { + s.l.Infoln("Starting ", ls.ID()) + err := ls.Start(ctx) + if err != nil { + s.l.Errorln(err) + } + }() + } + } +} + +func (s *services) StopRunningServices() { + s.rw.Lock() + defer s.rw.Unlock() + + for id, service := range s.running { + id := id + ls := service + go func() { + defer s.wg.Done() + s.l.Infoln("Stopping ", ls) + ls.Stop() + s.rw.Lock() + defer s.rw.Unlock() + delete(s.running, id) + }() + } +} + +func (s *services) Refresh() chan struct{} { + return s.refresh +} + +func (s *services) Wait() { + s.wg.Wait() +} diff --git a/managed/services/server/deps.go b/managed/services/server/deps.go index d7c139830b..82ff40bad1 100644 --- a/managed/services/server/deps.go +++ b/managed/services/server/deps.go @@ -127,8 +127,14 @@ type emailer interface { Send(ctx context.Context, settings *models.EmailAlertingSettings, emailTo string) error } -// rulesService is a subset of methods of ia.TemplatesService used by this package. +// templatesService is a subset of methods of ia.TemplatesService used by this package. // We use it instead of real type for testing and to avoid dependency cycle. type templatesService interface { CollectTemplates(ctx context.Context) } + +// haService is a subset of methods of ha.Service used by this package. +// We use it instead of real type for testing and to avoid dependency cycle. +type haService interface { + IsLeader() bool +} diff --git a/managed/services/server/server.go b/managed/services/server/server.go index 84c8d67ae1..6db57e23e4 100644 --- a/managed/services/server/server.go +++ b/managed/services/server/server.go @@ -64,6 +64,7 @@ type Server struct { rulesService rulesService dbaasInitializer dbaasInitializer emailer emailer + haService haService l *logrus.Entry @@ -104,6 +105,7 @@ type Params struct { RulesService rulesService DBaaSInitializer dbaasInitializer Emailer emailer + HAService haService } // NewServer returns new server for Server service. @@ -130,6 +132,7 @@ func NewServer(params *Params) (*Server, error) { rulesService: params.RulesService, dbaasInitializer: params.DBaaSInitializer, emailer: params.Emailer, + haService: params.HAService, l: logrus.WithField("component", "server"), pmmUpdateAuthFile: path, envSettings: &models.ChangeSettingsParams{}, @@ -240,6 +243,16 @@ func (s *Server) Readiness(ctx context.Context, req *serverpb.ReadinessRequest) return &serverpb.ReadinessResponse{}, nil } +// LeaderHealthCheck checks if the instance is the leader in a cluster. +// Returns an error if the instance isn't the leader. +// It's used for HA purpose. +func (s *Server) LeaderHealthCheck(ctx context.Context, req *serverpb.LeaderHealthCheckRequest) (*serverpb.LeaderHealthCheckResponse, error) { + if s.haService.IsLeader() { + return &serverpb.LeaderHealthCheckResponse{}, nil + } + return nil, status.Error(codes.FailedPrecondition, "this PMM Server isn't the leader") +} + func (s *Server) onlyInstalledVersionResponse(ctx context.Context) *serverpb.CheckUpdatesResponse { v := s.supervisord.InstalledPMMVersion(ctx) r := &serverpb.CheckUpdatesResponse{ diff --git a/managed/services/supervisord/devcontainer_test.go b/managed/services/supervisord/devcontainer_test.go index 6a545e7ec9..85e3792e2a 100644 --- a/managed/services/supervisord/devcontainer_test.go +++ b/managed/services/supervisord/devcontainer_test.go @@ -115,7 +115,7 @@ func TestDevContainer(t *testing.T) { vmParams, err := models.NewVictoriaMetricsParams(models.BasePrometheusConfigPath, models.VMBaseURL) require.NoError(t, err) - s := New("/etc/supervisord.d", checker, vmParams, models.PGParams{}, gRPCMessageMaxSize) + s := New("/etc/supervisord.d", checker, &models.Params{VMParams: vmParams, PGParams: &models.PGParams{}, HAParams: &models.HAParams{}}, gRPCMessageMaxSize) require.NotEmpty(t, s.supervisorctlPath) ctx, cancel := context.WithCancel(context.Background()) @@ -168,7 +168,7 @@ func TestDevContainer(t *testing.T) { // logrus.SetLevel(logrus.DebugLevel) checker := NewPMMUpdateChecker(logrus.WithField("test", t.Name())) vmParams := &models.VictoriaMetricsParams{} - s := New("/etc/supervisord.d", checker, vmParams, models.PGParams{}, gRPCMessageMaxSize) + s := New("/etc/supervisord.d", checker, &models.Params{VMParams: vmParams, PGParams: &models.PGParams{}, HAParams: &models.HAParams{}}, gRPCMessageMaxSize) require.NotEmpty(t, s.supervisorctlPath) ctx, cancel := context.WithCancel(context.Background()) diff --git a/managed/services/supervisord/logs.go b/managed/services/supervisord/logs.go index 951b12a071..22b60f8c74 100644 --- a/managed/services/supervisord/logs.go +++ b/managed/services/supervisord/logs.go @@ -61,7 +61,6 @@ type Logs struct { } // NewLogs creates a new Logs service. -// The number of last log lines to read is n. func NewLogs(pmmVersion string, pmmUpdateChecker *PMMUpdateChecker, vmParams victoriaMetricsParams) *Logs { return &Logs{ pmmVersion: pmmVersion, diff --git a/managed/services/supervisord/pmm_config.go b/managed/services/supervisord/pmm_config.go index fbe7f2b51f..b583e9f72b 100644 --- a/managed/services/supervisord/pmm_config.go +++ b/managed/services/supervisord/pmm_config.go @@ -128,6 +128,7 @@ stdout_logfile_maxbytes = 30MB stdout_logfile_backups = 2 redirect_stderr = true {{- end }} +{{- if not .DisableInternalClickhouse }} [program:clickhouse] priority = 2 @@ -144,6 +145,7 @@ stdout_logfile = /srv/logs/clickhouse-server.log stdout_logfile_maxbytes = 50MB stdout_logfile_backups = 2 redirect_stderr = true +{{- end }} [program:nginx] priority = 4 @@ -182,7 +184,7 @@ redirect_stderr = true priority = 15 command = /usr/sbin/pmm-agent --config-file=/usr/local/percona/pmm2/config/pmm-agent.yaml autorestart = true -autostart = true +autostart = false startretries = 1000 startsecs = 1 stopsignal = TERM diff --git a/managed/services/supervisord/pmm_config_test.go b/managed/services/supervisord/pmm_config_test.go new file mode 100644 index 0000000000..baaf279b58 --- /dev/null +++ b/managed/services/supervisord/pmm_config_test.go @@ -0,0 +1,57 @@ +// Copyright (C) 2023 Percona LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package supervisord + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestSavePMMConfig(t *testing.T) { + t.Parallel() + configDir := filepath.Join("..", "..", "testdata", "supervisord.d") + tests := []struct { + description string + params map[string]any + file string + }{ + { + description: "disable internal postgresql db", + params: map[string]any{"DisableInternalDB": true, "DisableSupervisor": false, "DisableInternalClickhouse": false}, + file: "pmm-db_disabled", + }, + { + description: "enable internal postgresql db", + params: map[string]any{"DisableInternalDB": false, "DisableSupervisor": false, "DisableInternalClickhouse": false}, + file: "pmm-db_enabled", + }, + } + for _, test := range tests { + test := test + t.Run(test.description, func(t *testing.T) { + t.Parallel() + expected, err := os.ReadFile(filepath.Join(configDir, test.file+".ini")) //nolint:gosec + require.NoError(t, err) + actual, err := marshalConfig(test.params) + require.NoError(t, err) + assert.Equal(t, string(expected), string(actual)) + }) + } +} diff --git a/managed/services/supervisord/supervisord.go b/managed/services/supervisord/supervisord.go index 79335b5b77..3ad4b7ba72 100644 --- a/managed/services/supervisord/supervisord.go +++ b/managed/services/supervisord/supervisord.go @@ -68,7 +68,8 @@ type Service struct { supervisordConfigsM sync.Mutex vmParams *models.VictoriaMetricsParams - pgParams models.PGParams + pgParams *models.PGParams + haParams *models.HAParams } type sub struct { @@ -84,7 +85,7 @@ const ( ) // New creates new service. -func New(configDir string, pmmUpdateCheck *PMMUpdateChecker, vmParams *models.VictoriaMetricsParams, pgParams models.PGParams, gRPCMessageMaxSize uint32) *Service { +func New(configDir string, pmmUpdateCheck *PMMUpdateChecker, params *models.Params, gRPCMessageMaxSize uint32) *Service { path, _ := exec.LookPath("supervisorctl") return &Service{ configDir: configDir, @@ -94,8 +95,9 @@ func New(configDir string, pmmUpdateCheck *PMMUpdateChecker, vmParams *models.Vi pmmUpdateCheck: pmmUpdateCheck, subs: make(map[chan *event]sub), lastEvents: make(map[string]eventType), - vmParams: vmParams, - pgParams: pgParams, + vmParams: params.VMParams, + pgParams: params.PGParams, + haParams: params.HAParams, } } @@ -437,6 +439,7 @@ func (s *Service) marshalConfig(tmpl *template.Template, settings *models.Settin } s.addPostgresParams(templateParams) + s.addClusterParams(templateParams) templateParams["PMMServerHost"] = "" if settings.PMMPublicAddress != "" { @@ -509,6 +512,9 @@ func addAlertManagerParams(alertManagerURL string, templateParams map[string]int // addPostgresParams adds pmm-server postgres database params to template config for grafana. func (s *Service) addPostgresParams(templateParams map[string]interface{}) { + if s.pgParams == nil { + return + } templateParams["PostgresAddr"] = s.pgParams.Addr templateParams["PostgresDBName"] = s.pgParams.DBName templateParams["PostgresDBUsername"] = s.pgParams.DBUsername @@ -519,6 +525,21 @@ func (s *Service) addPostgresParams(templateParams map[string]interface{}) { templateParams["PostgresSSLCertPath"] = s.pgParams.SSLCertPath } +func (s *Service) addClusterParams(templateParams map[string]interface{}) { + templateParams["HAEnabled"] = s.haParams.Enabled + if s.haParams.Enabled { + templateParams["GrafanaGossipPort"] = s.haParams.GrafanaGossipPort + templateParams["HAAdvertiseAddress"] = s.haParams.AdvertiseAddress + nodes := make([]string, len(s.haParams.Nodes)) + for i, node := range s.haParams.Nodes { + nodes[i] = fmt.Sprintf("%s:%d", node, s.haParams.GrafanaGossipPort) + } + templateParams["HANodes"] = strings.Join(nodes, ",") + } + //- GF_UNIFIED_ALERTING_HA_ADVERTISE_ADDRESS=172.20.0.5:9095 + //- GF_UNIFIED_ALERTING_HA_PEERS=pmm-server-active:9095,pmm-server-passive:9095 +} + // saveConfigAndReload saves given supervisord program configuration to file and reloads it. // If configuration can't be reloaded for some reason, old file is restored, and configuration is reloaded again. // Returns true if configuration was changed. @@ -582,7 +603,7 @@ func (s *Service) UpdateConfiguration(settings *models.Settings, ssoDetails *mod } for _, tmpl := range templates.Templates() { - if tmpl.Name() == "" { + if tmpl.Name() == "" || (tmpl.Name() == "victoriametrics" && s.vmParams.ExternalVM()) { continue } @@ -607,6 +628,18 @@ func (s *Service) RestartSupervisedService(serviceName string) error { return err } +// StartSupervisedService starts given service. +func (s *Service) StartSupervisedService(serviceName string) error { + _, err := s.supervisorctl("start", serviceName) + return err +} + +// StopSupervisedService stops given service. +func (s *Service) StopSupervisedService(serviceName string) error { + _, err := s.supervisorctl("stop", serviceName) + return err +} + //nolint:lll var templates = template.Must(template.New("").Option("missingkey=error").Parse(` {{define "dbaas-controller"}} @@ -818,6 +851,11 @@ environment = {{- if .PerconaSSODetails}} GF_AUTH_SIGNOUT_REDIRECT_URL="https://{{ .IssuerDomain }}/login/signout?fromURI=https://{{ .PMMServerAddress }}/graph/login" {{- end}} + {{- if .HAEnabled}} + GF_UNIFIED_ALERTING_HA_LISTEN_ADDRESS="0.0.0.0:{{ .GrafanaGossipPort }}", + GF_UNIFIED_ALERTING_HA_ADVERTISE_ADDRESS="{{ .HAAdvertiseAddress }}:{{ .GrafanaGossipPort }}", + GF_UNIFIED_ALERTING_HA_PEERS="{{ .HANodes }}" + {{- end}} user = grafana directory = /usr/share/grafana autorestart = true diff --git a/managed/services/supervisord/supervisord_test.go b/managed/services/supervisord/supervisord_test.go index 6676406f39..343d818db4 100644 --- a/managed/services/supervisord/supervisord_test.go +++ b/managed/services/supervisord/supervisord_test.go @@ -38,7 +38,17 @@ func TestConfig(t *testing.T) { configDir := filepath.Join("..", "..", "testdata", "supervisord.d") vmParams, err := models.NewVictoriaMetricsParams(models.BasePrometheusConfigPath, models.VMBaseURL) require.NoError(t, err) - s := New(configDir, pmmUpdateCheck, vmParams, models.PGParams{}, gRPCMessageMaxSize) + pgParams := &models.PGParams{ + Addr: "127.0.0.1:5432", + DBName: "postgres", + DBUsername: "db_username", + DBPassword: "db_password", + SSLMode: "verify", + SSLCAPath: "path-to-CA-cert", + SSLKeyPath: "path-to-key", + SSLCertPath: "path-to-cert", + } + s := New(configDir, pmmUpdateCheck, &models.Params{VMParams: vmParams, PGParams: pgParams, HAParams: &models.HAParams{}}, gRPCMessageMaxSize) settings := &models.Settings{ DataRetention: 30 * 24 * time.Hour, AlertManagerURL: "https://external-user:passw!,ord@external-alertmanager:6443/alerts", @@ -71,7 +81,7 @@ func TestDBaaSController(t *testing.T) { configDir := filepath.Join("..", "..", "testdata", "supervisord.d") vmParams, err := models.NewVictoriaMetricsParams(models.BasePrometheusConfigPath, models.VMBaseURL) require.NoError(t, err) - s := New(configDir, pmmUpdateCheck, vmParams, models.PGParams{}, gRPCMessageMaxSize) + s := New(configDir, pmmUpdateCheck, &models.Params{VMParams: vmParams, PGParams: &models.PGParams{}, HAParams: &models.HAParams{}}, gRPCMessageMaxSize) var tp *template.Template for _, tmpl := range templates.Templates() { @@ -161,35 +171,3 @@ func TestAddAlertManagerParam(t *testing.T) { require.Equal(t, "http://127.0.0.1:9093/alertmanager", params["AlertmanagerURL"]) }) } - -func TestSavePMMConfig(t *testing.T) { - t.Parallel() - configDir := filepath.Join("..", "..", "testdata", "supervisord.d") - tests := []struct { - description string - params map[string]any - file string - }{ - { - description: "disable internal postgresql db", - params: map[string]any{"DisableInternalDB": true, "DisableSupervisor": false}, - file: "pmm-db_disabled", - }, - { - description: "enable internal postgresql db", - params: map[string]any{"DisableInternalDB": false, "DisableSupervisor": false}, - file: "pmm-db_enabled", - }, - } - for _, test := range tests { - test := test - t.Run(test.description, func(t *testing.T) { - t.Parallel() - expected, err := os.ReadFile(filepath.Join(configDir, test.file+".ini")) //nolint:gosec - require.NoError(t, err) - actual, err := marshalConfig(test.params) - require.NoError(t, err) - assert.Equal(t, string(expected), string(actual)) - }) - } -} diff --git a/managed/testdata/haproxy/haproxy.cfg b/managed/testdata/haproxy/haproxy.cfg new file mode 100644 index 0000000000..57cf72dfb0 --- /dev/null +++ b/managed/testdata/haproxy/haproxy.cfg @@ -0,0 +1,39 @@ +global + log stdout local0 debug + log stdout local1 info + log stdout local2 info + daemon + +defaults + log global + mode http + option httplog + option dontlognull + timeout connect 5000 + timeout client 50000 + timeout server 50000 + +frontend http_front + bind *:80 + default_backend http_back + +frontend https_front + bind *:443 ssl crt /etc/ssl/private/localhost.pem + default_backend https_back + +backend http_back + option httpchk + http-check send meth POST uri /v1/leaderHealthCheck ver HTTP/1.1 hdr Host www + http-check expect status 200 + server pmm-server-active-http pmm-server-active:80 check + server pmm-server-passive-http pmm-server-passive:80 check backup + server pmm-server-passive-2-http pmm-server-passive-2:80 check backup + +backend https_back + option httpchk + http-check send meth POST uri /v1/leaderHealthCheck ver HTTP/1.1 hdr Host www + http-check expect status 200 + server pmm-server-active-https pmm-server-active:443 check ssl verify none backup + server pmm-server-passive-https pmm-server-passive:443 check ssl verify none backup + server pmm-server-passive-2-https pmm-server-passive-2:443 check ssl verify none backup + diff --git a/managed/testdata/haproxy/localhost.crt b/managed/testdata/haproxy/localhost.crt new file mode 100644 index 0000000000..1b601b09c1 --- /dev/null +++ b/managed/testdata/haproxy/localhost.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDPjCCAiYCCQC8Y6/8ayWo6DANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQGEwJU +UjEQMA4GA1UECgwHUGVyY29uYTESMBAGA1UEAwwJbG9jYWxob3N0MSwwKgYJKoZI +hvcNAQkBFh1udXJsYW4ubW9sZG9tdXJvdkBwZXJjb25hLmNvbTAeFw0yMzA3MDYy +MDA2NDNaFw0yNDA3MDUyMDA2NDNaMGExCzAJBgNVBAYTAlRSMRAwDgYDVQQKDAdQ +ZXJjb25hMRIwEAYDVQQDDAlsb2NhbGhvc3QxLDAqBgkqhkiG9w0BCQEWHW51cmxh +bi5tb2xkb211cm92QHBlcmNvbmEuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAmp+Xbi1C79S2l7IawE0dIBaKlx4vO/baQBDi+SXuhUonw8dPTqvD +DZy96/irc7SudvsJdcpcFn9tGfASDez56uZqt/39wsA+uUsym9yV39gcZPRzeoiV +nxDny1dcNJSqlGkcDp7BqXaj2/e6bK5RW3cpUnnRk4M7weDsdblBJLYPAIqTGMmZ +Chf/iKAz6i9E+FRuXi3rhFJKUEGv+5nh7Pjd/9BxmVyjl9f9SWhe+AkimOs+CYrh +lcGAHJ6XKq5KAMB43elZxSXgv6X5eTVhQsqf7X0e8n0OrXKqP+fqrQMPnkOsVvEj +q90mcG1mvvPrMoXN0XN1dfJEhyRB/hwi5QIDAQABMA0GCSqGSIb3DQEBCwUAA4IB +AQB9BKnOT2KiKTdnydorEpuMgzD1RZ9bfX8mGiucjPh5lcjO6L9haUbFN/6PupZP +x1WRKNwYm+R2vP/Q1tlkOwVDfBtycAhyx5NMRyHkmG90ap/hJUThF2D9Q+5A81Ma +tnpg5jbxPEBeMHujGZmDEiBNPHc9oP7HNuPXMzZWxAOjRAg2WtaqjyJi8ExnCP1t +4ELKdjojtSefhxQzZmdHNBKWa0kUJhDGfhvSo0//H9n8Q7VMmtVS94Klu/H+IG88 +EYmEzgkmty2eie+Jiv+S2WGDEUuopAReifGscFI3tYvNBbeU4GbtUCXKoNX8N6hO +1POaPPj84EK2ncLJXffk0XYq +-----END CERTIFICATE----- diff --git a/managed/testdata/haproxy/localhost.csr b/managed/testdata/haproxy/localhost.csr new file mode 100644 index 0000000000..84d654a289 --- /dev/null +++ b/managed/testdata/haproxy/localhost.csr @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICpjCCAY4CAQAwYTELMAkGA1UEBhMCVFIxEDAOBgNVBAoMB1BlcmNvbmExEjAQ +BgNVBAMMCWxvY2FsaG9zdDEsMCoGCSqGSIb3DQEJARYdbnVybGFuLm1vbGRvbXVy +b3ZAcGVyY29uYS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCa +n5duLULv1LaXshrATR0gFoqXHi879tpAEOL5Je6FSifDx09Oq8MNnL3r+KtztK52 ++wl1ylwWf20Z8BIN7Pnq5mq3/f3CwD65SzKb3JXf2Bxk9HN6iJWfEOfLV1w0lKqU +aRwOnsGpdqPb97psrlFbdylSedGTgzvB4Ox1uUEktg8AipMYyZkKF/+IoDPqL0T4 +VG5eLeuEUkpQQa/7meHs+N3/0HGZXKOX1/1JaF74CSKY6z4JiuGVwYAcnpcqrkoA +wHjd6VnFJeC/pfl5NWFCyp/tfR7yfQ6tcqo/5+qtAw+eQ6xW8SOr3SZwbWa+8+sy +hc3Rc3V18kSHJEH+HCLlAgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEALWj/APq2 +xfNqyRM4cf8uSpRiIe7OjE9HABFXWowLfFMJ6E337n9TnV/srCNxgdBPZ78pPJLR +EWFRJUtB/cwYqxSauWYg7+x1HtNn2yQnyKX1Fep8LBREs2ykXPQAmiTaUrxja0+W +D880Ck8uy+C8HKF/cBQA3ZCrdkrV9Q6829WG3FNtRdIu72SDZb9opxvufiOxCRgX +6E1CiZL4fTcgUXVcR9MxJfSNj+HgsO3mU5DiyvbsOpXxCfWDy2O0/CmonfhyDL8o +2EF870bTHMJ4sxMOIB9ZFj4TFwoVUnl08G+XEx2mFR1Hb2ooUDUO5LXt80lftJR0 +qBGJW/RYyCRrPA== +-----END CERTIFICATE REQUEST----- diff --git a/managed/testdata/haproxy/localhost.key b/managed/testdata/haproxy/localhost.key new file mode 100644 index 0000000000..81e8700b34 --- /dev/null +++ b/managed/testdata/haproxy/localhost.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAmp+Xbi1C79S2l7IawE0dIBaKlx4vO/baQBDi+SXuhUonw8dP +TqvDDZy96/irc7SudvsJdcpcFn9tGfASDez56uZqt/39wsA+uUsym9yV39gcZPRz +eoiVnxDny1dcNJSqlGkcDp7BqXaj2/e6bK5RW3cpUnnRk4M7weDsdblBJLYPAIqT +GMmZChf/iKAz6i9E+FRuXi3rhFJKUEGv+5nh7Pjd/9BxmVyjl9f9SWhe+AkimOs+ +CYrhlcGAHJ6XKq5KAMB43elZxSXgv6X5eTVhQsqf7X0e8n0OrXKqP+fqrQMPnkOs +VvEjq90mcG1mvvPrMoXN0XN1dfJEhyRB/hwi5QIDAQABAoIBAEk7zT0hstJkrRas +BH+QBntsMbfhU/3SrQwq81WN4aq/tJXFkIpyT6/izRE2df4XVYqE27YuYe9F6yad +ze9KjhPzjhgW9FmJNCwOsamgkFu0v74RCaC/kB4Go8JrXgCJaUFhhyhliNP6nSFR +87oF1gK8LZYinGCBh4wMO/KGC5SW6X9W6xf7f6RJYSPUH7h80XwnDRvsSnldH/3e +QPciQXP8fPUUW9EP9WERjpWTBZ8YqUG5P2w6ZlCrJ7L+/STMbscD3dYTUXxQUsfh +oxjsi9BQKTPFqiLH7gstCfdoufQJkfWMLSxpn8bBiHf5nhehuRRd9hKwz8+S1jdE +Lv0FYAECgYEAy8h9t5QwW3NSD1WclIFcxQhUeHIWHjLB8KJZi9eYJ4oZ7zWa5duV +0DTs3vJfVvxJ8XLbjnaap7ULWDZETbm2m6d5zJBaEGqLYShzlr15vNp0ZzDZghw+ +9hG/TqAB1jj76RYbI9h4TvzSI3+mbq3nl5Sykz1Envuznz2JiKbefIUCgYEAwj5h +fS0wAa5zjR8Lgs1nRFmg37qH6gn6w40yDDYwxDtM2L1l96ONPROvm6RZkM6hkHge +dKzabpHrh7eViQOgbUO+tyxttOdBspdK1ubi7UZTG6xq4zuzqiITP+BnhSw2mzDR +J276DMNalsmzQdI8v+eIMK0yxqOobcgRK979iuECgYBw/NP/onGBcxpPmEc968/1 +Cx5SvebXjYsMkeeWas5ZNfAVOqKMychx7bZcEwSbpTyWW/myLr6nN/F3UndipRLD +kQMuUect7PUkxJn6PUovVOxvfp1Kz8B1DPgGbx81mNjLrs8Te+WQ3grhVdiAy3l6 +CR9OFg1jHOnF5AfKtcLsRQKBgGL800WtX4eb1XsXVRBliLjGTDt3nYfhag95xwV+ +IEAAUFsruekHShTUEWvpx1MKWj97V1nyNKagaj0Ri3z1gi3sliZW19mW+F4Ax7zY +kNCGRBgYN6hxZk/Paavluhudun4/1HaaEYerjmDFjTp/30GUxky4FuYvxMeda1LG +IsNBAoGBAIeBiXXaB8QhlP4vJu6HT9IDZKRRiYTNjiS1o8CQ2U86FfyO7OVQ+19R +DJCYOc10foiwv+HEsEVXAjux79Z2h2dqqxtVoWPNh6yGs0SDKObmPWOEZsFABeF4 +RFTKlWMq0tvbXmnGwciA3Oy9DbsHp5jTo6qbuewVvE5PL7GTnokF +-----END RSA PRIVATE KEY----- diff --git a/managed/testdata/haproxy/localhost.pem b/managed/testdata/haproxy/localhost.pem new file mode 100644 index 0000000000..e401361481 --- /dev/null +++ b/managed/testdata/haproxy/localhost.pem @@ -0,0 +1,47 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAmp+Xbi1C79S2l7IawE0dIBaKlx4vO/baQBDi+SXuhUonw8dP +TqvDDZy96/irc7SudvsJdcpcFn9tGfASDez56uZqt/39wsA+uUsym9yV39gcZPRz +eoiVnxDny1dcNJSqlGkcDp7BqXaj2/e6bK5RW3cpUnnRk4M7weDsdblBJLYPAIqT +GMmZChf/iKAz6i9E+FRuXi3rhFJKUEGv+5nh7Pjd/9BxmVyjl9f9SWhe+AkimOs+ +CYrhlcGAHJ6XKq5KAMB43elZxSXgv6X5eTVhQsqf7X0e8n0OrXKqP+fqrQMPnkOs +VvEjq90mcG1mvvPrMoXN0XN1dfJEhyRB/hwi5QIDAQABAoIBAEk7zT0hstJkrRas +BH+QBntsMbfhU/3SrQwq81WN4aq/tJXFkIpyT6/izRE2df4XVYqE27YuYe9F6yad +ze9KjhPzjhgW9FmJNCwOsamgkFu0v74RCaC/kB4Go8JrXgCJaUFhhyhliNP6nSFR +87oF1gK8LZYinGCBh4wMO/KGC5SW6X9W6xf7f6RJYSPUH7h80XwnDRvsSnldH/3e +QPciQXP8fPUUW9EP9WERjpWTBZ8YqUG5P2w6ZlCrJ7L+/STMbscD3dYTUXxQUsfh +oxjsi9BQKTPFqiLH7gstCfdoufQJkfWMLSxpn8bBiHf5nhehuRRd9hKwz8+S1jdE +Lv0FYAECgYEAy8h9t5QwW3NSD1WclIFcxQhUeHIWHjLB8KJZi9eYJ4oZ7zWa5duV +0DTs3vJfVvxJ8XLbjnaap7ULWDZETbm2m6d5zJBaEGqLYShzlr15vNp0ZzDZghw+ +9hG/TqAB1jj76RYbI9h4TvzSI3+mbq3nl5Sykz1Envuznz2JiKbefIUCgYEAwj5h +fS0wAa5zjR8Lgs1nRFmg37qH6gn6w40yDDYwxDtM2L1l96ONPROvm6RZkM6hkHge +dKzabpHrh7eViQOgbUO+tyxttOdBspdK1ubi7UZTG6xq4zuzqiITP+BnhSw2mzDR +J276DMNalsmzQdI8v+eIMK0yxqOobcgRK979iuECgYBw/NP/onGBcxpPmEc968/1 +Cx5SvebXjYsMkeeWas5ZNfAVOqKMychx7bZcEwSbpTyWW/myLr6nN/F3UndipRLD +kQMuUect7PUkxJn6PUovVOxvfp1Kz8B1DPgGbx81mNjLrs8Te+WQ3grhVdiAy3l6 +CR9OFg1jHOnF5AfKtcLsRQKBgGL800WtX4eb1XsXVRBliLjGTDt3nYfhag95xwV+ +IEAAUFsruekHShTUEWvpx1MKWj97V1nyNKagaj0Ri3z1gi3sliZW19mW+F4Ax7zY +kNCGRBgYN6hxZk/Paavluhudun4/1HaaEYerjmDFjTp/30GUxky4FuYvxMeda1LG +IsNBAoGBAIeBiXXaB8QhlP4vJu6HT9IDZKRRiYTNjiS1o8CQ2U86FfyO7OVQ+19R +DJCYOc10foiwv+HEsEVXAjux79Z2h2dqqxtVoWPNh6yGs0SDKObmPWOEZsFABeF4 +RFTKlWMq0tvbXmnGwciA3Oy9DbsHp5jTo6qbuewVvE5PL7GTnokF +-----END RSA PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIIDPjCCAiYCCQC8Y6/8ayWo6DANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQGEwJU +UjEQMA4GA1UECgwHUGVyY29uYTESMBAGA1UEAwwJbG9jYWxob3N0MSwwKgYJKoZI +hvcNAQkBFh1udXJsYW4ubW9sZG9tdXJvdkBwZXJjb25hLmNvbTAeFw0yMzA3MDYy +MDA2NDNaFw0yNDA3MDUyMDA2NDNaMGExCzAJBgNVBAYTAlRSMRAwDgYDVQQKDAdQ +ZXJjb25hMRIwEAYDVQQDDAlsb2NhbGhvc3QxLDAqBgkqhkiG9w0BCQEWHW51cmxh +bi5tb2xkb211cm92QHBlcmNvbmEuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAmp+Xbi1C79S2l7IawE0dIBaKlx4vO/baQBDi+SXuhUonw8dPTqvD +DZy96/irc7SudvsJdcpcFn9tGfASDez56uZqt/39wsA+uUsym9yV39gcZPRzeoiV +nxDny1dcNJSqlGkcDp7BqXaj2/e6bK5RW3cpUnnRk4M7weDsdblBJLYPAIqTGMmZ +Chf/iKAz6i9E+FRuXi3rhFJKUEGv+5nh7Pjd/9BxmVyjl9f9SWhe+AkimOs+CYrh +lcGAHJ6XKq5KAMB43elZxSXgv6X5eTVhQsqf7X0e8n0OrXKqP+fqrQMPnkOsVvEj +q90mcG1mvvPrMoXN0XN1dfJEhyRB/hwi5QIDAQABMA0GCSqGSIb3DQEBCwUAA4IB +AQB9BKnOT2KiKTdnydorEpuMgzD1RZ9bfX8mGiucjPh5lcjO6L9haUbFN/6PupZP +x1WRKNwYm+R2vP/Q1tlkOwVDfBtycAhyx5NMRyHkmG90ap/hJUThF2D9Q+5A81Ma +tnpg5jbxPEBeMHujGZmDEiBNPHc9oP7HNuPXMzZWxAOjRAg2WtaqjyJi8ExnCP1t +4ELKdjojtSefhxQzZmdHNBKWa0kUJhDGfhvSo0//H9n8Q7VMmtVS94Klu/H+IG88 +EYmEzgkmty2eie+Jiv+S2WGDEUuopAReifGscFI3tYvNBbeU4GbtUCXKoNX8N6hO +1POaPPj84EK2ncLJXffk0XYq +-----END CERTIFICATE----- diff --git a/managed/testdata/pg/Makefile b/managed/testdata/pg/Makefile index f112b5db0c..ebfac42936 100644 --- a/managed/testdata/pg/Makefile +++ b/managed/testdata/pg/Makefile @@ -11,12 +11,12 @@ all: root-ssl server-ssl pmm-managed-ssl grafana-ssl ## Generates all required d root-ssl: ## Generates root-ssl cert. openssl req -new -sha256 -nodes -newkey rsa:2048 \ -config ./certs/root.cnf \ - -keyout /tmp/root.key \ - -out /tmp/root.csr + -keyout ./certs/root.key \ + -out ./certs/root.csr openssl x509 -req -days 3653 -sha256 \ - -in /tmp/root.csr \ + -in ./certs/root.csr \ -extfile /etc/ssl/openssl.cnf -extensions v3_ca \ - -signkey /tmp/root.key \ + -signkey ./certs/root.key \ -out ./certs/root.crt server-ssl: ## Generates server-ssl cert. @@ -26,9 +26,11 @@ server-ssl: ## Generates server-ssl cer -out /tmp/server.csr openssl x509 -req -days 3653 -sha256 \ -extfile ./certs/server.cnf -extensions req_ext \ - -CA ./certs/root.crt -CAkey /tmp/root.key -CAcreateserial \ + -CA ./certs/root.crt -CAkey ./certs/root.key -CAcreateserial \ -in /tmp/server.csr \ -out ./certs/server.crt + chmod 600 certs/server.crt + chmod 600 certs/server.key pmm-managed-ssl: ## Generates pmm-managed-ssl cert. openssl req -new -sha256 -nodes -newkey rsa:2048 \ @@ -39,6 +41,20 @@ pmm-managed-ssl: ## Generates pmm-managed-ssl -CA ./certs/root.crt -CAkey /tmp/root.key -CAcreateserial \ -in /tmp/pmm-managed.csr \ -out ./certs/pmm-managed.crt + chmod 600 certs/pmm-managed.crt + chmod 600 certs/pmm-managed.key + +grafana-ssl: ## Generates grafana-ssl cert. + openssl req -new -sha256 -nodes -newkey rsa:2048 \ + -config ./certs/grafana.cnf \ + -keyout ./certs/grafana.key \ + -out /tmp/grafana.csr + openssl x509 -req -days 3653 -sha256 \ + -CA ./certs/root.crt -CAkey /tmp/root.key -CAcreateserial \ + -in /tmp/grafana.csr \ + -out ./certs/grafana.crt + chmod 600 certs/grafana.crt + chmod 600 certs/grafana.key grafana-ssl: ## Generates grafana-ssl cert. openssl req -new -sha256 -nodes -newkey rsa:2048 \ diff --git a/managed/testdata/pg/conf/pg_hba.conf b/managed/testdata/pg/conf/pg_hba.conf index 81144295c3..c9d25ee111 100644 --- a/managed/testdata/pg/conf/pg_hba.conf +++ b/managed/testdata/pg/conf/pg_hba.conf @@ -1,5 +1,2 @@ local all all trust -hostnossl all pmm-managed all reject -hostssl all pmm-managed all cert -hostnossl all grafana all reject -hostssl all grafana all cert \ No newline at end of file +hostnossl all all trust \ No newline at end of file diff --git a/managed/testdata/supervisord.d/grafana.ini b/managed/testdata/supervisord.d/grafana.ini index c6a0788699..b049c82690 100644 --- a/managed/testdata/supervisord.d/grafana.ini +++ b/managed/testdata/supervisord.d/grafana.ini @@ -7,14 +7,14 @@ command = --homepath=/usr/share/grafana --config=/etc/grafana/grafana.ini environment = - PERCONA_TEST_POSTGRES_ADDR="", - PERCONA_TEST_POSTGRES_DBNAME="", - PERCONA_TEST_POSTGRES_USERNAME="", - PERCONA_TEST_POSTGRES_DBPASSWORD="", - PERCONA_TEST_POSTGRES_SSL_MODE="", - PERCONA_TEST_POSTGRES_SSL_CA_PATH="", - PERCONA_TEST_POSTGRES_SSL_KEY_PATH="", - PERCONA_TEST_POSTGRES_SSL_CERT_PATH="", + PERCONA_TEST_POSTGRES_ADDR="127.0.0.1:5432", + PERCONA_TEST_POSTGRES_DBNAME="postgres", + PERCONA_TEST_POSTGRES_USERNAME="db_username", + PERCONA_TEST_POSTGRES_DBPASSWORD="db_password", + PERCONA_TEST_POSTGRES_SSL_MODE="verify", + PERCONA_TEST_POSTGRES_SSL_CA_PATH="path-to-CA-cert", + PERCONA_TEST_POSTGRES_SSL_KEY_PATH="path-to-key", + PERCONA_TEST_POSTGRES_SSL_CERT_PATH="path-to-cert", PERCONA_TEST_PMM_CLICKHOUSE_DATASOURCE_ADDR="127.0.0.1:8123", PERCONA_TEST_PMM_CLICKHOUSE_HOST="127.0.0.1", PERCONA_TEST_PMM_CLICKHOUSE_PORT="9000", diff --git a/managed/testdata/supervisord.d/pmm-db_disabled.ini b/managed/testdata/supervisord.d/pmm-db_disabled.ini index b45251ab1f..8e1cb82648 100644 --- a/managed/testdata/supervisord.d/pmm-db_disabled.ini +++ b/managed/testdata/supervisord.d/pmm-db_disabled.ini @@ -77,7 +77,7 @@ redirect_stderr = true priority = 15 command = /usr/sbin/pmm-agent --config-file=/usr/local/percona/pmm2/config/pmm-agent.yaml autorestart = true -autostart = true +autostart = false startretries = 1000 startsecs = 1 stopsignal = TERM diff --git a/managed/testdata/supervisord.d/pmm-db_enabled.ini b/managed/testdata/supervisord.d/pmm-db_enabled.ini index 4d4ad4afd9..4ce13c4281 100644 --- a/managed/testdata/supervisord.d/pmm-db_enabled.ini +++ b/managed/testdata/supervisord.d/pmm-db_enabled.ini @@ -101,7 +101,7 @@ redirect_stderr = true priority = 15 command = /usr/sbin/pmm-agent --config-file=/usr/local/percona/pmm2/config/pmm-agent.yaml autorestart = true -autostart = true +autostart = false startretries = 1000 startsecs = 1 stopsignal = TERM diff --git a/managed/utils/envvars/parser.go b/managed/utils/envvars/parser.go index 3412e95f23..780aca1433 100644 --- a/managed/utils/envvars/parser.go +++ b/managed/utils/envvars/parser.go @@ -69,8 +69,10 @@ func (e InvalidDurationError) Error() string { return string(e) } // - the environment variables prefixed with GF_ passed as related to Grafana. // - the environment variables relating to proxies // - the environment variable set by podman -func ParseEnvVars(envs []string) (envSettings *models.ChangeSettingsParams, errs []error, warns []string) { //nolint:cyclop,nonamedreturns - envSettings = &models.ChangeSettingsParams{} +func ParseEnvVars(envs []string) (*models.ChangeSettingsParams, []error, []string) { //nolint:cyclop,maintidx + envSettings := &models.ChangeSettingsParams{} + var errs []error + var warns []string for _, env := range envs { p := strings.SplitN(env, "=", 2) diff --git a/update/.ansible-lint b/update/.ansible-lint index 29c64847a3..0e1628d74b 100644 --- a/update/.ansible-lint +++ b/update/.ansible-lint @@ -11,3 +11,4 @@ warn_list: # don't move to 'skip_list' - it will silence them completely - no-changed-when # Commands should not change things if nothing needs doing - parser-error # AnsibleParserError - ignore-errors # Use failed_when and specify error conditions instead of using ignore_errors + - empty-string-compare # Don't compare to empty string diff --git a/update/ansible/playbook/tasks/roles/initialization/tasks/main.yml b/update/ansible/playbook/tasks/roles/initialization/tasks/main.yml index b059f399eb..5113096993 100644 --- a/update/ansible/playbook/tasks/roles/initialization/tasks/main.yml +++ b/update/ansible/playbook/tasks/roles/initialization/tasks/main.yml @@ -107,20 +107,28 @@ name: postgres when: is_postgres_11.stat.exists -- name: Create grafana database in postgres - postgresql_db: - name: grafana - state: present - -- name: Create grafana user in postgres - postgresql_user: - db: grafana - name: grafana - password: grafana - priv: 'ALL' - expires: infinity - state: present - when: not ansible_check_mode +- name: Create grafana DB + block: + - name: Create grafana database in postgres + postgresql_db: + name: grafana + state: present + + - name: Create grafana user in postgres + postgresql_user: + db: grafana + name: grafana + password: grafana + priv: 'ALL' + expires: infinity + state: present + when: not ansible_check_mode + when: lookup('env','GF_DATABASE_URL') == '' and lookup('env','GF_DATABASE_HOST') == '' + +- name: Upgrade grafana database (Get the latest schema) + command: grafana cli --homepath=/usr/share/grafana admin data-migration encrypt-datasource-passwords + changed_when: True + when: lookup('env','PMM_TEST_HA_BOOTSTRAP') != '' and not pmm_current_version is version(pmm_image_version, '>=') - name: Create working directory for Alertmanager file: path=/srv/alertmanager/data state=directory owner=pmm group=pmm From 3749f318514c3b278bf0d5c3104be1bda8b1f40f Mon Sep 17 00:00:00 2001 From: Alex Demidoff Date: Tue, 28 Nov 2023 12:35:08 +0300 Subject: [PATCH 07/15] PMM-12462 Add HA usage telemetry (#2548) * PMM-12462 Add HA usage telemetry * PMM-12462 update the envvar list * PMM-12462 add more vars to the config * Update managed/services/telemetry/config.default.yml Co-authored-by: Nurlan Moldomurov * PMM-12462 follow up on review * PMM-12462 update the ids --------- Co-authored-by: Nurlan Moldomurov --- managed/services/telemetry/config.default.yml | 51 +++++++------------ 1 file changed, 19 insertions(+), 32 deletions(-) diff --git a/managed/services/telemetry/config.default.yml b/managed/services/telemetry/config.default.yml index 301a8a0f10..ee91f03464 100644 --- a/managed/services/telemetry/config.default.yml +++ b/managed/services/telemetry/config.default.yml @@ -970,35 +970,22 @@ telemetry: - metric_name: "postgresql_db_count" value: 1 - # Note: use these for testing and PMM-12462 - # - id: PMMServerFeatureToggles - # source: ENV_VARS - # summary: "Use of feature toggles in PMM Server" - # data: - # - metric_name: "pmm_server_disable_telemetry" - # column: "DISABLE_TELEMETRY" - # - metric_name: "pmm_server_enable_alerting" - # column: "ENABLE_ALERTING" - # - metric_name: "pmm_server_enable_backup_management" - # column: "ENABLE_BACKUP_MANAGEMENT" - # - metric_name: "pmm_server_enable_debug" - # column: "ENABLE_DEBUG" - # - metric_name: "pmm_server_enable_rbac" - # column: "ENABLE_RBAC" - - # - id: PMMServerFeatureTogglesStripValues - # source: ENV_VARS - # summary: "Use of feature toggles in PMM Server" - # transform: - # type: StripValues - # data: - # - metric_name: "pmm_server_disable_telemetry" - # column: "DISABLE_TELEMETRY" - # - metric_name: "pmm_server_enable_alerting" - # column: "ENABLE_ALERTING" - # - metric_name: "pmm_server_enable_backup_management" - # column: "ENABLE_BACKUP_MANAGEMENT" - # - metric_name: "pmm_server_enable_debug" - # column: "ENABLE_DEBUG" - # - metric_name: "pmm_server_enable_rbac" - # column: "ENABLE_RBAC" + - id: PMMServerHAEnabled + source: ENV_VARS + summary: "Use of HA feature" + data: + - metric_name: "pmm_server_ha_enable" + column: "PERCONA_TEST_HA_ENABLE" + + - id: PMMServerHAUseOfDBs + source: ENV_VARS + transform: + type: StripValues + summary: "Use of External DBs" + data: + - metric_name: "pmm_server_ha_clickhouse_address" + column: "PERCONA_TEST_PMM_CLICKHOUSE_ADDR" + - metric_name: "pmm_server_ha_postgres_address" + column: "PERCONA_TEST_POSTGRES_ADDR" + - metric_name: "pmm_server_ha_vm_url" + column: "PMM_VM_URL" From bef98bfd0f1712768056e13924e496a0cfe396e3 Mon Sep 17 00:00:00 2001 From: PMM Jenkins Date: Tue, 28 Nov 2023 10:56:00 +0000 Subject: [PATCH 08/15] Update descriptors --- descriptor.bin | Bin 788061 -> 791366 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/descriptor.bin b/descriptor.bin index 91825581b28bbafe5d69802602c072522d5e7b61..64d395d96791db292e976c5cc4452a4f1810d093 100644 GIT binary patch delta 47689 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 Je)~Ub{{mp;gOLCL From e648369e3eaff2e0c6605a28a0f20acfdcc6440f Mon Sep 17 00:00:00 2001 From: Alex Demidoff Date: Wed, 29 Nov 2023 13:16:36 +0300 Subject: [PATCH 09/15] PMM-12696 Add an API test workflow (#2649) * PMM-12696 Add an API test workflow * PMM-12696 Update the test name * PMM-12696 Update the test name * PMM-12696 Add ref to the pipelines * PMM-12696 add the branch param to workflow_call --- .github/workflows/api-tests.yml | 156 ++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 .github/workflows/api-tests.yml diff --git a/.github/workflows/api-tests.yml b/.github/workflows/api-tests.yml new file mode 100644 index 0000000000..897e725e01 --- /dev/null +++ b/.github/workflows/api-tests.yml @@ -0,0 +1,156 @@ +name: 'API' + +on: + # NOTE: no action on `push` and `pull_request` since the API tests need to be run + # against a container that has the latest changes from your code. Therefore, the + # PMM_SERVER_IMAGE should either be the one from your feature build or the local + # devcontainer. One more scenario is when we want to run the API tests on dev-latest + # to see if the tests are in a good shape. + # That said, this workflow is mostly a convenience if you prefer Github to Jenkins. + # https://github.com/Percona-Lab/jenkins-pipelines/blob/master/pmm/pmm2-api-tests.groovy + + workflow_dispatch: + inputs: + MYSQL_IMAGE: + description: "MYSQL image version" + default: "percona:5.7" + required: true + type: string + POSTGRESQL_IMAGE: + description: "POSTGRESQL image version" + default: "postgres:12" + required: true + type: string + MONGODB_IMAGE: + description: "MONGODB image version" + default: "percona/percona-server-mongodb:4.4" + required: true + type: string + PMM_SERVER_IMAGE: + description: "PMM Server image version" + default: "perconalab/pmm-server:dev-latest" + required: true + type: string + + workflow_call: + inputs: + BRANCH: + description: "The branch to source API tests from" + default: "main" + required: true + type: string + MYSQL_IMAGE: + description: "MYSQL image version" + default: "percona:5.7" + required: true + type: string + POSTGRESQL_IMAGE: + description: "POSTGRESQL image version" + default: "postgres:12" + required: true + type: string + MONGODB_IMAGE: + description: "MONGODB image version" + default: "percona/percona-server-mongodb:4.4" + required: true + type: string + PMM_SERVER_IMAGE: + description: "PMM Server image version" + default: "perconalab/pmm-server:dev-latest" + required: true + type: string + +jobs: + test: + name: Tests + runs-on: ubuntu-22.04 + env: + PMM_URL: http://admin:admin@127.0.0.1 + BRANCH: ${{ github.event.inputs.BRANCH || 'main' }} + MYSQL_IMAGE: ${{ github.event.inputs.MYSQL_IMAGE || 'percona:5.7' }} + POSTGRESQL_IMAGE: ${{ github.event.inputs.POSTGRESQL_IMAGE || 'postgres:12' }} + MONGODB_IMAGE: ${{ github.event.inputs.MONGODB_IMAGE || 'percona/percona-server-mongodb:4.4' }} + PMM_SERVER_IMAGE: ${{ github.event.inputs.PMM_SERVER_IMAGE || 'perconalab/pmm-server:dev-latest' }} + + steps: + - name: Check out code + uses: actions/checkout@v4 + with: + ref: ${{ env.BRANCH }} + + - name: Login to docker.io registry + uses: docker/login-action@v3 + with: + registry: docker.io + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_PASSWORD }} + + - name: Run PMM Server + run: | + cat <<-EOF > compose.pmm-server-test.yml + services: + pmm-server: + image: ${{ env.PMM_SERVER_IMAGE }} + container_name: pmm-agent_pmm-server + environment: + - PMM_DEBUG=1 + - PERCONA_TEST_CHECKS_INTERVAL=10s + - PERCONA_TEST_PLATFORM_ADDRESS=https://check-dev.percona.com + - PERCONA_TEST_PLATFORM_PUBLIC_KEY=RWTg+ZmCCjt7O8eWeAmTLAqW+1ozUbpRSKSwNTmO+exlS5KEIPYWuYdX + ports: + - 80:80 + - 443:443 + volumes: + - ./managed/testdata/checks:/srv/checks + EOF + + # Run it and wait until it's healthy + docker compose -f compose.pmm-server-test.yml up -d --wait --wait-timeout=100 + + - name: Build the test image + shell: bash + run: | + docker build -t percona/pmm-api-tests . + + - name: Run DB containers + shell: bash + run: | + pushd api-tests + docker compose up test_db # no daemon mode + # TODO: review the provisions below (copied from the Jenkins pipeline) + # MYSQL_IMAGE=${{env.MYSQL_IMAGE}} docker compose up -d mysql + # MONGO_IMAGE=${{env.MONGODB_IMAGE}} docker compose up -d mongo + # POSTGRES_IMAGE=${{env.POSTGRESQL_IMAGE}} docker compose up -d postgres + # docker compose up -d sysbench + popd + + - name: Check connectivity to PMM Server + shell: bash + run: curl -f ${{env.PMM_URL}}/ping + + - name: Run API tests + shell: bash + run: | + docker run -e PMM_SERVER_URL=${{env.PMM_URL}} \ + -e PMM_RUN_UPDATE_TEST=0 \ + -e PMM_RUN_STT_TESTS=0 \ + --name pmm-api-tests \ + --network host \ + percona/pmm-api-tests + + - name: Get PMM logs + if: ${{ failure() }} + run: curl --insecure ${{env.PMM_URL}}/logs.zip --output ${{ github.workspace }}/logs.zip || true + + - name: Upload the logs on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v3 + with: + name: "logs.zip" + path: ${{ github.workspace }}/logs.zip + + - name: Run debug commands on failure + if: ${{ failure() }} + run: | + echo "----- ENVIRONMENT VARIABLES -----" + env | sort From 1951850844c3b1539db4849f585965f6d5fe2356 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Nov 2023 12:44:11 +0200 Subject: [PATCH 10/15] Bump github.com/grafana/grafana-api-golang-client from 0.25.0 to 0.26.0 (#2609) Bumps [github.com/grafana/grafana-api-golang-client](https://github.com/grafana/grafana-api-golang-client) from 0.25.0 to 0.26.0. - [Release notes](https://github.com/grafana/grafana-api-golang-client/releases) - [Commits](https://github.com/grafana/grafana-api-golang-client/compare/v0.25.0...v0.26.0) --- updated-dependencies: - dependency-name: github.com/grafana/grafana-api-golang-client dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Artem Gavrilov --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 7c0add89df..912b0e2c83 100644 --- a/go.mod +++ b/go.mod @@ -43,7 +43,7 @@ require ( github.com/golang-migrate/migrate/v4 v4.16.1 github.com/golang/protobuf v1.5.3 github.com/google/uuid v1.4.0 - github.com/grafana/grafana-api-golang-client v0.25.0 + github.com/grafana/grafana-api-golang-client v0.26.0 github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 diff --git a/go.sum b/go.sum index c4285512b7..d50daaaf8b 100644 --- a/go.sum +++ b/go.sum @@ -412,8 +412,8 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4= github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/grafana/grafana-api-golang-client v0.25.0 h1:jDxnR0U5xgIwKzE+IliZJvjMUUTQxGq+c1s+3M46flI= -github.com/grafana/grafana-api-golang-client v0.25.0/go.mod h1:24W29gPe9yl0/3A9X624TPkAOR8DpHno490cPwnkv8E= +github.com/grafana/grafana-api-golang-client v0.26.0 h1:Eu2YsfUezYngy8ifvmLybgluIcn/2IS9u1xkzuYstEM= +github.com/grafana/grafana-api-golang-client v0.26.0/go.mod h1:uNLZEmgKtTjHBtCQMwNn3qsx2mpMb8zU+7T4Xv3NR9Y= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 h1:RtRsiaGvWxcwd8y3BiRZxsylPT8hLWZ5SPcfI+3IDNk= From 9db1746fe624e82738d67a00717ad681a8096b9f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Nov 2023 12:45:37 +0200 Subject: [PATCH 11/15] Bump eslint from 8.53.0 to 8.54.0 in /cli-tests (#2627) Bumps [eslint](https://github.com/eslint/eslint) from 8.53.0 to 8.54.0. - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/compare/v8.53.0...v8.54.0) --- updated-dependencies: - dependency-name: eslint dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Artem Gavrilov --- cli-tests/package-lock.json | 16 ++++++++-------- cli-tests/package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cli-tests/package-lock.json b/cli-tests/package-lock.json index b34019a698..24981a881b 100644 --- a/cli-tests/package-lock.json +++ b/cli-tests/package-lock.json @@ -22,7 +22,7 @@ "@types/shelljs": "^0.8.12", "@typescript-eslint/eslint-plugin": "^6.10.0", "@typescript-eslint/parser": "^6.10.0", - "eslint": "8.53", + "eslint": "8.54", "eslint-config-airbnb-base": "^15.0.0", "eslint-config-airbnb-typescript": "^17.1.0", "eslint-plugin-import": "^2.29.0", @@ -86,9 +86,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.53.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz", - "integrity": "sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==", + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.54.0.tgz", + "integrity": "sha512-ut5V+D+fOoWPgGGNj83GGjnntO39xDy6DWxO0wb7Jp3DcMX0TfIqdzHF85VTQkerdyGmuuMD9AKAo5KiNlf/AQ==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -942,15 +942,15 @@ } }, "node_modules/eslint": { - "version": "8.53.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz", - "integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==", + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.54.0.tgz", + "integrity": "sha512-NY0DfAkM8BIZDVl6PgSa1ttZbx3xHgJzSNJKYcQglem6CppHyMhRIQkBVSSMaSRnLhig3jsDbEzOjwCVt4AmmA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.3", - "@eslint/js": "8.53.0", + "@eslint/js": "8.54.0", "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", diff --git a/cli-tests/package.json b/cli-tests/package.json index 9d6be03827..3d72e3f470 100644 --- a/cli-tests/package.json +++ b/cli-tests/package.json @@ -26,7 +26,7 @@ "@types/shelljs": "^0.8.12", "@typescript-eslint/eslint-plugin": "^6.10.0", "@typescript-eslint/parser": "^6.10.0", - "eslint": "8.53", + "eslint": "8.54", "eslint-config-airbnb-base": "^15.0.0", "eslint-config-airbnb-typescript": "^17.1.0", "eslint-plugin-import": "^2.29.0", From 1d3977c665ff11bc17ae533d339b8bd22c1a30f4 Mon Sep 17 00:00:00 2001 From: Nurlan Moldomurov Date: Tue, 5 Dec 2023 16:22:24 +0300 Subject: [PATCH 12/15] PMM-12760 fix offline upgrade failure. (#2675) --- .../playbook/tasks/roles/initialization/tasks/main.yml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/update/ansible/playbook/tasks/roles/initialization/tasks/main.yml b/update/ansible/playbook/tasks/roles/initialization/tasks/main.yml index 5113096993..81c57b7ff4 100644 --- a/update/ansible/playbook/tasks/roles/initialization/tasks/main.yml +++ b/update/ansible/playbook/tasks/roles/initialization/tasks/main.yml @@ -220,12 +220,10 @@ method: GET retries: 120 delay: 1 - - - name: Disable maintenance mode - file: - state: absent - path: /usr/share/pmm-server/maintenance/maintenance.html # We use current_version_file['failed'] because we don't want to run this on creating container when: docker_upgrade - +- name: Disable maintenance mode + file: + state: absent + path: /usr/share/pmm-server/maintenance/maintenance.html From 839b6826357f6181a506f7db04422013b7ba552e Mon Sep 17 00:00:00 2001 From: Nurlan Moldomurov Date: Tue, 5 Dec 2023 16:22:44 +0300 Subject: [PATCH 13/15] PMM-12761 fix race condition for services. (#2672) --- managed/services/ha/services.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/managed/services/ha/services.go b/managed/services/ha/services.go index eabc233c8a..393044b924 100644 --- a/managed/services/ha/services.go +++ b/managed/services/ha/services.go @@ -39,7 +39,7 @@ func newServices() *services { return &services{ all: make(map[string]LeaderService), running: make(map[string]LeaderService), - refresh: make(chan struct{}), + refresh: make(chan struct{}, 1), l: logrus.WithField("component", "ha-services"), } } From 9a3843dc52af8ee2561ae2d1b3ee254e313ad493 Mon Sep 17 00:00:00 2001 From: Nurlan Moldomurov Date: Tue, 5 Dec 2023 20:39:05 +0300 Subject: [PATCH 14/15] Revert "PMM-8655 Send unsent messages after connection problems (#1970)" (#2676) * Revert "PMM-8655 Send unsent messages after connection problems (#1970)" This reverts commit a3a52c1c * PMM-8655 fix build * PMM-8655 fix linter * PMM-8655 fix linter --- agent/agents/mysql/perfschema/perfschema.go | 13 - agent/client/cache/cache.go | 108 ----- agent/client/cache/dummy.go | 64 --- agent/client/channel/channel.go | 37 +- agent/client/channel/channel_test.go | 8 +- agent/client/client.go | 288 ++++++------ agent/client/client_test.go | 145 +----- agent/commands/run.go | 8 +- agent/config/config.go | 37 +- agent/config/config_test.go | 35 -- agent/models/agent_message.go | 39 -- agent/models/cache.go | 31 -- agent/utils/buffer-ring/bigqueue/bigqueue.go | 429 ------------------ .../buffer-ring/bigqueue/bigqueue_test.go | 266 ----------- agent/utils/errors/errors.go | 20 - go.mod | 1 - go.sum | 8 - managed/services/checks/checks.go | 4 +- 18 files changed, 187 insertions(+), 1354 deletions(-) delete mode 100644 agent/client/cache/cache.go delete mode 100644 agent/client/cache/dummy.go delete mode 100644 agent/models/agent_message.go delete mode 100644 agent/models/cache.go delete mode 100644 agent/utils/buffer-ring/bigqueue/bigqueue.go delete mode 100644 agent/utils/buffer-ring/bigqueue/bigqueue_test.go diff --git a/agent/agents/mysql/perfschema/perfschema.go b/agent/agents/mysql/perfschema/perfschema.go index 2f914fe692..ae4af57dd3 100644 --- a/agent/agents/mysql/perfschema/perfschema.go +++ b/agent/agents/mysql/perfschema/perfschema.go @@ -21,7 +21,6 @@ import ( "fmt" "io" "math" - "sync" "time" "github.com/AlekSi/pointer" // register SQL driver @@ -46,18 +45,6 @@ type ( summaryMap map[string]*eventsStatementsSummaryByDigest ) -// mySQLVersion contains. -type mySQLVersion struct { - version float64 - vendor string -} - -// versionsCache provides cached access to MySQL version. -type versionsCache struct { - rw sync.RWMutex - items map[string]*mySQLVersion -} - const ( retainHistory = 5 * time.Minute refreshHistory = 5 * time.Second diff --git a/agent/client/cache/cache.go b/agent/client/cache/cache.go deleted file mode 100644 index abd5805665..0000000000 --- a/agent/client/cache/cache.go +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 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 cache incapsulates agent message storing logic. -package cache - -import ( - "path" - - "github.com/pkg/errors" - "github.com/sirupsen/logrus" - - "github.com/percona/pmm/agent/config" - "github.com/percona/pmm/agent/models" - "github.com/percona/pmm/agent/utils/buffer-ring/bigqueue" - "github.com/percona/pmm/api/agentpb" -) - -// Cache represent cache implementation based on bigqueue. -type Cache struct { - l *logrus.Entry - // prioritized represent cache for high priority agent messages e.g. job, action results - prioritized *bigqueue.Ring - // unprioritized represent cache for low priority agent messages e.g. qan metrics - unprioritized *bigqueue.Ring -} - -// New recreates cache. -func New(cfg config.Cache) (*Cache, error) { - if cfg.Disable { - return nil, errors.New("disable in cache config is set to true") - } - if cfg.Dir == "" { - return nil, errors.New("cache directory is not set up") - } - l := logrus.WithField("component", "cache") - prioritized, err := bigqueue.New(path.Join(cfg.Dir, "prioritized"), cfg.PrioritizedSize, l.WithField("type", "prioritized")) - if err != nil { - return nil, err - } - unprioritized, err := bigqueue.New(path.Join(cfg.Dir, "unprioritized"), cfg.UnprioritizedSize, l.WithField("type", "unprioritized")) - if err != nil { - return nil, err - } - return &Cache{ - l: l, - prioritized: prioritized, - unprioritized: unprioritized, - }, nil -} - -// Send stores agent response to cache on nil channel. -func (c *Cache) Send(resp *models.AgentResponse) error { - var cache *bigqueue.Ring - switch resp.Payload.(type) { - case *agentpb.StartActionResponse, - *agentpb.StopActionResponse, - *agentpb.PBMSwitchPITRResponse, - *agentpb.StartJobResponse, - *agentpb.JobStatusResponse, - *agentpb.GetVersionsResponse, - *agentpb.JobProgress, - *agentpb.StopJobResponse, - *agentpb.CheckConnectionResponse, - *agentpb.JobResult, - *agentpb.ServiceInfoResponse: - cache = c.prioritized - default: - cache = c.unprioritized - } - return cache.Send(resp) -} - -// SendAndWaitResponse stores AgentMessages with AgentMessageRequestPayload on nil channel. -func (c *Cache) SendAndWaitResponse(payload agentpb.AgentRequestPayload) (agentpb.ServerResponsePayload, error) { //nolint:ireturn - switch payload.(type) { - case *agentpb.ActionResultRequest: - return c.prioritized.SendAndWaitResponse(payload) - case *agentpb.QANCollectRequest, - *agentpb.StateChangedRequest: - return c.unprioritized.SendAndWaitResponse(payload) - default: - } - return &agentpb.StateChangedResponse{}, nil -} - -// Close closes cache databases. -func (c *Cache) Close() { - c.prioritized.Close() - c.unprioritized.Close() -} - -// SetSender sets sender and sends stored agent messages with sender. -func (c *Cache) SetSender(s models.Sender) { - c.prioritized.SetSender(s) - c.unprioritized.SetSender(s) -} diff --git a/agent/client/cache/dummy.go b/agent/client/cache/dummy.go deleted file mode 100644 index 7381b8e960..0000000000 --- a/agent/client/cache/dummy.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 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 cache - -import ( - "sync/atomic" - - "github.com/pkg/errors" - - "github.com/percona/pmm/agent/models" - agenterrors "github.com/percona/pmm/agent/utils/errors" - "github.com/percona/pmm/api/agentpb" -) - -// Dummy represent dummy cache. -type Dummy struct { - s atomic.Pointer[models.Sender] -} - -// Close to satisfy interface. -func (*Dummy) Close() {} - -// Send drops agent responses on nil channel. -func (c *Dummy) Send(resp *models.AgentResponse) error { - s := c.s.Load() - if s == nil { - return nil - } - err := (*s).Send(resp) - if err != nil && errors.As(err, &agenterrors.ErrChanConn) { - c.s.CompareAndSwap(s, nil) - } - return err -} - -// SendAndWaitResponse drops AgentMessages on nil channel. -func (c *Dummy) SendAndWaitResponse(payload agentpb.AgentRequestPayload) (agentpb.ServerResponsePayload, error) { //nolint:ireturn - s := c.s.Load() - if s == nil { - return &agentpb.StateChangedResponse{}, nil - } - resp, err := (*s).SendAndWaitResponse(payload) - if err != nil && errors.As(err, &agenterrors.ErrChanConn) { - c.s.CompareAndSwap(s, nil) - } - return resp, err -} - -// SetSender sets sender. -func (c *Dummy) SetSender(s models.Sender) { - c.s.Store(&s) -} diff --git a/agent/client/channel/channel.go b/agent/client/channel/channel.go index c022681540..3d76c5a697 100644 --- a/agent/client/channel/channel.go +++ b/agent/client/channel/channel.go @@ -28,8 +28,6 @@ import ( "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/proto" - "github.com/percona/pmm/agent/models" - agenterrors "github.com/percona/pmm/agent/utils/errors" "github.com/percona/pmm/api/agentpb" ) @@ -48,6 +46,15 @@ type ServerRequest struct { Payload agentpb.ServerRequestPayload } +// AgentResponse represents agent's response. +// It is similar to agentpb.AgentMessage except it can contain only responses, +// and the payload is already unwrapped (XXX instead of AgentMessage_XXX). +type AgentResponse struct { + ID uint32 + Status *grpcstatus.Status + Payload agentpb.AgentResponsePayload +} + // Response is a type used to pass response from pmm-server to the subscriber. type Response struct { Payload agentpb.ServerResponsePayload @@ -111,7 +118,7 @@ func New(stream agentpb.Agent_ConnectClient) *Channel { func (c *Channel) close(err error) { c.closeOnce.Do(func() { c.l.Debugf("Closing with error: %+v", err) - c.closeErr = agenterrors.NewChannelClosedError(err) + c.closeErr = err c.m.Lock() for _, ch := range c.responses { // unblock all subscribers @@ -141,7 +148,7 @@ func (c *Channel) Requests() <-chan *ServerRequest { } // Send sends message to pmm-managed. It is no-op once channel is closed (see Wait). -func (c *Channel) Send(resp *models.AgentResponse) error { +func (c *Channel) Send(resp *AgentResponse) { msg := &agentpb.AgentMessage{ Id: resp.ID, } @@ -151,7 +158,7 @@ func (c *Channel) Send(resp *models.AgentResponse) error { if resp.Status != nil { msg.Status = resp.Status.Proto() } - return c.send(msg) + c.send(msg) } // SendAndWaitResponse sends request to pmm-managed, blocks until response is available. @@ -162,24 +169,21 @@ func (c *Channel) SendAndWaitResponse(payload agentpb.AgentRequestPayload) (agen id := atomic.AddUint32(&c.lastSentRequestID, 1) ch := c.subscribe(id) - err := c.send(&agentpb.AgentMessage{ + c.send(&agentpb.AgentMessage{ Id: id, Payload: payload.AgentMessageRequestPayload(), }) - if err != nil { - return nil, err - } resp := <-ch return resp.Payload, resp.Error } -func (c *Channel) send(msg *agentpb.AgentMessage) error { +func (c *Channel) send(msg *agentpb.AgentMessage) { c.sendM.Lock() select { case <-c.closeWait: c.sendM.Unlock() - return c.Wait() + return default: } @@ -197,12 +201,10 @@ func (c *Channel) send(msg *agentpb.AgentMessage) error { err := c.s.Send(msg) c.sendM.Unlock() if err != nil { - err = errors.Wrap(err, "failed to send message") - c.close(err) - return agenterrors.NewChannelClosedError(err) + c.close(errors.Wrap(err, "failed to send message")) + return } c.mSend.Inc() - return nil } // runReader receives messages from server. @@ -312,13 +314,10 @@ func (c *Channel) runReceiver() { c.l.Warnf("pmm-managed was not able to process message with id: %d, handling of that payload type is unimplemented", msg.Id) continue } - err := c.Send(&models.AgentResponse{ + c.Send(&AgentResponse{ ID: msg.Id, Status: grpcstatus.New(codes.Unimplemented, "can't handle message type sent, it is not implemented"), }) - if err != nil { - c.l.Error(err) - } } } } diff --git a/agent/client/channel/channel_test.go b/agent/client/channel/channel_test.go index 5be2fee495..5a785fa508 100644 --- a/agent/client/channel/channel_test.go +++ b/agent/client/channel/channel_test.go @@ -34,7 +34,6 @@ import ( "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/timestamppb" - "github.com/percona/pmm/agent/models" "github.com/percona/pmm/agent/utils/truncate" "github.com/percona/pmm/api/agentpb" ) @@ -150,6 +149,7 @@ func TestAgentRequestWithTruncatedInvalidUTF8(t *testing.T) { Mysql: &agentpb.MetricsBucket_MySQL{}, }} resp, err = channel.SendAndWaitResponse(&request) + require.NoError(t, err) assert.Nil(t, resp) } @@ -248,13 +248,12 @@ func TestServerRequest(t *testing.T) { for req := range channel.Requests() { assert.IsType(t, &agentpb.Ping{}, req.Payload) - err := channel.Send(&models.AgentResponse{ + channel.Send(&AgentResponse{ ID: req.ID, Payload: &agentpb.Pong{ CurrentTime: timestamppb.Now(), }, }) - assert.NoError(t, err) } } @@ -417,11 +416,10 @@ func TestUnexpectedResponsePayloadFromServer(t *testing.T) { channel, _, teardown := setup(t, connect, io.EOF) defer teardown() req := <-channel.Requests() - err := channel.Send(&models.AgentResponse{ + channel.Send(&AgentResponse{ ID: req.ID, Payload: &agentpb.Pong{ CurrentTime: timestamppb.Now(), }, }) - assert.NoError(t, err) } diff --git a/agent/client/client.go b/agent/client/client.go index f6ba5547a7..b79eb8e261 100644 --- a/agent/client/client.go +++ b/agent/client/client.go @@ -35,11 +35,9 @@ import ( grpcstatus "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/timestamppb" - "github.com/percona/pmm/agent/client/cache" "github.com/percona/pmm/agent/client/channel" "github.com/percona/pmm/agent/config" "github.com/percona/pmm/agent/connectionuptime" - "github.com/percona/pmm/agent/models" "github.com/percona/pmm/agent/runner" "github.com/percona/pmm/agent/runner/actions" // TODO https://jira.percona.com/browse/PMM-7206 "github.com/percona/pmm/agent/runner/jobs" @@ -74,6 +72,7 @@ type Client struct { l *logrus.Entry backoff *backoff.Backoff + done chan struct{} // for unit tests only dialTimeout time.Duration @@ -86,16 +85,13 @@ type Client struct { cus *connectionuptime.Service logStore *tailog.Store - - wg sync.WaitGroup - cache models.Cache } // New creates new client. // // Caller should call Run. func New(cfg configGetter, supervisor supervisor, r *runner.Runner, connectionChecker connectionChecker, sv softwareVersioner, sib serviceInfoBroker, cus *connectionuptime.Service, logStore *tailog.Store) *Client { //nolint:lll - out := &Client{ + return &Client{ cfg: cfg, supervisor: supervisor, connectionChecker: connectionChecker, @@ -108,20 +104,22 @@ func New(cfg configGetter, supervisor supervisor, r *runner.Runner, connectionCh cus: cus, logStore: logStore, } - - var err error - if out.cache, err = cache.New(cfg.Get().Cache); err != nil { - out.l.Infof("Failed to init cache: %s. Initializing cachelless client.", err) - out.cache = &cache.Dummy{} - } - return out } -// Connect connects to the server, processes requests and sends responses. +// Run connects to the server, processes requests and sends responses. +// +// Once Run exits, connection is closed, and caller should cancel supervisor's context. +// Then caller should wait until Done() channel is closed. +// That Client instance can't be reused after that. // // Returned error is already logged and should be ignored. It is returned only for unit tests. -func (c *Client) Connect(ctx context.Context) error { +func (c *Client) Run(ctx context.Context) error { c.l.Info("Starting...") + + c.rw.Lock() + c.done = make(chan struct{}) + c.rw.Unlock() + cfg := c.cfg.Get() // do nothing until ctx is canceled if config misses critical info @@ -135,6 +133,7 @@ func (c *Client) Connect(ctx context.Context) error { if missing != "" { c.l.Errorf("%s is not provided, halting.", missing) <-ctx.Done() + close(c.done) return errors.Wrap(ctx.Err(), "missing "+missing) } @@ -159,11 +158,13 @@ func (c *Client) Connect(ctx context.Context) error { } } if ctx.Err() != nil { + close(c.done) if dialErr != nil { return dialErr } return ctx.Err() } + defer func() { if err := dialResult.conn.Close(); err != nil { c.l.Errorf("Connection closed: %s.", err) @@ -171,16 +172,65 @@ func (c *Client) Connect(ctx context.Context) error { } c.l.Info("Connection closed.") }() - c.supervisor.ClearChangesChannel() - c.SendActualStatuses() - c.cache.SetSender(dialResult.channel) c.rw.Lock() c.md = dialResult.md c.channel = dialResult.channel c.rw.Unlock() - c.processChannelRequests(ctx) + // Once the client is connected, ctx cancellation is ignored by it. + // + // We start goroutines, and terminate the gRPC connection and exit Run when any of them exits: + // + // 1. processActionResults reads action results from action runner and sends them to the channel. + // It exits when the action runner is stopped by cancelling ctx. + // + // 2. processSupervisorRequests reads requests (status changes and QAN data) from the supervisor and sends them to the channel. + // It exits when the supervisor is stopped by the caller. + // Caller stops supervisor when Run is left and gRPC connection is closed. + // + // 3. processChannelRequests reads requests from the channel and processes them. + // It exits when an unexpected message is received from the channel, or when can't be received at all. + // When Run is left, caller stops supervisor, and that allows processSupervisorRequests to exit. + // + // Done() channel is closed when all three goroutines exited. + + // TODO Make 2 and 3 behave more like 1 - that seems to be simpler. + // https://jira.percona.com/browse/PMM-4245 + + c.supervisor.ClearChangesChannel() + c.SendActualStatuses() + + oneDone := make(chan struct{}, 4) + go func() { + c.processActionResults(ctx) + c.l.Debug("processActionResults is finished") + oneDone <- struct{}{} + }() + go func() { + c.processJobsResults(ctx) + c.l.Debug("processJobsResults is finished") + oneDone <- struct{}{} + }() + go func() { + c.processSupervisorRequests(ctx) + c.l.Debug("processSupervisorRequests is finished") + oneDone <- struct{}{} + }() + go func() { + c.processChannelRequests(ctx) + c.l.Debug("processChannelRequests is finished") + oneDone <- struct{}{} + }() + + <-oneDone + go func() { + <-oneDone + <-oneDone + <-oneDone + c.l.Info("Done.") + close(c.done) + }() return nil } @@ -188,7 +238,7 @@ func (c *Client) Connect(ctx context.Context) error { func (c *Client) SendActualStatuses() { for _, agent := range c.supervisor.AgentsList() { c.l.Infof("Sending status: %s (port %d).", agent.Status, agent.ListenPort) - resp, err := c.sendAndWaitResponse( + resp, err := c.channel.SendAndWaitResponse( &agentpb.StateChangedRequest{ AgentId: agent.AgentId, Status: agent.Status, @@ -205,6 +255,11 @@ func (c *Client) SendActualStatuses() { } } +// Done is closed when all supervisors's requests are sent (if possible) and connection is closed. +func (c *Client) Done() <-chan struct{} { + return c.done +} + func (c *Client) processActionResults(ctx context.Context) { for { select { @@ -212,7 +267,7 @@ func (c *Client) processActionResults(ctx context.Context) { if result == nil { continue } - resp, err := c.sendAndWaitResponse(result) + resp, err := c.channel.SendAndWaitResponse(result) if err != nil { c.l.Error(err) continue @@ -234,7 +289,7 @@ func (c *Client) processJobsResults(ctx context.Context) { if message == nil { continue } - c.send(&models.AgentResponse{ + c.channel.Send(&channel.AgentResponse{ ID: 0, // Jobs send messages that don't require any responses, so we can leave message ID blank. Payload: message, }) @@ -246,47 +301,59 @@ func (c *Client) processJobsResults(ctx context.Context) { } func (c *Client) processSupervisorRequests(ctx context.Context) { - for { - select { - case state := <-c.supervisor.Changes(): - if state == nil { - continue - } - resp, err := c.sendAndWaitResponse(state) - if err != nil { - c.l.Error(err) - continue - } - if resp == nil { - c.l.Warn("Failed to send StateChanged request.") + var wg sync.WaitGroup + + wg.Add(1) + go func() { + defer wg.Done() + + for { + select { + case state := <-c.supervisor.Changes(): + if state == nil { + continue + } + resp, err := c.channel.SendAndWaitResponse(state) + if err != nil { + c.l.Error(err) + continue + } + if resp == nil { + c.l.Warn("Failed to send StateChanged request.") + } + case <-ctx.Done(): + c.l.Infof("Supervisor Changes() channel drained.") + return } - case <-ctx.Done(): - c.l.Infof("Supervisor Changes() channel drained.") - return } - } -} + }() -func (c *Client) processQANRequests(ctx context.Context) { - for { - select { - case collect := <-c.supervisor.QANRequests(): - if collect == nil { - continue - } - resp, err := c.sendAndWaitResponse(collect) - if err != nil { - c.l.Error(err) - continue - } - if resp == nil { - c.l.Warn("Failed to send QanCollect request.") + wg.Add(1) + go func() { + defer wg.Done() + + for { + select { + case collect := <-c.supervisor.QANRequests(): + if collect == nil { + continue + } + resp, err := c.channel.SendAndWaitResponse(collect) + if err != nil { + c.l.Error(err) + continue + } + if resp == nil { + c.l.Warn("Failed to send QanCollect request.") + } + case <-ctx.Done(): + c.l.Infof("Supervisor QANRequests() channel drained.") + return } - case <-ctx.Done(): - c.l.Infof("Supervisor QANRequests() channel drained.") - return } - } + }() + + wg.Wait() } func (c *Client) processChannelRequests(ctx context.Context) { @@ -360,24 +427,23 @@ LOOP: } c.cus.RegisterConnectionStatus(time.Now(), true) - response := &models.AgentResponse{ + response := &channel.AgentResponse{ ID: req.ID, Payload: responsePayload, } if status != nil { response.Status = status } - c.send(response) + c.channel.Send(response) case <-ctx.Done(): break LOOP } } if err := c.channel.Wait(); err != nil { c.l.Debugf("Channel closed: %s.", err) - } else { - c.l.Debug("Channel closed.") + return } - c.l.Debug("processChannelRequests is finished") + c.l.Debug("Channel closed.") } func (c *Client) handleStartActionRequest(p *agentpb.StartActionRequest) error { @@ -667,8 +733,29 @@ type dialResult struct { // dial tries to connect to the server once. // State changes are logged via l. Returned error is not user-visible. func dial(dialCtx context.Context, cfg *config.Config, l *logrus.Entry) (*dialResult, error) { + opts := []grpc.DialOption{ + grpc.WithBlock(), + grpc.WithUserAgent("pmm-agent/" + version.Version), + } + if cfg.Server.WithoutTLS { + opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials())) + } else { + host, _, _ := net.SplitHostPort(cfg.Server.Address) + tlsConfig := tlsconfig.Get() + tlsConfig.ServerName = host + tlsConfig.InsecureSkipVerify = cfg.Server.InsecureTLS + opts = append(opts, grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig))) + } + + if cfg.Server.Username != "" { + opts = append(opts, grpc.WithPerRPCCredentials(&basicAuth{ + username: cfg.Server.Username, + password: cfg.Server.Password, + })) + } + l.Infof("Connecting to %s ...", cfg.Server.FilteredURL()) - conn, err := grpc.DialContext(dialCtx, cfg.Server.Address, getGRPCOps(cfg)...) + conn, err := grpc.DialContext(dialCtx, cfg.Server.Address, opts...) if err != nil { msg := err.Error() @@ -902,79 +989,6 @@ func convertAgentErrorToGrpcStatus(agentErr error) *grpcstatus.Status { return status } -func getGRPCOps(cfg *config.Config) []grpc.DialOption { - opts := []grpc.DialOption{ - grpc.WithBlock(), - grpc.WithUserAgent("pmm-agent/" + version.Version), - } - if cfg.Server.WithoutTLS { - opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials())) - } else { - host, _, _ := net.SplitHostPort(cfg.Server.Address) - tlsConfig := tlsconfig.Get() - tlsConfig.ServerName = host - tlsConfig.InsecureSkipVerify = cfg.Server.InsecureTLS - opts = append(opts, grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig))) - } - - if cfg.Server.Username != "" { - opts = append(opts, grpc.WithPerRPCCredentials(&basicAuth{ - username: cfg.Server.Username, - password: cfg.Server.Password, - })) - } - return opts -} - -// Start starts client processes that handle requests and sends responses. -func (c *Client) Start(ctx context.Context) { - if _, ok := c.cache.(*cache.Cache); ok { - c.wg.Add(1) - go func() { - defer c.wg.Done() - <-ctx.Done() - c.cache.Close() - }() - } - c.wg.Add(4) - go func() { - defer c.wg.Done() - c.processActionResults(ctx) - c.l.Debug("processActionResults is finished") - }() - go func() { - defer c.wg.Done() - c.processJobsResults(ctx) - c.l.Debug("processJobsResults is finished") - }() - go func() { - defer c.wg.Done() - c.processSupervisorRequests(ctx) - c.l.Debug("processSupervisorRequests is finished") - }() - go func() { - defer c.wg.Done() - c.processQANRequests(ctx) - c.l.Debug("processQANRequests is finished") - }() -} - -// Wait waits for client processes to stop. -func (c *Client) Wait() { - c.wg.Wait() -} - -func (c *Client) sendAndWaitResponse(msg agentpb.AgentRequestPayload) (agentpb.ServerResponsePayload, error) { //nolint:ireturn - return c.cache.SendAndWaitResponse(msg) -} - -func (c *Client) send(msg *models.AgentResponse) { - err := c.cache.Send(msg) - if err != nil { - c.l.Error(err) - } -} - // check interface. var ( _ prometheus.Collector = (*Client)(nil) diff --git a/agent/client/client_test.go b/agent/client/client_test.go index c49eb91fad..ce987348f8 100644 --- a/agent/client/client_test.go +++ b/agent/client/client_test.go @@ -18,8 +18,6 @@ import ( "context" "fmt" "net" - "os" - "path" "testing" "time" @@ -87,7 +85,7 @@ func TestClient(t *testing.T) { cfgStorage := config.NewStorage(&config.Config{}) client := New(cfgStorage, nil, nil, nil, nil, nil, nil, nil) cancel() - err := client.Connect(ctx) + err := client.Run(ctx) assert.EqualError(t, err, "missing PMM Server address: context canceled") }) @@ -102,7 +100,7 @@ func TestClient(t *testing.T) { }) client := New(cfgStorage, nil, nil, nil, nil, nil, nil, nil) cancel() - err := client.Connect(ctx) + err := client.Run(ctx) assert.EqualError(t, err, "missing Agent ID: context canceled") }) @@ -118,7 +116,7 @@ func TestClient(t *testing.T) { }, }) client := New(cfgStorage, nil, nil, nil, nil, nil, connectionuptime.NewService(time.Hour), nil) - err := client.Connect(ctx) + err := client.Run(ctx) assert.EqualError(t, err, "failed to dial: context deadline exceeded") }) @@ -167,8 +165,7 @@ func TestClient(t *testing.T) { r := runner.New(cfgStorage.Get().RunnerCapacity) client := New(cfgStorage, &s, r, nil, nil, nil, connectionuptime.NewService(time.Hour), nil) - client.Start(context.Background()) - err := client.Connect(context.Background()) + err := client.Run(context.Background()) assert.NoError(t, err) assert.Equal(t, serverMD, client.GetServerConnectMetadata()) }) @@ -197,7 +194,7 @@ func TestClient(t *testing.T) { client := New(cfgStorage, nil, nil, nil, nil, nil, connectionuptime.NewService(time.Hour), nil) client.dialTimeout = 100 * time.Millisecond - err := client.Connect(ctx) + err := client.Run(ctx) assert.EqualError(t, err, "failed to get server metadata: rpc error: code = Canceled desc = context canceled", "%+v", err) }) }) @@ -286,8 +283,7 @@ func TestUnexpectedActionType(t *testing.T) { r := runner.New(cfgStorage.Get().RunnerCapacity) client := New(cfgStorage, s, r, nil, nil, nil, connectionuptime.NewService(time.Hour), nil) - client.Start(context.Background()) - err := client.Connect(context.Background()) + err := client.Run(context.Background()) assert.NoError(t, err) assert.Equal(t, serverMD, client.GetServerConnectMetadata()) } @@ -383,132 +379,3 @@ func TestArgListFromMongoDBParams(t *testing.T) { }) } } - -func TestCache(t *testing.T) { - t.Parallel() - cacheSize := uint32(3 * 1024 * 1024) - t.Run("Read", func(t *testing.T) { - t.Parallel() - serverMD := &agentpb.ServerConnectMetadata{ServerVersion: t.Name()} - - // test payload - payload := &agentpb.QANCollectRequest{MetricsBucket: []*agentpb.MetricsBucket{{Common: &agentpb.MetricsBucket_Common{Queryid: "33b65211f7df97665e74b8f98dbc90d5"}}}} - - connect := func(stream agentpb.Agent_ConnectServer) error { - md, err := agentpb.ReceiveAgentConnectMetadata(stream) - require.NoError(t, err) - assert.Equal(t, &agentpb.AgentConnectMetadata{ID: "agent_id"}, md) - err = agentpb.SendServerConnectMetadata(stream, serverMD) - require.NoError(t, err) - msg, err := stream.Recv() - require.NoError(t, err) - ping := msg.GetPing() - require.NotNil(t, ping) - err = stream.Send(&agentpb.ServerMessage{ - Id: msg.Id, - Payload: (&agentpb.Pong{CurrentTime: timestamppb.Now()}).ServerMessageResponsePayload(), - }) - require.NoError(t, err) - msg, err = stream.Recv() - require.NoError(t, err) - require.Equal(t, payload.MetricsBucket[0].Common.Queryid, msg.Payload.(*agentpb.AgentMessage_QanCollect).QanCollect.MetricsBucket[0].Common.Queryid) - return nil - } - - // setup for client processes - qan := make(chan *agentpb.QANCollectRequest, 1) - s := &mockSupervisor{} - s.On("Changes").Return(make(<-chan *agentpb.StateChangedRequest)) - s.On("QANRequests").Return((<-chan *agentpb.QANCollectRequest)(qan)) - s.On("AgentsList").Return([]*agentlocalpb.AgentInfo{}) - s.On("ClearChangesChannel").Return() - - // setup for cache - testDirname := path.Join(os.TempDir(), fmt.Sprint(t.Name(), time.Now().UnixNano())) - t.Cleanup(func() { _ = os.RemoveAll(testDirname) }) - cfgStorage := config.NewStorage(&config.Config{Cache: config.Cache{Dir: testDirname, PrioritizedSize: cacheSize, UnprioritizedSize: cacheSize}}) - - // actual test - client := New(cfgStorage, s, runner.New(0), nil, nil, nil, connectionuptime.NewService(time.Hour), nil) - client.Start(context.Background()) - time.Sleep(1 * time.Second) // time to start processes - qan <- payload // sending request with qan on closed connection to store in cache - port, teardown := setup(t, connect) - defer teardown() - - *cfgStorage = *config.NewStorage(&config.Config{ - ID: "agent_id", - Server: config.Server{ - Address: fmt.Sprintf("127.0.0.1:%d", port), // prepare config with server params - WithoutTLS: true, - }, - }) - require.NoError(t, client.Connect(context.Background())) - }) - t.Run("Read with shutdown", func(t *testing.T) { - t.Parallel() - serverMD := &agentpb.ServerConnectMetadata{ServerVersion: t.Name()} - - // test payload - payload := &agentpb.QANCollectRequest{MetricsBucket: []*agentpb.MetricsBucket{{Common: &agentpb.MetricsBucket_Common{Queryid: "33b65211f7df97665e74b8f98dbc90d6"}}}} - - connect := func(stream agentpb.Agent_ConnectServer) error { - md, err := agentpb.ReceiveAgentConnectMetadata(stream) - require.NoError(t, err) - assert.Equal(t, &agentpb.AgentConnectMetadata{ID: "agent_id"}, md) - err = agentpb.SendServerConnectMetadata(stream, serverMD) - require.NoError(t, err) - msg, err := stream.Recv() - require.NoError(t, err) - ping := msg.GetPing() - require.NotNil(t, ping) - err = stream.Send(&agentpb.ServerMessage{ - Id: msg.Id, - Payload: (&agentpb.Pong{CurrentTime: timestamppb.Now()}).ServerMessageResponsePayload(), - }) - require.NoError(t, err) - msg, err = stream.Recv() - require.NoError(t, err) - require.Equal(t, payload.MetricsBucket[0].Common.Queryid, msg.Payload.(*agentpb.AgentMessage_QanCollect).QanCollect.MetricsBucket[0].Common.Queryid) - return nil - } - - // setup for client processes - qan := make(chan *agentpb.QANCollectRequest, 1) - s := &mockSupervisor{} - s.On("Changes").Return(make(<-chan *agentpb.StateChangedRequest)) - s.On("QANRequests").Return((<-chan *agentpb.QANCollectRequest)(qan)) - s.On("AgentsList").Return([]*agentlocalpb.AgentInfo{}) - s.On("ClearChangesChannel").Return() - r := runner.New(0) - - // setup for cache - testDirname := path.Join(os.TempDir(), fmt.Sprint(t.Name(), time.Now().UnixNano())) - t.Cleanup(func() { _ = os.RemoveAll(testDirname) }) - cfgStorage := config.NewStorage(&config.Config{Cache: config.Cache{Dir: testDirname, PrioritizedSize: cacheSize, UnprioritizedSize: cacheSize}}) - - // actual test - client := New(cfgStorage, s, r, nil, nil, nil, connectionuptime.NewService(time.Hour), nil) - ctx, cancel := context.WithCancel(context.Background()) - client.Start(ctx) // starting client processes - time.Sleep(1 * time.Second) // time to start processes - qan <- payload // sending request with qan on closed connection to store in cache - time.Sleep(1 * time.Second) // time to store message before close cache - cancel() // shuting down client - client.Wait() // closing cache and waiting for processes to stop - - client = New(cfgStorage, s, r, nil, nil, nil, connectionuptime.NewService(time.Hour), nil) // new client setup - client.Start(context.Background()) - port, teardown := setup(t, connect) - defer teardown() - - *cfgStorage = *config.NewStorage(&config.Config{ // prepare config with new server params - ID: "agent_id", - Server: config.Server{ - Address: fmt.Sprintf("127.0.0.1:%d", port), - WithoutTLS: true, - }, - }) - require.NoError(t, client.Connect(context.Background())) - }) -} diff --git a/agent/commands/run.go b/agent/commands/run.go index 2d57167511..c62fefc664 100644 --- a/agent/commands/run.go +++ b/agent/commands/run.go @@ -95,12 +95,10 @@ func Run() { localServer.Run(ctx, reloadCh) cancel() }() - client.Start(ctx) processClientUntilCancel(ctx, client, reloadCh) cleanupTmp(cfg.Paths.TempDir, l) - client.Wait() wg.Wait() select { case <-rootCtx.Done(): @@ -113,9 +111,13 @@ func Run() { func processClientUntilCancel(ctx context.Context, client *client.Client, reloadCh chan bool) { for { clientCtx, cancelClientCtx := context.WithCancel(ctx) + err := client.Run(clientCtx) + if err != nil { + logrus.Errorf("Client error: %s", err) + } - _ = client.Connect(clientCtx) cancelClientCtx() + <-client.Done() select { case <-reloadCh: diff --git a/agent/config/config.go b/agent/config/config.go index a233fcb278..74748a24c4 100644 --- a/agent/config/config.go +++ b/agent/config/config.go @@ -37,10 +37,8 @@ import ( ) const ( - pathBaseDefault = "/usr/local/percona/pmm2" - agentTmpPath = "tmp" // temporary directory to keep exporters' config files, relative to pathBase - prioritizedCacheSize = 100 * 1024 * 1024 // 100 MB TODO: R&D on median daily amount - unprioritizedCacheSize = 500 * 1024 * 1024 // 500 MB TODO: R&D on median daily amount + pathBaseDefault = "/usr/local/percona/pmm2" + agentTmpPath = "tmp" // temporary directory to keep exporters' config files, relative to pathBase ) // Server represents PMM Server configuration. @@ -142,18 +140,6 @@ type Setup struct { ExposeExporter bool } -// Cache represent cache settings. -type Cache struct { - // Dir represent file to store valuable agent messages - Dir string `yaml:"dir"` - // PrioritizedSize represent cache size for high priority agent messages e.g., job, action results - PrioritizedSize uint32 `yaml:"prioritized_size"` - // UnprioritizedSize represent cache size for low priority agent messages e.g., qan metrics - UnprioritizedSize uint32 `yaml:"unprioritized_size"` - // Disable disables cache - Disable bool `yaml:"disable"` -} - // Config represents pmm-agent's configuration. // //nolint:maligned @@ -176,7 +162,6 @@ type Config struct { //nolint:musttag LogLinesCount uint `json:"log-lines-count"` WindowConnectedTime time.Duration `yaml:"window-connected-time"` - Cache Cache `yaml:"cache"` Setup Setup `yaml:"-"` } @@ -223,12 +208,6 @@ func get(args []string, cfg *Config, l *logrus.Entry) (configFileF string, err e if cfg.WindowConnectedTime == 0 { cfg.WindowConnectedTime = time.Hour } - if cfg.Cache.PrioritizedSize == 0 { - cfg.Cache.PrioritizedSize = prioritizedCacheSize - } - if cfg.Cache.UnprioritizedSize == 0 { - cfg.Cache.UnprioritizedSize = unprioritizedCacheSize - } for sp, v := range map[*string]string{ &cfg.Paths.NodeExporter: "node_exporter", @@ -243,7 +222,6 @@ func get(args []string, cfg *Config, l *logrus.Entry) (configFileF string, err e &cfg.Paths.PTPGSummary: "tools/pt-pg-summary", &cfg.Paths.PTMongoDBSummary: "tools/pt-mongodb-summary", &cfg.Paths.PTMySQLSummary: "tools/pt-mysql-summary", - &cfg.Cache.Dir: "cache", } { if *sp == "" { *sp = v @@ -286,9 +264,6 @@ func get(args []string, cfg *Config, l *logrus.Entry) (configFileF string, err e if !filepath.IsAbs(cfg.Paths.PTMySQLSummary) { cfg.Paths.PTMySQLSummary = filepath.Join(cfg.Paths.PathsBase, cfg.Paths.PTMySQLSummary) } - if !filepath.IsAbs(cfg.Cache.Dir) { - cfg.Cache.Dir = filepath.Join(cfg.Paths.PathsBase, cfg.Cache.Dir) - } for _, sp := range []*string{ &cfg.Paths.NodeExporter, @@ -412,14 +387,6 @@ func Application(cfg *Config) (*kingpin.Application, *string) { Envar("PMM_AGENT_PATHS_PT_MYSQL_SUMMARY").StringVar(&cfg.Paths.PTMySQLSummary) app.Flag("paths-tempdir", "Temporary directory for exporters [PMM_AGENT_PATHS_TEMPDIR]"). Envar("PMM_AGENT_PATHS_TEMPDIR").StringVar(&cfg.Paths.TempDir) - app.Flag("cache-dir", "Directory for cache [PMM_AGENT_CACHE_DIR]"). - Envar("PMM_AGENT_CACHE_DIR").StringVar(&cfg.Cache.Dir) - app.Flag("cache-prioritized-size", "Cache size for high priority agent messages e.g., job, action results [PMM_AGENT_CACHE_PRIORITIZED_SIZE]"). - Envar("PMM_AGENT_CACHE_PRIORITIZED_SIZE").Uint32Var(&cfg.Cache.PrioritizedSize) - app.Flag("cache-unprioritized-size", "Cache for low priority agent messages e.g., qan metrics [PMM_AGENT_CACHE_UNPRIORITIZED_SIZE]"). - Envar("PMM_AGENT_CACHE_UNPRIORITIZED_SIZE").Uint32Var(&cfg.Cache.UnprioritizedSize) - app.Flag("cache-disable", "Disables cache [PMM_AGENT_CACHE_DISABLE]"). - Envar("PMM_AGENT_CACHE_DISABLE").BoolVar(&cfg.Cache.Disable) // no flag for SlowLogFilePrefix - it is only for development and testing app.Flag("ports-min", "Minimal allowed port number for listening sockets [PMM_AGENT_PORTS_MIN]"). diff --git a/agent/config/config_test.go b/agent/config/config_test.go index be2287829a..490f6c450f 100644 --- a/agent/config/config_test.go +++ b/agent/config/config_test.go @@ -127,11 +127,6 @@ func TestGet(t *testing.T) { Max: 51999, }, LogLinesCount: 1024, - Cache: Cache{ - Dir: "/usr/local/percona/pmm2/cache", - PrioritizedSize: 104857600, - UnprioritizedSize: 524288000, - }, } assert.Equal(t, expected, actual) assert.Empty(t, configFilepath) @@ -192,11 +187,6 @@ func TestGet(t *testing.T) { Max: 51999, }, LogLinesCount: 1024, - Cache: Cache{ - Dir: "/usr/local/percona/pmm2/cache", - PrioritizedSize: 104857600, - UnprioritizedSize: 524288000, - }, } assert.Equal(t, expected, actual) assert.Equal(t, name, configFilepath) @@ -258,11 +248,6 @@ func TestGet(t *testing.T) { LogLevel: "info", Debug: true, LogLinesCount: 1024, - Cache: Cache{ - Dir: "/usr/local/percona/pmm2/cache", - PrioritizedSize: 104857600, - UnprioritizedSize: 524288000, - }, } assert.Equal(t, expected, actual) assert.Equal(t, name, configFilepath) @@ -329,11 +314,6 @@ func TestGet(t *testing.T) { }, Debug: true, LogLinesCount: 1024, - Cache: Cache{ - Dir: "/usr/local/percona/pmm2/cache", - PrioritizedSize: 104857600, - UnprioritizedSize: 524288000, - }, } assert.Equal(t, expected, actual) assert.Equal(t, name, configFilepath) @@ -399,11 +379,6 @@ func TestGet(t *testing.T) { }, Debug: true, LogLinesCount: 1024, - Cache: Cache{ - Dir: "/base/cache", - PrioritizedSize: 104857600, - UnprioritizedSize: 524288000, - }, } assert.Equal(t, expected, actual) assert.Equal(t, name, configFilepath) @@ -467,11 +442,6 @@ func TestGet(t *testing.T) { }, Debug: true, LogLinesCount: 1024, - Cache: Cache{ - Dir: "/base/cache", - PrioritizedSize: 104857600, - UnprioritizedSize: 524288000, - }, } assert.Equal(t, expected, actual) assert.Equal(t, name, configFilepath) @@ -520,11 +490,6 @@ func TestGet(t *testing.T) { }, Debug: true, LogLinesCount: 1024, - Cache: Cache{ - Dir: "/usr/local/percona/pmm2/cache", - PrioritizedSize: 104857600, - UnprioritizedSize: 524288000, - }, } assert.Equal(t, expected, actual) assert.Equal(t, filepath.Join(wd, name), configFilepath) diff --git a/agent/models/agent_message.go b/agent/models/agent_message.go deleted file mode 100644 index a8f858c6e1..0000000000 --- a/agent/models/agent_message.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 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 models contains client domain models and helpers. -package models - -import ( - "google.golang.org/grpc/status" - - "github.com/percona/pmm/api/agentpb" -) - -// AgentRequest represents an request from agent. -// It is similar to agentpb.AgentMessage except it can contain only requests, -// and the payload is already unwrapped (XXX instead of AgentMessage_XXX). -type AgentRequest struct { - ID uint32 - Payload agentpb.AgentRequestPayload -} - -// AgentResponse represents agent's response. -// It is similar to agentpb.AgentMessage except it can contain only responses, -// and the payload is already unwrapped (XXX instead of AgentMessage_XXX). -type AgentResponse struct { - ID uint32 - Status *status.Status - Payload agentpb.AgentResponsePayload -} diff --git a/agent/models/cache.go b/agent/models/cache.go deleted file mode 100644 index fee575cdb0..0000000000 --- a/agent/models/cache.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 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 models contains client domain models and helpers. -package models - -import "github.com/percona/pmm/api/agentpb" - -// Sender is a subset of methods of channel, cache. -type Sender interface { - Send(resp *AgentResponse) error - SendAndWaitResponse(payload agentpb.AgentRequestPayload) (agentpb.ServerResponsePayload, error) -} - -// Cache represent cache methods. -type Cache interface { - Sender - Close() - SetSender(s Sender) -} diff --git a/agent/utils/buffer-ring/bigqueue/bigqueue.go b/agent/utils/buffer-ring/bigqueue/bigqueue.go deleted file mode 100644 index 8bc35b8472..0000000000 --- a/agent/utils/buffer-ring/bigqueue/bigqueue.go +++ /dev/null @@ -1,429 +0,0 @@ -// Copyright 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 bigqueue implements ring buffer based on bigqueue. -package bigqueue - -import ( - "fmt" - "math" - "os" - "path/filepath" - "sync" - "sync/atomic" - "time" - - "github.com/jhunters/bigqueue" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" - grpcstatus "google.golang.org/grpc/status" - "google.golang.org/protobuf/proto" - - "github.com/percona/pmm/agent/models" - agenterrors "github.com/percona/pmm/agent/utils/errors" - "github.com/percona/pmm/api/agentpb" -) - -const ( - metaFileSize = 16 + 8 // represent FrontFileInfo size + MetaFileInfo size - indexEntrySize = 32 // represent index entry size -) - -var ( - ErrClosed = errors.New("cache closed") - - dataPageSize = 1024 * 1024 // represent page size for data entries - indexPageSize = dataPageSize / indexEntrySize // represent page size for index entries (default bigqueue ratio) - drainThreshold = int64(1024 * 1024) // represent threshold for preliminary draining - gcDuration = 10 * time.Second // represent gc ticker duration -) - -// Ring represent ring buffer based on bigqueue. -type Ring struct { - l *logrus.Entry - fq *bigqueue.FileQueue - wg sync.WaitGroup - - sendLock sync.RWMutex - recvLock sync.RWMutex - totalSize int64 // represent the limit after which old data will be overwritten - - sender atomic.Pointer[models.Sender] - - gcCh chan struct{} - drainCh chan struct{} - recvNotifyCh chan struct{} - establishCh chan struct{} - done chan struct{} -} - -// New creates/loads ring buffer. -func New(dir string, size uint32, l *logrus.Entry) (*Ring, error) { - err := initPaths(dir) - if err != nil { - return nil, err - } - dir, queueName := filepath.Split(dir) - if lastRuneIdx := len(dir) - 1; len(dir) > 0 && rune(dir[lastRuneIdx]) == filepath.Separator { - dir = dir[:lastRuneIdx] - } - if metaSize := uint32(metaFileSize + indexPageSize + dataPageSize); metaSize > size { - return nil, fmt.Errorf("cache size must be greater than '%d' bytes to store at least one entry", metaSize) - } - fq := &bigqueue.FileQueue{} - if err = fq.Open(dir, queueName, &bigqueue.Options{ - DataPageSize: dataPageSize, - IndexItemsPerPage: int(math.Log2(float64(indexPageSize) / indexEntrySize)), - }); err != nil { - return nil, err - } - out := &Ring{ - l: l, - fq: fq, - totalSize: int64(size), - drainCh: make(chan struct{}, 1), - gcCh: make(chan struct{}, 1), - establishCh: make(chan struct{}, 1), - recvNotifyCh: make(chan struct{}, 1), - done: make(chan struct{}), - } - out.gcRunner() - out.sendRunner() - if !out.isEmpty() { - asyncNotify(out.recvNotifyCh) - } - return out, nil -} - -// Send stores agent responses in cache on nil channel. -func (r *Ring) Send(resp *models.AgentResponse) error { - msg := &agentpb.AgentMessage{Id: resp.ID} - if resp.Payload != nil { - msg.Payload = resp.Payload.AgentMessageResponsePayload() - } - if resp.Status != nil { - msg.Status = resp.Status.Proto() - } - - var ( - err error - s = r.sender.Load() - ) - - r.recvLock.Lock() - defer r.recvLock.Unlock() - if r.isEmpty() && s != nil { - err = (*s).Send(resp) - if err != nil && errors.As(err, &agenterrors.ErrChanConn) { - if r.sender.CompareAndSwap(s, nil) { - asyncRelease(r.establishCh) - r.l.Debugf("sender released: %v", err) - } - } else { - return err - } - } - - r.push(msg) - return nil -} - -// SendAndWaitResponse stores AgentMessageRequestPayload on nil channel. -func (r *Ring) SendAndWaitResponse(payload agentpb.AgentRequestPayload) (agentpb.ServerResponsePayload, error) { //nolint:unparam,ireturn - var ( - err error - resp agentpb.ServerResponsePayload - s = r.sender.Load() - ) - - r.recvLock.Lock() - defer r.recvLock.Unlock() - if r.isEmpty() && s != nil { - resp, err = (*s).SendAndWaitResponse(payload) - if err != nil && errors.As(err, &agenterrors.ErrChanConn) { - if r.sender.CompareAndSwap(s, nil) { - asyncRelease(r.establishCh) - r.l.Debugf("sender released: %v", err) - } - } else { - return resp, err - } - } - - r.push(&agentpb.AgentMessage{Payload: payload.AgentMessageRequestPayload()}) - return &agentpb.StateChangedResponse{}, nil -} - -// SetSender check and set sender and notify sender loop. -func (r *Ring) SetSender(s models.Sender) { - r.sender.Store(&s) - asyncNotify(r.establishCh) - r.l.Debug("sender set") -} - -// Close closes cache. -func (r *Ring) Close() { - select { - case <-r.done: - default: - close(r.done) - r.wg.Wait() - if err := r.fq.Close(); err != nil { - r.l.Errorf("closing cache: %+v", err) - } - r.l.Info("cache closed") - } -} - -func (r *Ring) isEmpty() bool { - return r.fq.IsEmpty() -} - -func (r *Ring) push(msg *agentpb.AgentMessage) { - b, err := proto.Marshal(msg) - if err != nil { - r.l.Errorf("marshal proto while inserting message to cache: %+v", err) - return - } - size := int64(len(b)) + indexEntrySize - if size > r.totalSize { - r.l.Errorf("data size: '%d' overflows free cache space: '%d'", size, r.totalSize) - return - } - select { - case <-r.done: - return - default: - } - _, err = r.fq.Enqueue(b) - if err != nil { - r.l.Errorf("inserting to cache: %+v", err) - } - asyncNotify(r.recvNotifyCh) -} - -func (r *Ring) gcRunner() { - r.wg.Add(1) - go func() { - defer r.wg.Done() - ticker := time.NewTicker(gcDuration) - defer ticker.Stop() - for { - select { - case <-r.done: - r.doDrain() - return - case <-r.drainCh: - r.doDrain() - case <-ticker.C: - r.doDrain() - case <-r.gcCh: - r.sendLock.Lock() - r.runGC() - r.sendLock.Unlock() - } - } - }() -} - -func (r *Ring) doDrain() { - if overflow := r.size() + drainThreshold - r.totalSize; overflow > 0 { - r.sendLock.Lock() - r.drain(overflow) - r.runGC() - r.sendLock.Unlock() - } -} - -func (r *Ring) sendRunner() { - r.wg.Add(1) - go func() { - defer r.wg.Done() - for { - select { - case <-r.done: - return - case <-r.recvNotifyCh: - r.sendInLoop() - } - } - }() -} - -func (r *Ring) sendInLoop() { - var s *models.Sender - for { - s = r.sender.Load() - if s != nil { - break - } - select { - case <-r.done: - return - case <-r.establishCh: - continue - } - } - r.sendLock.Lock() - defer r.sendLock.Unlock() - var count int - for { - select { - case <-r.done: - return - default: - } - r.recvLock.Lock() - _, b, err := r.fq.Peek() - r.recvLock.Unlock() - if err != nil { - r.l.Errorf("reading entry from cache: %+v", err) - } - if b == nil { - break - } - var m agentpb.AgentMessage - if err := proto.Unmarshal(b, &m); err != nil { - r.l.Errorf("unmarshal entry from cache: %+v", err) - } else if err = r.send(*s, &m); err != nil { - if r.sender.CompareAndSwap(s, nil) { - asyncRelease(r.establishCh) - r.l.Debugf("sender released: %v", err) - } - break - } - r.recvLock.Lock() - r.fq.Skip(1) //nolint:errcheck - r.recvLock.Unlock() - count++ - } - if count > 0 { - asyncNotify(r.gcCh) - } -} - -// initPaths creates all paths for queue to use. Original repo creates directories with perm error. -func initPaths(dir string) error { - for _, path := range []string{ - "", - bigqueue.IndexFileName, - bigqueue.DataFileName, - bigqueue.MetaFileName, - bigqueue.FrontFileName, - } { - if err := os.MkdirAll(filepath.Join(dir, path), os.ModePerm); err != nil { - return err - } - } - return nil -} - -func (r *Ring) drain(amount int64) { - for size := int64(0); size < amount; { - r.recvLock.Lock() - _, b, err := r.fq.Dequeue() - r.recvLock.Unlock() - if err != nil { - r.l.Errorf("draining cache: %+v", err) - return - } - if b == nil { - return - } - size += int64(len(b)) + indexEntrySize - } -} - -func (r *Ring) size() int64 { - r.recvLock.Lock() - status := r.fq.Status() - r.recvLock.Unlock() - sum := status.FrontFileInfo.Size + status.MetaFileInfo.Size - for _, list := range status.IndexFileList { - sum += list.Size - } - for _, list := range status.DataFileList { - sum += list.Size - } - return sum -} - -func (r *Ring) runGC() { - r.recvLock.Lock() - defer r.recvLock.Unlock() - if err := r.fq.Gc(); err != nil { - r.l.Errorf("run gc: %+v", err) - } -} - -func (r *Ring) send(s models.Sender, m *agentpb.AgentMessage) error { - var err error - switch p := m.Payload.(type) { - // responses - case *agentpb.AgentMessage_StartAction: - err = s.Send(&models.AgentResponse{ID: m.Id, Status: grpcstatus.FromProto(m.Status), Payload: p.StartAction}) - case *agentpb.AgentMessage_StopAction: - err = s.Send(&models.AgentResponse{ID: m.Id, Status: grpcstatus.FromProto(m.Status), Payload: p.StopAction}) - case *agentpb.AgentMessage_PbmSwitchPitr: - err = s.Send(&models.AgentResponse{ID: m.Id, Status: grpcstatus.FromProto(m.Status), Payload: p.PbmSwitchPitr}) - case *agentpb.AgentMessage_StartJob: - err = s.Send(&models.AgentResponse{ID: m.Id, Status: grpcstatus.FromProto(m.Status), Payload: p.StartJob}) - case *agentpb.AgentMessage_JobStatus: - err = s.Send(&models.AgentResponse{ID: m.Id, Status: grpcstatus.FromProto(m.Status), Payload: p.JobStatus}) - case *agentpb.AgentMessage_GetVersions: - err = s.Send(&models.AgentResponse{ID: m.Id, Status: grpcstatus.FromProto(m.Status), Payload: p.GetVersions}) - case *agentpb.AgentMessage_JobProgress: - err = s.Send(&models.AgentResponse{ID: m.Id, Status: grpcstatus.FromProto(m.Status), Payload: p.JobProgress}) - case *agentpb.AgentMessage_StopJob: - err = s.Send(&models.AgentResponse{ID: m.Id, Status: grpcstatus.FromProto(m.Status), Payload: p.StopJob}) - case *agentpb.AgentMessage_CheckConnection: - err = s.Send(&models.AgentResponse{ID: m.Id, Status: grpcstatus.FromProto(m.Status), Payload: p.CheckConnection}) - case *agentpb.AgentMessage_JobResult: - err = s.Send(&models.AgentResponse{ID: m.Id, Status: grpcstatus.FromProto(m.Status), Payload: p.JobResult}) - case *agentpb.AgentMessage_AgentLogs: - err = s.Send(&models.AgentResponse{ID: m.Id, Status: grpcstatus.FromProto(m.Status), Payload: p.AgentLogs}) - case *agentpb.AgentMessage_SetState: - err = s.Send(&models.AgentResponse{ID: m.Id, Status: grpcstatus.FromProto(m.Status), Payload: p.SetState}) - case *agentpb.AgentMessage_Pong: - err = s.Send(&models.AgentResponse{ID: m.Id, Status: grpcstatus.FromProto(m.Status), Payload: p.Pong}) - // requests - case *agentpb.AgentMessage_ActionResult: - _, err = s.SendAndWaitResponse(p.ActionResult) - case *agentpb.AgentMessage_QanCollect: - _, err = s.SendAndWaitResponse(p.QanCollect) - case *agentpb.AgentMessage_StateChanged: - _, err = s.SendAndWaitResponse(p.StateChanged) - default: - r.l.Errorf("unknown message: %T", m) - return nil - } - if err != nil && errors.As(err, &agenterrors.ErrChanConn) { - return err - } - return nil -} - -func asyncNotify(ch chan struct{}) { - select { - case ch <- struct{}{}: - default: - } -} - -func asyncRelease(ch chan struct{}) { - select { - case <-ch: - default: - } -} diff --git a/agent/utils/buffer-ring/bigqueue/bigqueue_test.go b/agent/utils/buffer-ring/bigqueue/bigqueue_test.go deleted file mode 100644 index b153ed818e..0000000000 --- a/agent/utils/buffer-ring/bigqueue/bigqueue_test.go +++ /dev/null @@ -1,266 +0,0 @@ -// Copyright 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 bigqueue - -import ( - "bytes" - "io/fs" - "math/rand" - "os" - "path/filepath" - "runtime" - "strings" - "sync/atomic" - "testing" - "time" - - "github.com/sirupsen/logrus" - "github.com/stretchr/testify/assert" - - "github.com/percona/pmm/agent/models" - "github.com/percona/pmm/api/agentpb" -) - -func TestMetaSizes(t *testing.T) { //nolint:tparallel - indexPageSize = 20 - t.Run("meta file size", func(t *testing.T) { - t.Parallel() - ring, err := New(filepath.Join(os.TempDir(), newRandomString(10)), uint32(dataPageSize+indexPageSize+metaFileSize), nil) - assert.NoError(t, err) - status := ring.fq.Status() - assert.Equal(t, int64(metaFileSize), status.FrontFileInfo.Size+status.MetaFileInfo.Size) - }) - t.Run("index entry size", func(t *testing.T) { - t.Parallel() - ring, err := New(filepath.Join(os.TempDir(), newRandomString(10)), uint32(dataPageSize+indexPageSize+metaFileSize), nil) - assert.NoError(t, err) - _, err = ring.fq.Enqueue([]byte("1")) - assert.NoError(t, err) - assert.Equal(t, int64(indexEntrySize), ring.fq.Status().IndexFileList[0].Size) - }) -} - -func TestNew(t *testing.T) { //nolint:tparallel - indexPageSize = 20 - t.Run("new with size less than meta", func(t *testing.T) { - t.Parallel() - _, err := New(filepath.Join(os.TempDir(), newRandomString(10)), uint32(dataPageSize+indexPageSize+metaFileSize)-1, nil) - assert.Error(t, err) - }) - t.Run("data size too big", func(t *testing.T) { - t.Parallel() - ring, log, cleanup := setupTest(t, filepath.Join(os.TempDir(), newRandomString(10)), uint32(dataPageSize+indexPageSize+metaFileSize)) - t.Cleanup(cleanup) - _, err := ring.SendAndWaitResponse(&agentpb.QANCollectRequest{MetricsBucket: []*agentpb.MetricsBucket{{ - Common: &agentpb.MetricsBucket_Common{Queryid: newRandomString(dataPageSize + indexPageSize + metaFileSize)}, - }}}) - assert.NoError(t, err) - assert.Equal(t, "level=error msg=\"data size: '1048668' overflows free cache space: '1048620'\" cache=test\n", log.String()) - }) -} - -type sender struct { - t *testing.T - i uint32 -} - -func (s *sender) Send(resp *models.AgentResponse) error { return nil } -func (s *sender) SendAndWaitResponse(payload agentpb.AgentRequestPayload) (agentpb.ServerResponsePayload, error) { - qan, ok := payload.(*agentpb.QANCollectRequest) - assert.Equal(s.t, true, ok) - assert.Equal(s.t, 1, len(qan.MetricsBucket)) - assert.Equal(s.t, atomic.LoadUint32(&s.i), qan.MetricsBucket[0].Common.PlaceholdersCount) - atomic.AddUint32(&s.i, 1) - return nil, nil -} - -func TestDrain(t *testing.T) { //nolint:tparallel - dataPageSize = indexEntrySize - indexPageSize = dataPageSize - payloadLen := indexEntrySize - 11 // queryId + proto = 32 - drainThreshold = 0 - - t.Run("push", func(t *testing.T) { - t.Parallel() - dir := filepath.Join(os.TempDir(), newRandomString(10)) - ring, log, cleanup := setupTest(t, dir, uint32(dataPageSize+indexPageSize)*3+metaFileSize) - t.Cleanup(cleanup) - - for i := uint32(1); i <= 4; i++ { - _, err := ring.SendAndWaitResponse(&agentpb.QANCollectRequest{MetricsBucket: []*agentpb.MetricsBucket{{ - Common: &agentpb.MetricsBucket_Common{PlaceholdersCount: i, Queryid: newRandomString(payloadLen)}, - }}}) - assert.NoError(t, err) - runtime.Gosched() - } - asyncNotify(ring.drainCh) - runtime.Gosched() - time.Sleep(1 * time.Second) - s := sender{ - i: uint32(2), // first must be drained - t: t, - } - ring.SetSender(&s) - time.Sleep(1 * time.Second) - assert.NotEqual(t, uint32(2), atomic.LoadUint32(&s.i)) - assert.Equal(t, -1, strings.LastIndex(log.String(), "level=error")) - }) - t.Run("shutdown", func(t *testing.T) { - t.Parallel() - dir := filepath.Join(os.TempDir(), newRandomString(10)) - ring, log, _ := setupTest(t, dir, uint32(dataPageSize+indexPageSize)*3+metaFileSize) - for i := uint32(1); i <= 4; i++ { - _, err := ring.SendAndWaitResponse(&agentpb.QANCollectRequest{MetricsBucket: []*agentpb.MetricsBucket{{ - Common: &agentpb.MetricsBucket_Common{PlaceholdersCount: i, Queryid: newRandomString(payloadLen)}, - }}}) - assert.NoError(t, err) - runtime.Gosched() - } - time.Sleep(1 * time.Second) - ring.Close() - assert.Equal(t, -1, strings.LastIndex(log.String(), "closing cache")) - - ring, log, cleanup := setupTest(t, dir, uint32(dataPageSize+indexPageSize)*3+metaFileSize) - t.Cleanup(cleanup) - s := sender{ - i: uint32(2), // first must be drained - t: t, - } - ring.SetSender(&s) - time.Sleep(1 * time.Second) - assert.NotEqual(t, uint32(2), atomic.LoadUint32(&s.i)) - assert.Equal(t, -1, strings.LastIndex(log.String(), "level=error")) - }) - t.Run("size", func(t *testing.T) { - t.Parallel() - dir := filepath.Join(os.TempDir(), newRandomString(10)) - ring, log, cleanup := setupTest(t, dir, uint32(dataPageSize+indexPageSize)*4+metaFileSize) - t.Cleanup(cleanup) - for i := uint32(1); i <= 5; i++ { - _, err := ring.SendAndWaitResponse(&agentpb.QANCollectRequest{MetricsBucket: []*agentpb.MetricsBucket{{ - Common: &agentpb.MetricsBucket_Common{PlaceholdersCount: i, Queryid: newRandomString(payloadLen)}, - }}}) - assert.NoError(t, err) - runtime.Gosched() - } - asyncNotify(ring.drainCh) - runtime.Gosched() - time.Sleep(1 * time.Second) - - // after push all messages - size, err := dirSize(dir) - assert.NoError(t, err) - assert.Equal(t, int64(344), size) - s := sender{ - i: uint32(2), // first must be drained - t: t, - } - ring.SetSender(&s) - time.Sleep(1 * time.Second) - assert.NotEqual(t, uint32(2), atomic.LoadUint32(&s.i)) - assert.Equal(t, -1, strings.LastIndex(log.String(), "level=error")) - - // after send - size, err = dirSize(dir) - assert.NoError(t, err) - assert.Equal(t, int64(indexPageSize+dataPageSize+metaFileSize), size) - }) -} - -func TestReadWrite(t *testing.T) { //nolint:tparallel - dataPageSize = indexEntrySize - indexPageSize = dataPageSize - payloadLen := indexEntrySize - 11 // queryId + proto = 32 - drainThreshold = 0 - - t.Run("async read write", func(t *testing.T) { - t.Parallel() - dir := filepath.Join(os.TempDir(), newRandomString(10)) - ring, log, cleanup := setupTest(t, dir, uint32(dataPageSize+indexPageSize)*10+metaFileSize) - t.Cleanup(cleanup) - - started := make(chan struct{}) - go func() { - close(started) - for i := uint32(1); i <= 10; i++ { - _, err := ring.SendAndWaitResponse(&agentpb.QANCollectRequest{MetricsBucket: []*agentpb.MetricsBucket{{ - Common: &agentpb.MetricsBucket_Common{PlaceholdersCount: i, Queryid: newRandomString(payloadLen)}, - }}}) - assert.NoError(t, err) - runtime.Gosched() - } - }() - <-started - s := sender{ - i: uint32(1), - t: t, - } - ring.SetSender(&s) - time.Sleep(1 * time.Second) - assert.NotEqual(t, uint32(1), atomic.LoadUint32(&s.i)) - assert.Equal(t, -1, strings.LastIndex(log.String(), "level=error")) - }) -} - -func newRandomString(length int) string { - r := rand.New(rand.NewSource(time.Now().UnixNano())) //nolint:gosec - const alp = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" - b := make([]byte, length) - for i := range b { - b[i] = alp[r.Intn(len(alp))] - } - return string(b) -} - -func setupTest(t *testing.T, dir string, size uint32) (*Ring, *bytes.Buffer, func()) { - t.Helper() - var buf bytes.Buffer - testLogger := logrus.Logger{ - Out: &buf, - Level: logrus.ErrorLevel, - Formatter: &logrus.TextFormatter{ - DisableColors: true, - DisableTimestamp: true, - DisableSorting: true, - }, - } - out, err := New(dir, size, testLogger.WithField("cache", "test")) - assert.NoError(t, err) - cleanup := func() { - out.Close() - assert.Equal(t, -1, strings.LastIndex(buf.String(), "closing cache")) - assert.NoError(t, os.RemoveAll(dir)) - } - return out, &buf, cleanup -} - -func dirSize(path string) (int64, error) { - var size int64 - err := filepath.WalkDir(path, func(_ string, d fs.DirEntry, err error) error { - if err != nil { - return err - } - if d.IsDir() { - return nil - } - fi, err := d.Info() - if err != nil { - return err - } - size += fi.Size() - return err - }) - return size, err -} diff --git a/agent/utils/errors/errors.go b/agent/utils/errors/errors.go index 95b750564f..55ca35f1f4 100644 --- a/agent/utils/errors/errors.go +++ b/agent/utils/errors/errors.go @@ -23,24 +23,4 @@ var ( // ErrActionQueueOverflow is returned when the agent is already running the maximum number of actions. ErrActionQueueOverflow = errors.New("action queue overflow") - // ErrChanConn is returned when the channel is closed. - ErrChanConn ChannelClosedError ) - -// NewChannelClosedError creates new channel connection closed error. -func NewChannelClosedError(err error) ChannelClosedError { - return ChannelClosedError{err} -} - -// ChannelClosedError is returned when the channel is closed. -type ChannelClosedError struct { - e error -} - -func (c ChannelClosedError) Error() string { - return c.e.Error() -} - -func (c ChannelClosedError) Unwrap() error { - return c.e -} diff --git a/go.mod b/go.mod index 7c0add89df..5c1df025c0 100644 --- a/go.mod +++ b/go.mod @@ -49,7 +49,6 @@ require ( github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 github.com/hashicorp/go-version v1.6.0 github.com/hashicorp/raft v1.5.0 - github.com/jhunters/bigqueue v1.2.7 github.com/jmoiron/sqlx v1.3.5 github.com/jotaen/kong-completion v0.0.5 github.com/lib/pq v1.10.9 diff --git a/go.sum b/go.sum index c4285512b7..fb147835c3 100644 --- a/go.sum +++ b/go.sum @@ -410,7 +410,6 @@ github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4= github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/grafana/grafana-api-golang-client v0.25.0 h1:jDxnR0U5xgIwKzE+IliZJvjMUUTQxGq+c1s+3M46flI= github.com/grafana/grafana-api-golang-client v0.25.0/go.mod h1:24W29gPe9yl0/3A9X624TPkAOR8DpHno490cPwnkv8E= @@ -465,8 +464,6 @@ github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80s github.com/jackc/pgx v3.6.2+incompatible h1:2zP5OD7kiyR3xzRYMhOcXVvkDZsImVXfj+yIyTQf3/o= github.com/jackc/pgx v3.6.2+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jhunters/bigqueue v1.2.7 h1:vwuQMWPBPxhnytZr0ydkzpZdQnnGd/WZmQsJSIJVGsw= -github.com/jhunters/bigqueue v1.2.7/go.mod h1:bHuCzOuSk3Q/Rc74d0pyF1PCPOiDDdr/Ugxu60awYpI= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= @@ -490,7 +487,6 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= @@ -767,11 +763,7 @@ github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrf github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= -github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= -github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= diff --git a/managed/services/checks/checks.go b/managed/services/checks/checks.go index c3e95f0302..f31312834f 100644 --- a/managed/services/checks/checks.go +++ b/managed/services/checks/checks.go @@ -1494,14 +1494,14 @@ func (s *Service) filterSupportedChecks(advisors []check.Advisor) []check.Adviso for _, c := range advisor.Checks { if c.Version > maxSupportedVersion { s.l.Warnf("Unsupported checks version: %d, max supported version: %d.", c.Version, maxSupportedVersion) - continue + continue LOOP } switch c.Version { case 1: if ok := isQueryTypeSupported(c.Type); !ok { s.l.Warnf("Unsupported check type: %s.", c.Type) - continue + continue LOOP } case 2: for _, query := range c.Queries { From 44c759d026e9a03030e2da08da09cf80086b0317 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Dec 2023 12:38:58 +0200 Subject: [PATCH 15/15] Bump github.com/brianvoe/gofakeit/v6 from 6.24.0 to 6.26.0 (#2678) Bumps [github.com/brianvoe/gofakeit/v6](https://github.com/brianvoe/gofakeit) from 6.24.0 to 6.26.0. - [Release notes](https://github.com/brianvoe/gofakeit/releases) - [Commits](https://github.com/brianvoe/gofakeit/compare/v6.24.0...v6.26.0) --- updated-dependencies: - dependency-name: github.com/brianvoe/gofakeit/v6 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d9842ce24b..add8324889 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 github.com/aws/aws-sdk-go v1.47.0 github.com/blang/semver v3.5.1+incompatible - github.com/brianvoe/gofakeit/v6 v6.24.0 + github.com/brianvoe/gofakeit/v6 v6.26.0 github.com/charmbracelet/bubbles v0.16.1 github.com/charmbracelet/bubbletea v0.24.1 github.com/charmbracelet/lipgloss v0.9.0 diff --git a/go.sum b/go.sum index c8be11d635..7b1fa2e126 100644 --- a/go.sum +++ b/go.sum @@ -141,8 +141,8 @@ github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/brianvoe/gofakeit v3.18.0+incompatible h1:wDOmHc9DLG4nRjUVVaxA+CEglKOW72Y5+4WNxUIkjM8= github.com/brianvoe/gofakeit v3.18.0+incompatible/go.mod h1:kfwdRA90vvNhPutZWfH7WPaDzUjz+CZFqG+rPkOjGOc= -github.com/brianvoe/gofakeit/v6 v6.24.0 h1:74yq7RRz/noddscZHRS2T84oHZisW9muwbb8sRnU52A= -github.com/brianvoe/gofakeit/v6 v6.24.0/go.mod h1:Ow6qC71xtwm79anlwKRlWZW6zVq9D2XHE4QSSMP/rU8= +github.com/brianvoe/gofakeit/v6 v6.26.0 h1:DzJHo4K6RrAbglU6cReh+XqoaunuUMZ8OAQGXrYsXt8= +github.com/brianvoe/gofakeit/v6 v6.26.0/go.mod h1:Xj58BMSnFqcn/fAQeSK+/PLtC5kSb7FJIq4JyGa8vEs= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=