From 182ea1e4da888083e79b8d1bd06c10b31bfed5bf Mon Sep 17 00:00:00 2001 From: Nurlan Moldomurov Date: Wed, 22 Nov 2023 13:08:13 +0300 Subject: [PATCH 01/14] PMM-12702 Fix SSLSni checker. (#2625) * PMM-12702 Fix SSLSni checker. * revert agentversion.go * PMM-12702 Fix linter. * PMM-12702 Fix linter and usage of As. --- managed/models/agentversion.go | 12 ++-- managed/models/agentversion_test.go | 96 +++++++++++++++++++++++++++-- 2 files changed, 97 insertions(+), 11 deletions(-) diff --git a/managed/models/agentversion.go b/managed/models/agentversion.go index 4723b91af7..272b19e4de 100644 --- a/managed/models/agentversion.go +++ b/managed/models/agentversion.go @@ -33,7 +33,7 @@ type AgentNotSupportedError struct { MinAgentVersion string } -func (e *AgentNotSupportedError) Error() string { +func (e AgentNotSupportedError) Error() string { return fmt.Sprintf("'%s' functionality is not supported by pmm-agent %q version %q. Required minimum version is %q", e.Functionality, e.AgentID, e.AgentVersion, e.MinAgentVersion) } @@ -44,11 +44,11 @@ func PMMAgentSupported(q *reform.Querier, pmmAgentID, functionalityPrefix string if err != nil { return errors.Errorf("failed to get PMM Agent: %s", err) } - return isAgentSupported(pmmAgent, functionalityPrefix, pmmMinVersion) + return IsAgentSupported(pmmAgent, functionalityPrefix, pmmMinVersion) } -// isAgentSupported contains logic for PMMAgentSupported. -func isAgentSupported(agentModel *Agent, functionalityPrefix string, pmmMinVersion *version.Version) error { +// IsAgentSupported contains logic for PMMAgentSupported. +func IsAgentSupported(agentModel *Agent, functionalityPrefix string, pmmMinVersion *version.Version) error { if agentModel == nil { return errors.New("nil agent") } @@ -61,7 +61,7 @@ func isAgentSupported(agentModel *Agent, functionalityPrefix string, pmmMinVersi } if pmmAgentVersion.LessThan(pmmMinVersion) { - return errors.WithStack(&AgentNotSupportedError{ + return errors.WithStack(AgentNotSupportedError{ AgentID: agentModel.AgentID, Functionality: functionalityPrefix, AgentVersion: *agentModel.Version, @@ -74,7 +74,7 @@ func isAgentSupported(agentModel *Agent, functionalityPrefix string, pmmMinVersi func IsPostgreSQLSSLSniSupported(q *reform.Querier, pmmAgentID string) (bool, error) { err := PMMAgentSupported(q, pmmAgentID, "postgresql SSL sni check", PMMAgentMinVersionForPostgreSQLSSLSni) switch { - case errors.Is(err, &AgentNotSupportedError{}): + case errors.As(err, &AgentNotSupportedError{}): return false, nil case err == nil: return true, nil diff --git a/managed/models/agentversion_test.go b/managed/models/agentversion_test.go index e47ac1716f..665c0f3e72 100644 --- a/managed/models/agentversion_test.go +++ b/managed/models/agentversion_test.go @@ -13,14 +13,21 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -package models +package models_test import ( "testing" + "time" "github.com/AlekSi/pointer" "github.com/hashicorp/go-version" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "gopkg.in/reform.v1" + "gopkg.in/reform.v1/dialects/postgresql" + + "github.com/percona/pmm/managed/models" + "github.com/percona/pmm/managed/utils/testdb" ) func TestPMMAgentSupported(t *testing.T) { @@ -64,11 +71,11 @@ func TestPMMAgentSupported(t *testing.T) { test := test t.Run(test.name, func(t *testing.T) { t.Parallel() - agentModel := Agent{ + agentModel := models.Agent{ AgentID: "Test agent ID", Version: pointer.ToString(test.agentVersion), } - err := isAgentSupported(&agentModel, prefix, minVersion) + err := models.IsAgentSupported(&agentModel, prefix, minVersion) if test.errString == "" { assert.NoError(t, err) } else { @@ -78,12 +85,91 @@ func TestPMMAgentSupported(t *testing.T) { } t.Run("No version info", func(t *testing.T) { - err := isAgentSupported(&Agent{AgentID: "Test agent ID"}, prefix, version.Must(version.NewVersion("2.30.0"))) + err := models.IsAgentSupported(&models.Agent{AgentID: "Test agent ID"}, prefix, version.Must(version.NewVersion("2.30.0"))) assert.Contains(t, err.Error(), "has no version info") }) t.Run("Nil agent", func(t *testing.T) { - err := isAgentSupported(nil, prefix, version.Must(version.NewVersion("2.30.0"))) + err := models.IsAgentSupported(nil, prefix, version.Must(version.NewVersion("2.30.0"))) assert.Contains(t, err.Error(), "nil agent") }) } + +func TestIsPostgreSQLSSLSniSupported(t *testing.T) { + now, origNowF := models.Now(), models.Now + models.Now = func() time.Time { + return now + } + sqlDB := testdb.Open(t, models.SetupFixtures, nil) + defer func() { + models.Now = origNowF + require.NoError(t, sqlDB.Close()) + }() + + setup := func(t *testing.T) (q *reform.Querier, teardown func(t *testing.T)) { + t.Helper() + db := reform.NewDB(sqlDB, postgresql.Dialect, reform.NewPrintfLogger(t.Logf)) + tx, err := db.Begin() + require.NoError(t, err) + q = tx.Querier + + for _, str := range []reform.Struct{ + &models.Node{ + NodeID: "N1", + NodeType: models.GenericNodeType, + NodeName: "Generic Node", + }, + + &models.Agent{ + AgentID: "New", + AgentType: models.PMMAgentType, + RunsOnNodeID: pointer.ToString("N1"), + Version: pointer.ToString("2.41.0"), + }, + + &models.Agent{ + AgentID: "Old", + AgentType: models.PMMAgentType, + RunsOnNodeID: pointer.ToString("N1"), + Version: pointer.ToString("2.40.1"), + }, + } { + require.NoError(t, q.Insert(str), "failed to INSERT %+v", str) + } + + teardown = func(t *testing.T) { + t.Helper() + require.NoError(t, tx.Rollback()) + } + return + } + q, teardown := setup(t) + defer teardown(t) + + tests := []struct { + pmmAgentID string + expected bool + }{ + { + "New", + true, + }, + { + "Old", + false, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.pmmAgentID, func(t *testing.T) { + actual, err := models.IsPostgreSQLSSLSniSupported(q, tt.pmmAgentID) + assert.Equal(t, tt.expected, actual) + assert.NoError(t, err) + }) + } + + t.Run("Non-existing ID", func(t *testing.T) { + _, err := models.IsPostgreSQLSSLSniSupported(q, "Not exist") + assert.Error(t, err) + }) +} From a067046aa9139ff2e1130c96cbfb3365904f4089 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Nov 2023 19:56:07 +0200 Subject: [PATCH 02/14] Bump golang.org/x/tools from 0.14.0 to 0.15.0 (#2611) Bumps [golang.org/x/tools](https://github.com/golang/tools) from 0.14.0 to 0.15.0. - [Release notes](https://github.com/golang/tools/releases) - [Commits](https://github.com/golang/tools/compare/v0.14.0...v0.15.0) --- updated-dependencies: - dependency-name: golang.org/x/tools 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 | 12 ++++++------ go.sum | 20 ++++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/go.mod b/go.mod index 3af5a6d5c5..6f0198c448 100644 --- a/go.mod +++ b/go.mod @@ -75,11 +75,11 @@ require ( github.com/stretchr/testify v1.8.4 go.mongodb.org/mongo-driver v1.12.0 go.starlark.net v0.0.0-20230717150657-8a3343210976 - golang.org/x/crypto v0.14.0 - golang.org/x/sync v0.4.0 + golang.org/x/crypto v0.15.0 + golang.org/x/sync v0.5.0 golang.org/x/sys v0.14.0 golang.org/x/text v0.14.0 - golang.org/x/tools v0.14.0 + golang.org/x/tools v0.15.0 google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d google.golang.org/grpc v1.59.0 @@ -243,10 +243,10 @@ require ( github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect go.opentelemetry.io/otel v1.19.0 // indirect go.opentelemetry.io/otel/trace v1.19.0 // indirect - golang.org/x/mod v0.13.0 // indirect - golang.org/x/net v0.17.0 // indirect + golang.org/x/mod v0.14.0 // indirect + golang.org/x/net v0.18.0 // indirect golang.org/x/oauth2 v0.12.0 // indirect - golang.org/x/term v0.13.0 // indirect + golang.org/x/term v0.14.0 // indirect google.golang.org/appengine v1.6.7 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gotest.tools/v3 v3.3.0 // indirect diff --git a/go.sum b/go.sum index a32ef7f9ce..bcacdc145b 100644 --- a/go.sum +++ b/go.sum @@ -873,8 +873,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= -golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -913,8 +913,8 @@ golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= +golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -936,8 +936,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1001,8 +1001,8 @@ golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8= +golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1076,8 +1076,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= -golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= +golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= +golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 5dd1d3ce53bcbb79aeafa23df0819f249b9b471f Mon Sep 17 00:00:00 2001 From: Artem Gavrilov Date: Thu, 23 Nov 2023 13:01:22 +0200 Subject: [PATCH 03/14] PMM-12460 PMM Dump integration (#2493) * PMM-12460 PMM Dump API * PMM-12460 Add created_at to the dump API model * PMM-12460 Add dump model, implement ListDumps method * PMM-12460 Fix * PMM-12460 Allow batch dupm deletes * PMM-12460 Dupm integration draft * PMM-12460 Draft * PMM-12460 Fix bad merge * PMM-12460 Fix dump * PMM-12460 Use API tokens for pmm-dump authorization * PMM-12460 Linter fixes * PMM-12460 Fix dump logs processing * PMM-12460 Use API keys expiration * PMM-12460 Update dupm statuses, fix dates hadnling * PMM-12460 Try to fix pmm-dump build * PMM-12460 Debug * PMM-12460 Debug * PMM-12460 Different fixes * PMM-12460 Use service names instead of node ids * PMM-12460 Handle service names * PMM-12460 Implement dump deletion * PMM-12460 Fix * PMM-12460 Add dump upload API * PMM-12460 Refactoring * PMM-12460 Refactorinb * PMM-12460 Implement FTP upload * PMM-12460 Fix pmm-dump authentication * PMM-12460 Fix migrations * PMM-12460 Fixes and improvements * PMM-12460 Revert API key creation changes * PMM-12460 Fix * PMM-12460 Fixed * PMM-12460 Revert debug info * PMM-12460 Fix tests * PMM-12460 Fixes * PMM-12460 Fix tests * PMM-12460 Fix linter * PMM-12460 Use SFTP instead FTP * PMM-12460 Remove dedug code * PMM-12460 Lint fixes * PMM-12460 Log error * PMM-12460 Add dump metrics * PMM-12460 Fix * PMM-12460 Add pmm dumps telemetry * PMM-12460 Expose directory with pmm-dupm artifacts via nginx * PMM-12460 Fix dumps telemetry * PMM-12460 Fix comments * PMM-12460 Tune nginx config * PMM-12460 Make start and end time optional (nullable) * PMM-12460 Polishing * PMM-12460 Don't return filepath for dupms that are not in success state * PMM-12460 Remove redundant field * PMM-12460 Remove dump metrics * PMM-12460 Refactoring * PMM-12460 Fix * PMM-12460 Add API validation rules * PMM-12460 Configure ssh key exchange algos * PMM-12460 Update pmm-dump --------- Co-authored-by: michael.okoko Co-authored-by: Yash Sartanpara <119680679+YashSartanpara1@users.noreply.github.com> --- Makefile.include | 5 +- api/managementpb/dump/dump.pb.go | 1200 ++++++++++++ api/managementpb/dump/dump.pb.gw.go | 491 +++++ api/managementpb/dump/dump.pb.validate.go | 1728 +++++++++++++++++ api/managementpb/dump/dump.proto | 127 ++ api/managementpb/dump/dump_grpc.pb.go | 271 +++ .../client/dumps/delete_dump_parameters.go | 144 ++ .../client/dumps/delete_dump_responses.go | 295 +++ .../dump/json/client/dumps/dumps_client.go | 232 +++ .../client/dumps/get_dump_logs_parameters.go | 144 ++ .../client/dumps/get_dump_logs_responses.go | 444 +++++ .../client/dumps/list_dumps_parameters.go | 146 ++ .../json/client/dumps/list_dumps_responses.go | 521 +++++ .../client/dumps/start_dump_parameters.go | 144 ++ .../json/client/dumps/start_dump_responses.go | 386 ++++ .../client/dumps/upload_dump_parameters.go | 144 ++ .../client/dumps/upload_dump_responses.go | 396 ++++ .../dump/json/client/pmm_dump_api_client.go | 112 ++ api/managementpb/dump/json/dump.json | 476 +++++ api/managementpb/dump/json/header.json | 11 + api/swagger/swagger-dev.json | 463 ++++- build/packages/rpm/server/SPECS/pmm-dump.spec | 9 +- descriptor.bin | Bin 781289 -> 788061 bytes go.mod | 4 +- go.sum | 22 +- managed/cmd/pmm-managed/main.go | 13 +- managed/models/database.go | 24 + managed/models/dump.go | 104 + managed/models/dump_helpers.go | 247 +++ managed/models/dump_helpers_test.go | 280 +++ managed/models/dump_reform.go | 262 +++ managed/services/dump/dump.go | 299 +++ managed/services/management/dump/deps.go | 26 + managed/services/management/dump/dump.go | 308 +++ .../management/dump/mock_dump_service_test.go | 93 + managed/services/telemetry/config.default.yml | 46 +- .../tasks/roles/nginx/files/conf.d/pmm.conf | 5 + 37 files changed, 9601 insertions(+), 21 deletions(-) create mode 100644 api/managementpb/dump/dump.pb.go create mode 100644 api/managementpb/dump/dump.pb.gw.go create mode 100644 api/managementpb/dump/dump.pb.validate.go create mode 100644 api/managementpb/dump/dump.proto create mode 100644 api/managementpb/dump/dump_grpc.pb.go create mode 100644 api/managementpb/dump/json/client/dumps/delete_dump_parameters.go create mode 100644 api/managementpb/dump/json/client/dumps/delete_dump_responses.go create mode 100644 api/managementpb/dump/json/client/dumps/dumps_client.go create mode 100644 api/managementpb/dump/json/client/dumps/get_dump_logs_parameters.go create mode 100644 api/managementpb/dump/json/client/dumps/get_dump_logs_responses.go create mode 100644 api/managementpb/dump/json/client/dumps/list_dumps_parameters.go create mode 100644 api/managementpb/dump/json/client/dumps/list_dumps_responses.go create mode 100644 api/managementpb/dump/json/client/dumps/start_dump_parameters.go create mode 100644 api/managementpb/dump/json/client/dumps/start_dump_responses.go create mode 100644 api/managementpb/dump/json/client/dumps/upload_dump_parameters.go create mode 100644 api/managementpb/dump/json/client/dumps/upload_dump_responses.go create mode 100644 api/managementpb/dump/json/client/pmm_dump_api_client.go create mode 100644 api/managementpb/dump/json/dump.json create mode 100644 api/managementpb/dump/json/header.json create mode 100644 managed/models/dump.go create mode 100644 managed/models/dump_helpers.go create mode 100644 managed/models/dump_helpers_test.go create mode 100644 managed/models/dump_reform.go create mode 100644 managed/services/dump/dump.go create mode 100644 managed/services/management/dump/deps.go create mode 100644 managed/services/management/dump/dump.go create mode 100644 managed/services/management/dump/mock_dump_service_test.go diff --git a/Makefile.include b/Makefile.include index 7c59505c85..c383de9a2c 100644 --- a/Makefile.include +++ b/Makefile.include @@ -47,7 +47,7 @@ gen-api: ## Generate PMM API bin/buf generate -v api - for API in api/agentlocalpb api/serverpb api/inventorypb api/managementpb api/managementpb/dbaas api/managementpb/ia api/managementpb/alerting api/managementpb/backup api/managementpb/azure api/managementpb/role api/qanpb api/managementpb/agent api/managementpb/node api/managementpb/service api/platformpb api/userpb; do \ + for API in api/agentlocalpb api/serverpb api/inventorypb api/managementpb api/managementpb/dbaas api/managementpb/ia api/managementpb/alerting api/managementpb/backup api/managementpb/dump api/managementpb/azure api/managementpb/role api/qanpb api/managementpb/agent api/managementpb/node api/managementpb/service api/platformpb api/userpb; do \ set -x ; \ bin/swagger mixin $$API/json/header.json $$API/*.swagger.json --output=$$API/json/$$(basename $$API).json --keep-spec-order; \ bin/swagger flatten --with-flatten=expand --with-flatten=remove-unused $$API/json/$$(basename $$API).json --output=$$API/json/$$(basename $$API).json ; \ @@ -91,6 +91,7 @@ gen-api: ## Generate PMM API api/managementpb/ia/json/ia.json \ api/managementpb/alerting/json/alerting.json \ api/managementpb/backup/json/backup.json \ + api/managementpb/dump/json/dump.json \ api/managementpb/azure/json/azure.json \ api/managementpb/role/json/role.json \ api/managementpb/agent/json/agent.json \ @@ -127,7 +128,7 @@ clean: clean_swagger ## Remove generated files find api -name '*.pb.gw.go' -print -delete find api -name '*.validate.go' -print -delete - for API in api/agentlocalpb api/serverpb api/inventorypb api/managementpb api/managementpb/dbaas api/managementpb/ia api/managementpb/alerting api/managementpb/backup api/managementpb/role api/managementpb/agent api/managementpb/node api/managementpb/service api/qanpb api/platformpb ; do \ + for API in api/agentlocalpb api/serverpb api/inventorypb api/managementpb api/managementpb/dbaas api/managementpb/ia api/managementpb/alerting api/managementpb/backup api/management/dump api/managementpb/role api/managementpb/agent api/managementpb/node api/managementpb/service api/qanpb api/platformpb ; do \ rm -fr $$API/json/client $$API/json/models $$API/json/$$(basename $$API).json ; \ done rm -f api/swagger/swagger.json api/swagger/swagger-dev.json diff --git a/api/managementpb/dump/dump.pb.go b/api/managementpb/dump/dump.pb.go new file mode 100644 index 0000000000..19e895a780 --- /dev/null +++ b/api/managementpb/dump/dump.pb.go @@ -0,0 +1,1200 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0-devel +// protoc (unknown) +// source: managementpb/dump/dump.proto + +package dumpv1beta1 + +import ( + reflect "reflect" + sync "sync" + + _ "github.com/envoyproxy/protoc-gen-validate/validate" + _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" + _ "google.golang.org/genproto/googleapis/api/annotations" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type DumpStatus int32 + +const ( + DumpStatus_DUMP_STATUS_INVALID DumpStatus = 0 + DumpStatus_DUMP_STATUS_IN_PROGRESS DumpStatus = 1 + DumpStatus_DUMP_STATUS_SUCCESS DumpStatus = 2 + DumpStatus_DUMP_STATUS_ERROR DumpStatus = 3 +) + +// Enum value maps for DumpStatus. +var ( + DumpStatus_name = map[int32]string{ + 0: "DUMP_STATUS_INVALID", + 1: "DUMP_STATUS_IN_PROGRESS", + 2: "DUMP_STATUS_SUCCESS", + 3: "DUMP_STATUS_ERROR", + } + DumpStatus_value = map[string]int32{ + "DUMP_STATUS_INVALID": 0, + "DUMP_STATUS_IN_PROGRESS": 1, + "DUMP_STATUS_SUCCESS": 2, + "DUMP_STATUS_ERROR": 3, + } +) + +func (x DumpStatus) Enum() *DumpStatus { + p := new(DumpStatus) + *p = x + return p +} + +func (x DumpStatus) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (DumpStatus) Descriptor() protoreflect.EnumDescriptor { + return file_managementpb_dump_dump_proto_enumTypes[0].Descriptor() +} + +func (DumpStatus) Type() protoreflect.EnumType { + return &file_managementpb_dump_dump_proto_enumTypes[0] +} + +func (x DumpStatus) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use DumpStatus.Descriptor instead. +func (DumpStatus) EnumDescriptor() ([]byte, []int) { + return file_managementpb_dump_dump_proto_rawDescGZIP(), []int{0} +} + +type Dump struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + DumpId string `protobuf:"bytes,1,opt,name=dump_id,json=dumpId,proto3" json:"dump_id,omitempty"` + Status DumpStatus `protobuf:"varint,2,opt,name=status,proto3,enum=dump.v1beta1.DumpStatus" json:"status,omitempty"` + ServiceNames []string `protobuf:"bytes,3,rep,name=service_names,json=serviceNames,proto3" json:"service_names,omitempty"` + StartTime *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` + EndTime *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` +} + +func (x *Dump) Reset() { + *x = Dump{} + if protoimpl.UnsafeEnabled { + mi := &file_managementpb_dump_dump_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Dump) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Dump) ProtoMessage() {} + +func (x *Dump) ProtoReflect() protoreflect.Message { + mi := &file_managementpb_dump_dump_proto_msgTypes[0] + 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 Dump.ProtoReflect.Descriptor instead. +func (*Dump) Descriptor() ([]byte, []int) { + return file_managementpb_dump_dump_proto_rawDescGZIP(), []int{0} +} + +func (x *Dump) GetDumpId() string { + if x != nil { + return x.DumpId + } + return "" +} + +func (x *Dump) GetStatus() DumpStatus { + if x != nil { + return x.Status + } + return DumpStatus_DUMP_STATUS_INVALID +} + +func (x *Dump) GetServiceNames() []string { + if x != nil { + return x.ServiceNames + } + return nil +} + +func (x *Dump) GetStartTime() *timestamppb.Timestamp { + if x != nil { + return x.StartTime + } + return nil +} + +func (x *Dump) GetEndTime() *timestamppb.Timestamp { + if x != nil { + return x.EndTime + } + return nil +} + +func (x *Dump) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +type StartDumpRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ServiceNames []string `protobuf:"bytes,1,rep,name=service_names,json=serviceNames,proto3" json:"service_names,omitempty"` + StartTime *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` + EndTime *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty"` + ExportQan bool `protobuf:"varint,4,opt,name=export_qan,json=exportQan,proto3" json:"export_qan,omitempty"` + IgnoreLoad bool `protobuf:"varint,5,opt,name=ignore_load,json=ignoreLoad,proto3" json:"ignore_load,omitempty"` +} + +func (x *StartDumpRequest) Reset() { + *x = StartDumpRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_managementpb_dump_dump_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StartDumpRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StartDumpRequest) ProtoMessage() {} + +func (x *StartDumpRequest) ProtoReflect() protoreflect.Message { + mi := &file_managementpb_dump_dump_proto_msgTypes[1] + 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 StartDumpRequest.ProtoReflect.Descriptor instead. +func (*StartDumpRequest) Descriptor() ([]byte, []int) { + return file_managementpb_dump_dump_proto_rawDescGZIP(), []int{1} +} + +func (x *StartDumpRequest) GetServiceNames() []string { + if x != nil { + return x.ServiceNames + } + return nil +} + +func (x *StartDumpRequest) GetStartTime() *timestamppb.Timestamp { + if x != nil { + return x.StartTime + } + return nil +} + +func (x *StartDumpRequest) GetEndTime() *timestamppb.Timestamp { + if x != nil { + return x.EndTime + } + return nil +} + +func (x *StartDumpRequest) GetExportQan() bool { + if x != nil { + return x.ExportQan + } + return false +} + +func (x *StartDumpRequest) GetIgnoreLoad() bool { + if x != nil { + return x.IgnoreLoad + } + return false +} + +type StartDumpResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + DumpId string `protobuf:"bytes,1,opt,name=dump_id,json=dumpId,proto3" json:"dump_id,omitempty"` +} + +func (x *StartDumpResponse) Reset() { + *x = StartDumpResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_managementpb_dump_dump_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StartDumpResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StartDumpResponse) ProtoMessage() {} + +func (x *StartDumpResponse) ProtoReflect() protoreflect.Message { + mi := &file_managementpb_dump_dump_proto_msgTypes[2] + 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 StartDumpResponse.ProtoReflect.Descriptor instead. +func (*StartDumpResponse) Descriptor() ([]byte, []int) { + return file_managementpb_dump_dump_proto_rawDescGZIP(), []int{2} +} + +func (x *StartDumpResponse) GetDumpId() string { + if x != nil { + return x.DumpId + } + return "" +} + +type ListDumpsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ListDumpsRequest) Reset() { + *x = ListDumpsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_managementpb_dump_dump_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListDumpsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListDumpsRequest) ProtoMessage() {} + +func (x *ListDumpsRequest) ProtoReflect() protoreflect.Message { + mi := &file_managementpb_dump_dump_proto_msgTypes[3] + 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 ListDumpsRequest.ProtoReflect.Descriptor instead. +func (*ListDumpsRequest) Descriptor() ([]byte, []int) { + return file_managementpb_dump_dump_proto_rawDescGZIP(), []int{3} +} + +type ListDumpsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Dumps []*Dump `protobuf:"bytes,1,rep,name=dumps,proto3" json:"dumps,omitempty"` +} + +func (x *ListDumpsResponse) Reset() { + *x = ListDumpsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_managementpb_dump_dump_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListDumpsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListDumpsResponse) ProtoMessage() {} + +func (x *ListDumpsResponse) ProtoReflect() protoreflect.Message { + mi := &file_managementpb_dump_dump_proto_msgTypes[4] + 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 ListDumpsResponse.ProtoReflect.Descriptor instead. +func (*ListDumpsResponse) Descriptor() ([]byte, []int) { + return file_managementpb_dump_dump_proto_rawDescGZIP(), []int{4} +} + +func (x *ListDumpsResponse) GetDumps() []*Dump { + if x != nil { + return x.Dumps + } + return nil +} + +type DeleteDumpRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + DumpIds []string `protobuf:"bytes,1,rep,name=dump_ids,json=dumpIds,proto3" json:"dump_ids,omitempty"` +} + +func (x *DeleteDumpRequest) Reset() { + *x = DeleteDumpRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_managementpb_dump_dump_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteDumpRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteDumpRequest) ProtoMessage() {} + +func (x *DeleteDumpRequest) ProtoReflect() protoreflect.Message { + mi := &file_managementpb_dump_dump_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 DeleteDumpRequest.ProtoReflect.Descriptor instead. +func (*DeleteDumpRequest) Descriptor() ([]byte, []int) { + return file_managementpb_dump_dump_proto_rawDescGZIP(), []int{5} +} + +func (x *DeleteDumpRequest) GetDumpIds() []string { + if x != nil { + return x.DumpIds + } + return nil +} + +type DeleteDumpResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *DeleteDumpResponse) Reset() { + *x = DeleteDumpResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_managementpb_dump_dump_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteDumpResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteDumpResponse) ProtoMessage() {} + +func (x *DeleteDumpResponse) ProtoReflect() protoreflect.Message { + mi := &file_managementpb_dump_dump_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 DeleteDumpResponse.ProtoReflect.Descriptor instead. +func (*DeleteDumpResponse) Descriptor() ([]byte, []int) { + return file_managementpb_dump_dump_proto_rawDescGZIP(), []int{6} +} + +type GetLogsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + DumpId string `protobuf:"bytes,1,opt,name=dump_id,json=dumpId,proto3" json:"dump_id,omitempty"` + Offset uint32 `protobuf:"varint,2,opt,name=offset,proto3" json:"offset,omitempty"` + Limit uint32 `protobuf:"varint,3,opt,name=limit,proto3" json:"limit,omitempty"` +} + +func (x *GetLogsRequest) Reset() { + *x = GetLogsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_managementpb_dump_dump_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetLogsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetLogsRequest) ProtoMessage() {} + +func (x *GetLogsRequest) ProtoReflect() protoreflect.Message { + mi := &file_managementpb_dump_dump_proto_msgTypes[7] + 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 GetLogsRequest.ProtoReflect.Descriptor instead. +func (*GetLogsRequest) Descriptor() ([]byte, []int) { + return file_managementpb_dump_dump_proto_rawDescGZIP(), []int{7} +} + +func (x *GetLogsRequest) GetDumpId() string { + if x != nil { + return x.DumpId + } + return "" +} + +func (x *GetLogsRequest) GetOffset() uint32 { + if x != nil { + return x.Offset + } + return 0 +} + +func (x *GetLogsRequest) GetLimit() uint32 { + if x != nil { + return x.Limit + } + return 0 +} + +type GetLogsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Logs []*LogChunk `protobuf:"bytes,1,rep,name=logs,proto3" json:"logs,omitempty"` + End bool `protobuf:"varint,2,opt,name=end,proto3" json:"end,omitempty"` +} + +func (x *GetLogsResponse) Reset() { + *x = GetLogsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_managementpb_dump_dump_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetLogsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetLogsResponse) ProtoMessage() {} + +func (x *GetLogsResponse) ProtoReflect() protoreflect.Message { + mi := &file_managementpb_dump_dump_proto_msgTypes[8] + 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 GetLogsResponse.ProtoReflect.Descriptor instead. +func (*GetLogsResponse) Descriptor() ([]byte, []int) { + return file_managementpb_dump_dump_proto_rawDescGZIP(), []int{8} +} + +func (x *GetLogsResponse) GetLogs() []*LogChunk { + if x != nil { + return x.Logs + } + return nil +} + +func (x *GetLogsResponse) GetEnd() bool { + if x != nil { + return x.End + } + return false +} + +// LogChunk represent one chunk of logs. +type LogChunk struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ChunkId uint32 `protobuf:"varint,1,opt,name=chunk_id,json=chunkId,proto3" json:"chunk_id,omitempty"` + Data string `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` +} + +func (x *LogChunk) Reset() { + *x = LogChunk{} + if protoimpl.UnsafeEnabled { + mi := &file_managementpb_dump_dump_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LogChunk) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LogChunk) ProtoMessage() {} + +func (x *LogChunk) ProtoReflect() protoreflect.Message { + mi := &file_managementpb_dump_dump_proto_msgTypes[9] + 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 LogChunk.ProtoReflect.Descriptor instead. +func (*LogChunk) Descriptor() ([]byte, []int) { + return file_managementpb_dump_dump_proto_rawDescGZIP(), []int{9} +} + +func (x *LogChunk) GetChunkId() uint32 { + if x != nil { + return x.ChunkId + } + return 0 +} + +func (x *LogChunk) GetData() string { + if x != nil { + return x.Data + } + return "" +} + +type SFTPParameters struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + User string `protobuf:"bytes,2,opt,name=user,proto3" json:"user,omitempty"` + Password string `protobuf:"bytes,3,opt,name=password,proto3" json:"password,omitempty"` + Directory string `protobuf:"bytes,4,opt,name=directory,proto3" json:"directory,omitempty"` +} + +func (x *SFTPParameters) Reset() { + *x = SFTPParameters{} + if protoimpl.UnsafeEnabled { + mi := &file_managementpb_dump_dump_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SFTPParameters) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SFTPParameters) ProtoMessage() {} + +func (x *SFTPParameters) ProtoReflect() protoreflect.Message { + mi := &file_managementpb_dump_dump_proto_msgTypes[10] + 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 SFTPParameters.ProtoReflect.Descriptor instead. +func (*SFTPParameters) Descriptor() ([]byte, []int) { + return file_managementpb_dump_dump_proto_rawDescGZIP(), []int{10} +} + +func (x *SFTPParameters) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +func (x *SFTPParameters) GetUser() string { + if x != nil { + return x.User + } + return "" +} + +func (x *SFTPParameters) GetPassword() string { + if x != nil { + return x.Password + } + return "" +} + +func (x *SFTPParameters) GetDirectory() string { + if x != nil { + return x.Directory + } + return "" +} + +type UploadDumpRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + DumpIds []string `protobuf:"bytes,1,rep,name=dump_ids,json=dumpIds,proto3" json:"dump_ids,omitempty"` + // SFTP upload parameters. + SftpParameters *SFTPParameters `protobuf:"bytes,2,opt,name=sftp_parameters,json=sftpParameters,proto3" json:"sftp_parameters,omitempty"` +} + +func (x *UploadDumpRequest) Reset() { + *x = UploadDumpRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_managementpb_dump_dump_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UploadDumpRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UploadDumpRequest) ProtoMessage() {} + +func (x *UploadDumpRequest) ProtoReflect() protoreflect.Message { + mi := &file_managementpb_dump_dump_proto_msgTypes[11] + 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 UploadDumpRequest.ProtoReflect.Descriptor instead. +func (*UploadDumpRequest) Descriptor() ([]byte, []int) { + return file_managementpb_dump_dump_proto_rawDescGZIP(), []int{11} +} + +func (x *UploadDumpRequest) GetDumpIds() []string { + if x != nil { + return x.DumpIds + } + return nil +} + +func (x *UploadDumpRequest) GetSftpParameters() *SFTPParameters { + if x != nil { + return x.SftpParameters + } + return nil +} + +type UploadDumpResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *UploadDumpResponse) Reset() { + *x = UploadDumpResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_managementpb_dump_dump_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UploadDumpResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UploadDumpResponse) ProtoMessage() {} + +func (x *UploadDumpResponse) ProtoReflect() protoreflect.Message { + mi := &file_managementpb_dump_dump_proto_msgTypes[12] + 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 UploadDumpResponse.ProtoReflect.Descriptor instead. +func (*UploadDumpResponse) Descriptor() ([]byte, []int) { + return file_managementpb_dump_dump_proto_rawDescGZIP(), []int{12} +} + +var File_managementpb_dump_dump_proto protoreflect.FileDescriptor + +var file_managementpb_dump_dump_proto_rawDesc = []byte{ + 0x0a, 0x1c, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2f, 0x64, + 0x75, 0x6d, 0x70, 0x2f, 0x64, 0x75, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, + 0x64, 0x75, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x1a, 0x1c, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, + 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x76, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa3, 0x02, 0x0a, 0x04, 0x44, 0x75, 0x6d, 0x70, 0x12, 0x17, 0x0a, + 0x07, 0x64, 0x75, 0x6d, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x64, 0x75, 0x6d, 0x70, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x64, 0x75, 0x6d, 0x70, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x75, 0x6d, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x39, 0x0a, + 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 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, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 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, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, + 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x07, 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, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0xe9, 0x01, 0x0a, 0x10, 0x53, + 0x74, 0x61, 0x72, 0x74, 0x44, 0x75, 0x6d, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x23, 0x0a, 0x0d, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, + 0x61, 0x6d, 0x65, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x18, 0x02, 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, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, + 0x35, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 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, 0x07, 0x65, + 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, + 0x5f, 0x71, 0x61, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x65, 0x78, 0x70, 0x6f, + 0x72, 0x74, 0x51, 0x61, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x5f, + 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x67, 0x6e, 0x6f, + 0x72, 0x65, 0x4c, 0x6f, 0x61, 0x64, 0x22, 0x2c, 0x0a, 0x11, 0x53, 0x74, 0x61, 0x72, 0x74, 0x44, + 0x75, 0x6d, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x64, + 0x75, 0x6d, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x75, + 0x6d, 0x70, 0x49, 0x64, 0x22, 0x12, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x75, 0x6d, 0x70, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x3d, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, + 0x44, 0x75, 0x6d, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, + 0x05, 0x64, 0x75, 0x6d, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x64, + 0x75, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x75, 0x6d, 0x70, + 0x52, 0x05, 0x64, 0x75, 0x6d, 0x70, 0x73, 0x22, 0x3a, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x44, 0x75, 0x6d, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x08, + 0x64, 0x75, 0x6d, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x42, 0x0a, + 0xfa, 0x42, 0x07, 0x92, 0x01, 0x04, 0x08, 0x01, 0x18, 0x01, 0x52, 0x07, 0x64, 0x75, 0x6d, 0x70, + 0x49, 0x64, 0x73, 0x22, 0x14, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x75, 0x6d, + 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x60, 0x0a, 0x0e, 0x47, 0x65, 0x74, + 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x07, 0x64, + 0x75, 0x6d, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, + 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x06, 0x64, 0x75, 0x6d, 0x70, 0x49, 0x64, 0x12, 0x16, 0x0a, + 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x6f, + 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x22, 0x4f, 0x0a, 0x0f, 0x47, + 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, + 0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x64, + 0x75, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x43, + 0x68, 0x75, 0x6e, 0x6b, 0x52, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x22, 0x39, 0x0a, 0x08, + 0x4c, 0x6f, 0x67, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x75, 0x6e, + 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x63, 0x68, 0x75, 0x6e, + 0x6b, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x93, 0x01, 0x0a, 0x0e, 0x53, 0x46, 0x54, 0x50, + 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x21, 0x0a, 0x07, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, + 0x72, 0x02, 0x10, 0x01, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, + 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, + 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x23, 0x0a, 0x08, 0x70, 0x61, + 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, + 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, + 0x1c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x22, 0x8b, 0x01, + 0x0a, 0x11, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x44, 0x75, 0x6d, 0x70, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x08, 0x64, 0x75, 0x6d, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x09, 0x42, 0x0a, 0xfa, 0x42, 0x07, 0x92, 0x01, 0x04, 0x08, 0x01, 0x18, + 0x01, 0x52, 0x07, 0x64, 0x75, 0x6d, 0x70, 0x49, 0x64, 0x73, 0x12, 0x4f, 0x0a, 0x0f, 0x73, 0x66, + 0x74, 0x70, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x64, 0x75, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2e, 0x53, 0x46, 0x54, 0x50, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, + 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0e, 0x73, 0x66, 0x74, + 0x70, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x22, 0x14, 0x0a, 0x12, 0x55, + 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x44, 0x75, 0x6d, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x2a, 0x72, 0x0a, 0x0a, 0x44, 0x75, 0x6d, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x17, 0x0a, 0x13, 0x44, 0x55, 0x4d, 0x50, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x49, + 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x44, 0x55, 0x4d, 0x50, + 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x49, 0x4e, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, + 0x45, 0x53, 0x53, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x44, 0x55, 0x4d, 0x50, 0x5f, 0x53, 0x54, + 0x41, 0x54, 0x55, 0x53, 0x5f, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x02, 0x12, 0x15, + 0x0a, 0x11, 0x44, 0x55, 0x4d, 0x50, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x45, 0x52, + 0x52, 0x4f, 0x52, 0x10, 0x03, 0x32, 0xf0, 0x04, 0x0a, 0x05, 0x44, 0x75, 0x6d, 0x70, 0x73, 0x12, + 0x78, 0x0a, 0x09, 0x53, 0x74, 0x61, 0x72, 0x74, 0x44, 0x75, 0x6d, 0x70, 0x12, 0x1e, 0x2e, 0x64, + 0x75, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x72, + 0x74, 0x44, 0x75, 0x6d, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x64, + 0x75, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x72, + 0x74, 0x44, 0x75, 0x6d, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x3a, 0x01, 0x2a, 0x22, 0x1f, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2f, 0x64, 0x75, 0x6d, 0x70, 0x2f, 0x44, 0x75, + 0x6d, 0x70, 0x73, 0x2f, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x77, 0x0a, 0x09, 0x4c, 0x69, 0x73, + 0x74, 0x44, 0x75, 0x6d, 0x70, 0x73, 0x12, 0x1e, 0x2e, 0x64, 0x75, 0x6d, 0x70, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x75, 0x6d, 0x70, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x64, 0x75, 0x6d, 0x70, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x75, 0x6d, 0x70, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x23, 0x3a, + 0x01, 0x2a, 0x22, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x2f, 0x64, 0x75, 0x6d, 0x70, 0x2f, 0x44, 0x75, 0x6d, 0x70, 0x73, 0x2f, 0x4c, 0x69, + 0x73, 0x74, 0x12, 0x7c, 0x0a, 0x0a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x75, 0x6d, 0x70, + 0x12, 0x1f, 0x2e, 0x64, 0x75, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x75, 0x6d, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x20, 0x2e, 0x64, 0x75, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x75, 0x6d, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x3a, 0x01, 0x2a, 0x22, 0x20, + 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2f, 0x64, + 0x75, 0x6d, 0x70, 0x2f, 0x44, 0x75, 0x6d, 0x70, 0x73, 0x2f, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x12, 0x78, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x44, 0x75, 0x6d, 0x70, 0x4c, 0x6f, 0x67, 0x73, 0x12, + 0x1c, 0x2e, 0x64, 0x75, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, + 0x64, 0x75, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2c, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x26, 0x3a, 0x01, 0x2a, 0x22, 0x21, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2f, 0x64, 0x75, 0x6d, 0x70, 0x2f, 0x44, 0x75, 0x6d, + 0x70, 0x73, 0x2f, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x73, 0x12, 0x7c, 0x0a, 0x0a, 0x55, 0x70, + 0x6c, 0x6f, 0x61, 0x64, 0x44, 0x75, 0x6d, 0x70, 0x12, 0x1f, 0x2e, 0x64, 0x75, 0x6d, 0x70, 0x2e, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x44, 0x75, + 0x6d, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x64, 0x75, 0x6d, 0x70, + 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x44, + 0x75, 0x6d, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x25, 0x3a, 0x01, 0x2a, 0x22, 0x20, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2f, 0x64, 0x75, 0x6d, 0x70, 0x2f, 0x44, 0x75, 0x6d, 0x70, + 0x73, 0x2f, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0xa8, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, + 0x2e, 0x64, 0x75, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x42, 0x09, 0x44, + 0x75, 0x6d, 0x70, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x38, 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, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x70, 0x62, 0x2f, 0x64, 0x75, 0x6d, 0x70, 0x3b, 0x64, 0x75, 0x6d, 0x70, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0xa2, 0x02, 0x03, 0x44, 0x58, 0x58, 0xaa, 0x02, 0x0c, 0x44, 0x75, 0x6d, + 0x70, 0x2e, 0x56, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0xca, 0x02, 0x0c, 0x44, 0x75, 0x6d, 0x70, + 0x5c, 0x56, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0xe2, 0x02, 0x18, 0x44, 0x75, 0x6d, 0x70, 0x5c, + 0x56, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0xea, 0x02, 0x0d, 0x44, 0x75, 0x6d, 0x70, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_managementpb_dump_dump_proto_rawDescOnce sync.Once + file_managementpb_dump_dump_proto_rawDescData = file_managementpb_dump_dump_proto_rawDesc +) + +func file_managementpb_dump_dump_proto_rawDescGZIP() []byte { + file_managementpb_dump_dump_proto_rawDescOnce.Do(func() { + file_managementpb_dump_dump_proto_rawDescData = protoimpl.X.CompressGZIP(file_managementpb_dump_dump_proto_rawDescData) + }) + return file_managementpb_dump_dump_proto_rawDescData +} + +var ( + file_managementpb_dump_dump_proto_enumTypes = make([]protoimpl.EnumInfo, 1) + file_managementpb_dump_dump_proto_msgTypes = make([]protoimpl.MessageInfo, 13) + file_managementpb_dump_dump_proto_goTypes = []interface{}{ + (DumpStatus)(0), // 0: dump.v1beta1.DumpStatus + (*Dump)(nil), // 1: dump.v1beta1.Dump + (*StartDumpRequest)(nil), // 2: dump.v1beta1.StartDumpRequest + (*StartDumpResponse)(nil), // 3: dump.v1beta1.StartDumpResponse + (*ListDumpsRequest)(nil), // 4: dump.v1beta1.ListDumpsRequest + (*ListDumpsResponse)(nil), // 5: dump.v1beta1.ListDumpsResponse + (*DeleteDumpRequest)(nil), // 6: dump.v1beta1.DeleteDumpRequest + (*DeleteDumpResponse)(nil), // 7: dump.v1beta1.DeleteDumpResponse + (*GetLogsRequest)(nil), // 8: dump.v1beta1.GetLogsRequest + (*GetLogsResponse)(nil), // 9: dump.v1beta1.GetLogsResponse + (*LogChunk)(nil), // 10: dump.v1beta1.LogChunk + (*SFTPParameters)(nil), // 11: dump.v1beta1.SFTPParameters + (*UploadDumpRequest)(nil), // 12: dump.v1beta1.UploadDumpRequest + (*UploadDumpResponse)(nil), // 13: dump.v1beta1.UploadDumpResponse + (*timestamppb.Timestamp)(nil), // 14: google.protobuf.Timestamp + } +) + +var file_managementpb_dump_dump_proto_depIdxs = []int32{ + 0, // 0: dump.v1beta1.Dump.status:type_name -> dump.v1beta1.DumpStatus + 14, // 1: dump.v1beta1.Dump.start_time:type_name -> google.protobuf.Timestamp + 14, // 2: dump.v1beta1.Dump.end_time:type_name -> google.protobuf.Timestamp + 14, // 3: dump.v1beta1.Dump.created_at:type_name -> google.protobuf.Timestamp + 14, // 4: dump.v1beta1.StartDumpRequest.start_time:type_name -> google.protobuf.Timestamp + 14, // 5: dump.v1beta1.StartDumpRequest.end_time:type_name -> google.protobuf.Timestamp + 1, // 6: dump.v1beta1.ListDumpsResponse.dumps:type_name -> dump.v1beta1.Dump + 10, // 7: dump.v1beta1.GetLogsResponse.logs:type_name -> dump.v1beta1.LogChunk + 11, // 8: dump.v1beta1.UploadDumpRequest.sftp_parameters:type_name -> dump.v1beta1.SFTPParameters + 2, // 9: dump.v1beta1.Dumps.StartDump:input_type -> dump.v1beta1.StartDumpRequest + 4, // 10: dump.v1beta1.Dumps.ListDumps:input_type -> dump.v1beta1.ListDumpsRequest + 6, // 11: dump.v1beta1.Dumps.DeleteDump:input_type -> dump.v1beta1.DeleteDumpRequest + 8, // 12: dump.v1beta1.Dumps.GetDumpLogs:input_type -> dump.v1beta1.GetLogsRequest + 12, // 13: dump.v1beta1.Dumps.UploadDump:input_type -> dump.v1beta1.UploadDumpRequest + 3, // 14: dump.v1beta1.Dumps.StartDump:output_type -> dump.v1beta1.StartDumpResponse + 5, // 15: dump.v1beta1.Dumps.ListDumps:output_type -> dump.v1beta1.ListDumpsResponse + 7, // 16: dump.v1beta1.Dumps.DeleteDump:output_type -> dump.v1beta1.DeleteDumpResponse + 9, // 17: dump.v1beta1.Dumps.GetDumpLogs:output_type -> dump.v1beta1.GetLogsResponse + 13, // 18: dump.v1beta1.Dumps.UploadDump:output_type -> dump.v1beta1.UploadDumpResponse + 14, // [14:19] is the sub-list for method output_type + 9, // [9:14] is the sub-list for method input_type + 9, // [9:9] is the sub-list for extension type_name + 9, // [9:9] is the sub-list for extension extendee + 0, // [0:9] is the sub-list for field type_name +} + +func init() { file_managementpb_dump_dump_proto_init() } +func file_managementpb_dump_dump_proto_init() { + if File_managementpb_dump_dump_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_managementpb_dump_dump_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Dump); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_managementpb_dump_dump_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StartDumpRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_managementpb_dump_dump_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StartDumpResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_managementpb_dump_dump_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListDumpsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_managementpb_dump_dump_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListDumpsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_managementpb_dump_dump_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteDumpRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_managementpb_dump_dump_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteDumpResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_managementpb_dump_dump_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetLogsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_managementpb_dump_dump_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetLogsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_managementpb_dump_dump_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LogChunk); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_managementpb_dump_dump_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SFTPParameters); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_managementpb_dump_dump_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UploadDumpRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_managementpb_dump_dump_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UploadDumpResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_managementpb_dump_dump_proto_rawDesc, + NumEnums: 1, + NumMessages: 13, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_managementpb_dump_dump_proto_goTypes, + DependencyIndexes: file_managementpb_dump_dump_proto_depIdxs, + EnumInfos: file_managementpb_dump_dump_proto_enumTypes, + MessageInfos: file_managementpb_dump_dump_proto_msgTypes, + }.Build() + File_managementpb_dump_dump_proto = out.File + file_managementpb_dump_dump_proto_rawDesc = nil + file_managementpb_dump_dump_proto_goTypes = nil + file_managementpb_dump_dump_proto_depIdxs = nil +} diff --git a/api/managementpb/dump/dump.pb.gw.go b/api/managementpb/dump/dump.pb.gw.go new file mode 100644 index 0000000000..97f94a9292 --- /dev/null +++ b/api/managementpb/dump/dump.pb.gw.go @@ -0,0 +1,491 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: managementpb/dump/dump.proto + +/* +Package dumpv1beta1 is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package dumpv1beta1 + +import ( + "context" + "io" + "net/http" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "github.com/grpc-ecosystem/grpc-gateway/v2/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" +) + +// Suppress "imported and not used" errors +var ( + _ codes.Code + _ io.Reader + _ status.Status + _ = runtime.String + _ = utilities.NewDoubleArray + _ = metadata.Join +) + +func request_Dumps_StartDump_0(ctx context.Context, marshaler runtime.Marshaler, client DumpsClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq StartDumpRequest + 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.StartDump(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err +} + +func local_request_Dumps_StartDump_0(ctx context.Context, marshaler runtime.Marshaler, server DumpsServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq StartDumpRequest + 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.StartDump(ctx, &protoReq) + return msg, metadata, err +} + +func request_Dumps_ListDumps_0(ctx context.Context, marshaler runtime.Marshaler, client DumpsClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListDumpsRequest + 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.ListDumps(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err +} + +func local_request_Dumps_ListDumps_0(ctx context.Context, marshaler runtime.Marshaler, server DumpsServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListDumpsRequest + 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.ListDumps(ctx, &protoReq) + return msg, metadata, err +} + +func request_Dumps_DeleteDump_0(ctx context.Context, marshaler runtime.Marshaler, client DumpsClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq DeleteDumpRequest + 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.DeleteDump(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err +} + +func local_request_Dumps_DeleteDump_0(ctx context.Context, marshaler runtime.Marshaler, server DumpsServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq DeleteDumpRequest + 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.DeleteDump(ctx, &protoReq) + return msg, metadata, err +} + +func request_Dumps_GetDumpLogs_0(ctx context.Context, marshaler runtime.Marshaler, client DumpsClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetLogsRequest + 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.GetDumpLogs(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err +} + +func local_request_Dumps_GetDumpLogs_0(ctx context.Context, marshaler runtime.Marshaler, server DumpsServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetLogsRequest + 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.GetDumpLogs(ctx, &protoReq) + return msg, metadata, err +} + +func request_Dumps_UploadDump_0(ctx context.Context, marshaler runtime.Marshaler, client DumpsClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq UploadDumpRequest + 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.UploadDump(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err +} + +func local_request_Dumps_UploadDump_0(ctx context.Context, marshaler runtime.Marshaler, server DumpsServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq UploadDumpRequest + 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.UploadDump(ctx, &protoReq) + return msg, metadata, err +} + +// RegisterDumpsHandlerServer registers the http handlers for service Dumps to "mux". +// UnaryRPC :call DumpsServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterDumpsHandlerFromEndpoint instead. +func RegisterDumpsHandlerServer(ctx context.Context, mux *runtime.ServeMux, server DumpsServer) error { + mux.Handle("POST", pattern_Dumps_StartDump_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, "/dump.v1beta1.Dumps/StartDump", runtime.WithHTTPPathPattern("/v1/management/dump/Dumps/Start")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Dumps_StartDump_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_Dumps_StartDump_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + + mux.Handle("POST", pattern_Dumps_ListDumps_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, "/dump.v1beta1.Dumps/ListDumps", runtime.WithHTTPPathPattern("/v1/management/dump/Dumps/List")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Dumps_ListDumps_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_Dumps_ListDumps_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + + mux.Handle("POST", pattern_Dumps_DeleteDump_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, "/dump.v1beta1.Dumps/DeleteDump", runtime.WithHTTPPathPattern("/v1/management/dump/Dumps/Delete")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Dumps_DeleteDump_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_Dumps_DeleteDump_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + + mux.Handle("POST", pattern_Dumps_GetDumpLogs_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, "/dump.v1beta1.Dumps/GetDumpLogs", runtime.WithHTTPPathPattern("/v1/management/dump/Dumps/GetLogs")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Dumps_GetDumpLogs_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_Dumps_GetDumpLogs_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + + mux.Handle("POST", pattern_Dumps_UploadDump_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, "/dump.v1beta1.Dumps/UploadDump", runtime.WithHTTPPathPattern("/v1/management/dump/Dumps/Upload")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Dumps_UploadDump_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_Dumps_UploadDump_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + + return nil +} + +// RegisterDumpsHandlerFromEndpoint is same as RegisterDumpsHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterDumpsHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.DialContext(ctx, endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterDumpsHandler(ctx, mux, conn) +} + +// RegisterDumpsHandler registers the http handlers for service Dumps to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterDumpsHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterDumpsHandlerClient(ctx, mux, NewDumpsClient(conn)) +} + +// RegisterDumpsHandlerClient registers the http handlers for service Dumps +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "DumpsClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "DumpsClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "DumpsClient" to call the correct interceptors. +func RegisterDumpsHandlerClient(ctx context.Context, mux *runtime.ServeMux, client DumpsClient) error { + mux.Handle("POST", pattern_Dumps_StartDump_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, "/dump.v1beta1.Dumps/StartDump", runtime.WithHTTPPathPattern("/v1/management/dump/Dumps/Start")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Dumps_StartDump_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_Dumps_StartDump_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + + mux.Handle("POST", pattern_Dumps_ListDumps_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, "/dump.v1beta1.Dumps/ListDumps", runtime.WithHTTPPathPattern("/v1/management/dump/Dumps/List")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Dumps_ListDumps_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_Dumps_ListDumps_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + + mux.Handle("POST", pattern_Dumps_DeleteDump_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, "/dump.v1beta1.Dumps/DeleteDump", runtime.WithHTTPPathPattern("/v1/management/dump/Dumps/Delete")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Dumps_DeleteDump_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_Dumps_DeleteDump_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + + mux.Handle("POST", pattern_Dumps_GetDumpLogs_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, "/dump.v1beta1.Dumps/GetDumpLogs", runtime.WithHTTPPathPattern("/v1/management/dump/Dumps/GetLogs")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Dumps_GetDumpLogs_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_Dumps_GetDumpLogs_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + + mux.Handle("POST", pattern_Dumps_UploadDump_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, "/dump.v1beta1.Dumps/UploadDump", runtime.WithHTTPPathPattern("/v1/management/dump/Dumps/Upload")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Dumps_UploadDump_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_Dumps_UploadDump_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) + + return nil +} + +var ( + pattern_Dumps_StartDump_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"v1", "management", "dump", "Dumps", "Start"}, "")) + + pattern_Dumps_ListDumps_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"v1", "management", "dump", "Dumps", "List"}, "")) + + pattern_Dumps_DeleteDump_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"v1", "management", "dump", "Dumps", "Delete"}, "")) + + pattern_Dumps_GetDumpLogs_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"v1", "management", "dump", "Dumps", "GetLogs"}, "")) + + pattern_Dumps_UploadDump_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"v1", "management", "dump", "Dumps", "Upload"}, "")) +) + +var ( + forward_Dumps_StartDump_0 = runtime.ForwardResponseMessage + + forward_Dumps_ListDumps_0 = runtime.ForwardResponseMessage + + forward_Dumps_DeleteDump_0 = runtime.ForwardResponseMessage + + forward_Dumps_GetDumpLogs_0 = runtime.ForwardResponseMessage + + forward_Dumps_UploadDump_0 = runtime.ForwardResponseMessage +) diff --git a/api/managementpb/dump/dump.pb.validate.go b/api/managementpb/dump/dump.pb.validate.go new file mode 100644 index 0000000000..68148db850 --- /dev/null +++ b/api/managementpb/dump/dump.pb.validate.go @@ -0,0 +1,1728 @@ +// Code generated by protoc-gen-validate. DO NOT EDIT. +// source: managementpb/dump/dump.proto + +package dumpv1beta1 + +import ( + "bytes" + "errors" + "fmt" + "net" + "net/mail" + "net/url" + "regexp" + "sort" + "strings" + "time" + "unicode/utf8" + + "google.golang.org/protobuf/types/known/anypb" +) + +// ensure the imports are used +var ( + _ = bytes.MinRead + _ = errors.New("") + _ = fmt.Print + _ = utf8.UTFMax + _ = (*regexp.Regexp)(nil) + _ = (*strings.Reader)(nil) + _ = net.IPv4len + _ = time.Duration(0) + _ = (*url.URL)(nil) + _ = (*mail.Address)(nil) + _ = anypb.Any{} + _ = sort.Sort +) + +// Validate checks the field values on Dump 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 *Dump) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on Dump 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 DumpMultiError, or nil if none found. +func (m *Dump) ValidateAll() error { + return m.validate(true) +} + +func (m *Dump) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for DumpId + + // no validation rules for Status + + if all { + switch v := interface{}(m.GetStartTime()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, DumpValidationError{ + field: "StartTime", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, DumpValidationError{ + field: "StartTime", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetStartTime()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return DumpValidationError{ + field: "StartTime", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if all { + switch v := interface{}(m.GetEndTime()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, DumpValidationError{ + field: "EndTime", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, DumpValidationError{ + field: "EndTime", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetEndTime()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return DumpValidationError{ + field: "EndTime", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if all { + switch v := interface{}(m.GetCreatedAt()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, DumpValidationError{ + field: "CreatedAt", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, DumpValidationError{ + field: "CreatedAt", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetCreatedAt()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return DumpValidationError{ + field: "CreatedAt", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if len(errors) > 0 { + return DumpMultiError(errors) + } + + return nil +} + +// DumpMultiError is an error wrapping multiple validation errors returned by +// Dump.ValidateAll() if the designated constraints aren't met. +type DumpMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m DumpMultiError) 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 DumpMultiError) AllErrors() []error { return m } + +// DumpValidationError is the validation error returned by Dump.Validate if the +// designated constraints aren't met. +type DumpValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e DumpValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e DumpValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e DumpValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e DumpValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e DumpValidationError) ErrorName() string { return "DumpValidationError" } + +// Error satisfies the builtin error interface +func (e DumpValidationError) 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 %sDump.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = DumpValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = DumpValidationError{} + +// Validate checks the field values on StartDumpRequest 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 *StartDumpRequest) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on StartDumpRequest 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 +// StartDumpRequestMultiError, or nil if none found. +func (m *StartDumpRequest) ValidateAll() error { + return m.validate(true) +} + +func (m *StartDumpRequest) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + if all { + switch v := interface{}(m.GetStartTime()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, StartDumpRequestValidationError{ + field: "StartTime", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, StartDumpRequestValidationError{ + field: "StartTime", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetStartTime()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return StartDumpRequestValidationError{ + field: "StartTime", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if all { + switch v := interface{}(m.GetEndTime()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, StartDumpRequestValidationError{ + field: "EndTime", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, StartDumpRequestValidationError{ + field: "EndTime", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetEndTime()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return StartDumpRequestValidationError{ + field: "EndTime", + reason: "embedded message failed validation", + cause: err, + } + } + } + + // no validation rules for ExportQan + + // no validation rules for IgnoreLoad + + if len(errors) > 0 { + return StartDumpRequestMultiError(errors) + } + + return nil +} + +// StartDumpRequestMultiError is an error wrapping multiple validation errors +// returned by StartDumpRequest.ValidateAll() if the designated constraints +// aren't met. +type StartDumpRequestMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m StartDumpRequestMultiError) 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 StartDumpRequestMultiError) AllErrors() []error { return m } + +// StartDumpRequestValidationError is the validation error returned by +// StartDumpRequest.Validate if the designated constraints aren't met. +type StartDumpRequestValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e StartDumpRequestValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e StartDumpRequestValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e StartDumpRequestValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e StartDumpRequestValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e StartDumpRequestValidationError) ErrorName() string { return "StartDumpRequestValidationError" } + +// Error satisfies the builtin error interface +func (e StartDumpRequestValidationError) 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 %sStartDumpRequest.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = StartDumpRequestValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = StartDumpRequestValidationError{} + +// Validate checks the field values on StartDumpResponse 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 *StartDumpResponse) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on StartDumpResponse 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 +// StartDumpResponseMultiError, or nil if none found. +func (m *StartDumpResponse) ValidateAll() error { + return m.validate(true) +} + +func (m *StartDumpResponse) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for DumpId + + if len(errors) > 0 { + return StartDumpResponseMultiError(errors) + } + + return nil +} + +// StartDumpResponseMultiError is an error wrapping multiple validation errors +// returned by StartDumpResponse.ValidateAll() if the designated constraints +// aren't met. +type StartDumpResponseMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m StartDumpResponseMultiError) 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 StartDumpResponseMultiError) AllErrors() []error { return m } + +// StartDumpResponseValidationError is the validation error returned by +// StartDumpResponse.Validate if the designated constraints aren't met. +type StartDumpResponseValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e StartDumpResponseValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e StartDumpResponseValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e StartDumpResponseValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e StartDumpResponseValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e StartDumpResponseValidationError) ErrorName() string { + return "StartDumpResponseValidationError" +} + +// Error satisfies the builtin error interface +func (e StartDumpResponseValidationError) 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 %sStartDumpResponse.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = StartDumpResponseValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = StartDumpResponseValidationError{} + +// Validate checks the field values on ListDumpsRequest 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 *ListDumpsRequest) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on ListDumpsRequest 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 +// ListDumpsRequestMultiError, or nil if none found. +func (m *ListDumpsRequest) ValidateAll() error { + return m.validate(true) +} + +func (m *ListDumpsRequest) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + if len(errors) > 0 { + return ListDumpsRequestMultiError(errors) + } + + return nil +} + +// ListDumpsRequestMultiError is an error wrapping multiple validation errors +// returned by ListDumpsRequest.ValidateAll() if the designated constraints +// aren't met. +type ListDumpsRequestMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m ListDumpsRequestMultiError) 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 ListDumpsRequestMultiError) AllErrors() []error { return m } + +// ListDumpsRequestValidationError is the validation error returned by +// ListDumpsRequest.Validate if the designated constraints aren't met. +type ListDumpsRequestValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e ListDumpsRequestValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e ListDumpsRequestValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e ListDumpsRequestValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e ListDumpsRequestValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e ListDumpsRequestValidationError) ErrorName() string { return "ListDumpsRequestValidationError" } + +// Error satisfies the builtin error interface +func (e ListDumpsRequestValidationError) 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 %sListDumpsRequest.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = ListDumpsRequestValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = ListDumpsRequestValidationError{} + +// Validate checks the field values on ListDumpsResponse 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 *ListDumpsResponse) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on ListDumpsResponse 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 +// ListDumpsResponseMultiError, or nil if none found. +func (m *ListDumpsResponse) ValidateAll() error { + return m.validate(true) +} + +func (m *ListDumpsResponse) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + for idx, item := range m.GetDumps() { + _, _ = idx, item + + if all { + switch v := interface{}(item).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, ListDumpsResponseValidationError{ + field: fmt.Sprintf("Dumps[%v]", idx), + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, ListDumpsResponseValidationError{ + field: fmt.Sprintf("Dumps[%v]", idx), + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(item).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return ListDumpsResponseValidationError{ + field: fmt.Sprintf("Dumps[%v]", idx), + reason: "embedded message failed validation", + cause: err, + } + } + } + + } + + if len(errors) > 0 { + return ListDumpsResponseMultiError(errors) + } + + return nil +} + +// ListDumpsResponseMultiError is an error wrapping multiple validation errors +// returned by ListDumpsResponse.ValidateAll() if the designated constraints +// aren't met. +type ListDumpsResponseMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m ListDumpsResponseMultiError) 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 ListDumpsResponseMultiError) AllErrors() []error { return m } + +// ListDumpsResponseValidationError is the validation error returned by +// ListDumpsResponse.Validate if the designated constraints aren't met. +type ListDumpsResponseValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e ListDumpsResponseValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e ListDumpsResponseValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e ListDumpsResponseValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e ListDumpsResponseValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e ListDumpsResponseValidationError) ErrorName() string { + return "ListDumpsResponseValidationError" +} + +// Error satisfies the builtin error interface +func (e ListDumpsResponseValidationError) 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 %sListDumpsResponse.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = ListDumpsResponseValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = ListDumpsResponseValidationError{} + +// Validate checks the field values on DeleteDumpRequest 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 *DeleteDumpRequest) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on DeleteDumpRequest 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 +// DeleteDumpRequestMultiError, or nil if none found. +func (m *DeleteDumpRequest) ValidateAll() error { + return m.validate(true) +} + +func (m *DeleteDumpRequest) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + if len(m.GetDumpIds()) < 1 { + err := DeleteDumpRequestValidationError{ + field: "DumpIds", + reason: "value must contain at least 1 item(s)", + } + if !all { + return err + } + errors = append(errors, err) + } + + _DeleteDumpRequest_DumpIds_Unique := make(map[string]struct{}, len(m.GetDumpIds())) + + for idx, item := range m.GetDumpIds() { + _, _ = idx, item + + if _, exists := _DeleteDumpRequest_DumpIds_Unique[item]; exists { + err := DeleteDumpRequestValidationError{ + field: fmt.Sprintf("DumpIds[%v]", idx), + reason: "repeated value must contain unique items", + } + if !all { + return err + } + errors = append(errors, err) + } else { + _DeleteDumpRequest_DumpIds_Unique[item] = struct{}{} + } + + // no validation rules for DumpIds[idx] + } + + if len(errors) > 0 { + return DeleteDumpRequestMultiError(errors) + } + + return nil +} + +// DeleteDumpRequestMultiError is an error wrapping multiple validation errors +// returned by DeleteDumpRequest.ValidateAll() if the designated constraints +// aren't met. +type DeleteDumpRequestMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m DeleteDumpRequestMultiError) 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 DeleteDumpRequestMultiError) AllErrors() []error { return m } + +// DeleteDumpRequestValidationError is the validation error returned by +// DeleteDumpRequest.Validate if the designated constraints aren't met. +type DeleteDumpRequestValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e DeleteDumpRequestValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e DeleteDumpRequestValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e DeleteDumpRequestValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e DeleteDumpRequestValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e DeleteDumpRequestValidationError) ErrorName() string { + return "DeleteDumpRequestValidationError" +} + +// Error satisfies the builtin error interface +func (e DeleteDumpRequestValidationError) 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 %sDeleteDumpRequest.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = DeleteDumpRequestValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = DeleteDumpRequestValidationError{} + +// Validate checks the field values on DeleteDumpResponse 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 *DeleteDumpResponse) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on DeleteDumpResponse 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 +// DeleteDumpResponseMultiError, or nil if none found. +func (m *DeleteDumpResponse) ValidateAll() error { + return m.validate(true) +} + +func (m *DeleteDumpResponse) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + if len(errors) > 0 { + return DeleteDumpResponseMultiError(errors) + } + + return nil +} + +// DeleteDumpResponseMultiError is an error wrapping multiple validation errors +// returned by DeleteDumpResponse.ValidateAll() if the designated constraints +// aren't met. +type DeleteDumpResponseMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m DeleteDumpResponseMultiError) 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 DeleteDumpResponseMultiError) AllErrors() []error { return m } + +// DeleteDumpResponseValidationError is the validation error returned by +// DeleteDumpResponse.Validate if the designated constraints aren't met. +type DeleteDumpResponseValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e DeleteDumpResponseValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e DeleteDumpResponseValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e DeleteDumpResponseValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e DeleteDumpResponseValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e DeleteDumpResponseValidationError) ErrorName() string { + return "DeleteDumpResponseValidationError" +} + +// Error satisfies the builtin error interface +func (e DeleteDumpResponseValidationError) 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 %sDeleteDumpResponse.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = DeleteDumpResponseValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = DeleteDumpResponseValidationError{} + +// Validate checks the field values on GetLogsRequest 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 *GetLogsRequest) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on GetLogsRequest 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 GetLogsRequestMultiError, +// or nil if none found. +func (m *GetLogsRequest) ValidateAll() error { + return m.validate(true) +} + +func (m *GetLogsRequest) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + if utf8.RuneCountInString(m.GetDumpId()) < 1 { + err := GetLogsRequestValidationError{ + field: "DumpId", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + // no validation rules for Offset + + // no validation rules for Limit + + if len(errors) > 0 { + return GetLogsRequestMultiError(errors) + } + + return nil +} + +// GetLogsRequestMultiError is an error wrapping multiple validation errors +// returned by GetLogsRequest.ValidateAll() if the designated constraints +// aren't met. +type GetLogsRequestMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m GetLogsRequestMultiError) 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 GetLogsRequestMultiError) AllErrors() []error { return m } + +// GetLogsRequestValidationError is the validation error returned by +// GetLogsRequest.Validate if the designated constraints aren't met. +type GetLogsRequestValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e GetLogsRequestValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e GetLogsRequestValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e GetLogsRequestValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e GetLogsRequestValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e GetLogsRequestValidationError) ErrorName() string { return "GetLogsRequestValidationError" } + +// Error satisfies the builtin error interface +func (e GetLogsRequestValidationError) 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 %sGetLogsRequest.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = GetLogsRequestValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = GetLogsRequestValidationError{} + +// Validate checks the field values on GetLogsResponse 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 *GetLogsResponse) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on GetLogsResponse 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 +// GetLogsResponseMultiError, or nil if none found. +func (m *GetLogsResponse) ValidateAll() error { + return m.validate(true) +} + +func (m *GetLogsResponse) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + for idx, item := range m.GetLogs() { + _, _ = idx, item + + if all { + switch v := interface{}(item).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, GetLogsResponseValidationError{ + field: fmt.Sprintf("Logs[%v]", idx), + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, GetLogsResponseValidationError{ + field: fmt.Sprintf("Logs[%v]", idx), + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(item).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return GetLogsResponseValidationError{ + field: fmt.Sprintf("Logs[%v]", idx), + reason: "embedded message failed validation", + cause: err, + } + } + } + + } + + // no validation rules for End + + if len(errors) > 0 { + return GetLogsResponseMultiError(errors) + } + + return nil +} + +// GetLogsResponseMultiError is an error wrapping multiple validation errors +// returned by GetLogsResponse.ValidateAll() if the designated constraints +// aren't met. +type GetLogsResponseMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m GetLogsResponseMultiError) 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 GetLogsResponseMultiError) AllErrors() []error { return m } + +// GetLogsResponseValidationError is the validation error returned by +// GetLogsResponse.Validate if the designated constraints aren't met. +type GetLogsResponseValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e GetLogsResponseValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e GetLogsResponseValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e GetLogsResponseValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e GetLogsResponseValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e GetLogsResponseValidationError) ErrorName() string { return "GetLogsResponseValidationError" } + +// Error satisfies the builtin error interface +func (e GetLogsResponseValidationError) 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 %sGetLogsResponse.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = GetLogsResponseValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = GetLogsResponseValidationError{} + +// Validate checks the field values on LogChunk 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 *LogChunk) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on LogChunk 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 LogChunkMultiError, or nil +// if none found. +func (m *LogChunk) ValidateAll() error { + return m.validate(true) +} + +func (m *LogChunk) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for ChunkId + + // no validation rules for Data + + if len(errors) > 0 { + return LogChunkMultiError(errors) + } + + return nil +} + +// LogChunkMultiError is an error wrapping multiple validation errors returned +// by LogChunk.ValidateAll() if the designated constraints aren't met. +type LogChunkMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m LogChunkMultiError) 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 LogChunkMultiError) AllErrors() []error { return m } + +// LogChunkValidationError is the validation error returned by +// LogChunk.Validate if the designated constraints aren't met. +type LogChunkValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e LogChunkValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e LogChunkValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e LogChunkValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e LogChunkValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e LogChunkValidationError) ErrorName() string { return "LogChunkValidationError" } + +// Error satisfies the builtin error interface +func (e LogChunkValidationError) 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 %sLogChunk.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = LogChunkValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = LogChunkValidationError{} + +// Validate checks the field values on SFTPParameters 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 *SFTPParameters) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on SFTPParameters 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 SFTPParametersMultiError, +// or nil if none found. +func (m *SFTPParameters) ValidateAll() error { + return m.validate(true) +} + +func (m *SFTPParameters) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + if utf8.RuneCountInString(m.GetAddress()) < 1 { + err := SFTPParametersValidationError{ + field: "Address", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetUser()) < 1 { + err := SFTPParametersValidationError{ + field: "User", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if utf8.RuneCountInString(m.GetPassword()) < 1 { + err := SFTPParametersValidationError{ + field: "Password", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + // no validation rules for Directory + + if len(errors) > 0 { + return SFTPParametersMultiError(errors) + } + + return nil +} + +// SFTPParametersMultiError is an error wrapping multiple validation errors +// returned by SFTPParameters.ValidateAll() if the designated constraints +// aren't met. +type SFTPParametersMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m SFTPParametersMultiError) 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 SFTPParametersMultiError) AllErrors() []error { return m } + +// SFTPParametersValidationError is the validation error returned by +// SFTPParameters.Validate if the designated constraints aren't met. +type SFTPParametersValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e SFTPParametersValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e SFTPParametersValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e SFTPParametersValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e SFTPParametersValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e SFTPParametersValidationError) ErrorName() string { return "SFTPParametersValidationError" } + +// Error satisfies the builtin error interface +func (e SFTPParametersValidationError) 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 %sSFTPParameters.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = SFTPParametersValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = SFTPParametersValidationError{} + +// Validate checks the field values on UploadDumpRequest 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 *UploadDumpRequest) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on UploadDumpRequest 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 +// UploadDumpRequestMultiError, or nil if none found. +func (m *UploadDumpRequest) ValidateAll() error { + return m.validate(true) +} + +func (m *UploadDumpRequest) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + if len(m.GetDumpIds()) < 1 { + err := UploadDumpRequestValidationError{ + field: "DumpIds", + reason: "value must contain at least 1 item(s)", + } + if !all { + return err + } + errors = append(errors, err) + } + + _UploadDumpRequest_DumpIds_Unique := make(map[string]struct{}, len(m.GetDumpIds())) + + for idx, item := range m.GetDumpIds() { + _, _ = idx, item + + if _, exists := _UploadDumpRequest_DumpIds_Unique[item]; exists { + err := UploadDumpRequestValidationError{ + field: fmt.Sprintf("DumpIds[%v]", idx), + reason: "repeated value must contain unique items", + } + if !all { + return err + } + errors = append(errors, err) + } else { + _UploadDumpRequest_DumpIds_Unique[item] = struct{}{} + } + + // no validation rules for DumpIds[idx] + } + + if m.GetSftpParameters() == nil { + err := UploadDumpRequestValidationError{ + field: "SftpParameters", + reason: "value is required", + } + if !all { + return err + } + errors = append(errors, err) + } + + if all { + switch v := interface{}(m.GetSftpParameters()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, UploadDumpRequestValidationError{ + field: "SftpParameters", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, UploadDumpRequestValidationError{ + field: "SftpParameters", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetSftpParameters()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return UploadDumpRequestValidationError{ + field: "SftpParameters", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if len(errors) > 0 { + return UploadDumpRequestMultiError(errors) + } + + return nil +} + +// UploadDumpRequestMultiError is an error wrapping multiple validation errors +// returned by UploadDumpRequest.ValidateAll() if the designated constraints +// aren't met. +type UploadDumpRequestMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m UploadDumpRequestMultiError) 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 UploadDumpRequestMultiError) AllErrors() []error { return m } + +// UploadDumpRequestValidationError is the validation error returned by +// UploadDumpRequest.Validate if the designated constraints aren't met. +type UploadDumpRequestValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e UploadDumpRequestValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e UploadDumpRequestValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e UploadDumpRequestValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e UploadDumpRequestValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e UploadDumpRequestValidationError) ErrorName() string { + return "UploadDumpRequestValidationError" +} + +// Error satisfies the builtin error interface +func (e UploadDumpRequestValidationError) 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 %sUploadDumpRequest.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = UploadDumpRequestValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = UploadDumpRequestValidationError{} + +// Validate checks the field values on UploadDumpResponse 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 *UploadDumpResponse) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on UploadDumpResponse 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 +// UploadDumpResponseMultiError, or nil if none found. +func (m *UploadDumpResponse) ValidateAll() error { + return m.validate(true) +} + +func (m *UploadDumpResponse) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + if len(errors) > 0 { + return UploadDumpResponseMultiError(errors) + } + + return nil +} + +// UploadDumpResponseMultiError is an error wrapping multiple validation errors +// returned by UploadDumpResponse.ValidateAll() if the designated constraints +// aren't met. +type UploadDumpResponseMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m UploadDumpResponseMultiError) 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 UploadDumpResponseMultiError) AllErrors() []error { return m } + +// UploadDumpResponseValidationError is the validation error returned by +// UploadDumpResponse.Validate if the designated constraints aren't met. +type UploadDumpResponseValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e UploadDumpResponseValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e UploadDumpResponseValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e UploadDumpResponseValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e UploadDumpResponseValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e UploadDumpResponseValidationError) ErrorName() string { + return "UploadDumpResponseValidationError" +} + +// Error satisfies the builtin error interface +func (e UploadDumpResponseValidationError) 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 %sUploadDumpResponse.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = UploadDumpResponseValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = UploadDumpResponseValidationError{} diff --git a/api/managementpb/dump/dump.proto b/api/managementpb/dump/dump.proto new file mode 100644 index 0000000000..4328af81fc --- /dev/null +++ b/api/managementpb/dump/dump.proto @@ -0,0 +1,127 @@ +syntax = "proto3"; + +package dump.v1beta1; + +import "google/api/annotations.proto"; +import "google/protobuf/timestamp.proto"; +import "protoc-gen-openapiv2/options/annotations.proto"; +import "validate/validate.proto"; + +option go_package = "api/managementpb/dump;dumpv1beta1"; + +enum DumpStatus { + DUMP_STATUS_INVALID = 0; + DUMP_STATUS_IN_PROGRESS = 1; + DUMP_STATUS_SUCCESS = 2; + DUMP_STATUS_ERROR = 3; +} + +message Dump { + string dump_id = 1; + DumpStatus status = 2; + repeated string service_names = 3; + google.protobuf.Timestamp start_time = 4; + google.protobuf.Timestamp end_time = 5; + google.protobuf.Timestamp created_at = 7; +} + +message StartDumpRequest { + repeated string service_names = 1; + google.protobuf.Timestamp start_time = 2; + google.protobuf.Timestamp end_time = 3; + bool export_qan = 4; + bool ignore_load = 5; +} + +message StartDumpResponse { + string dump_id = 1; +} + +message ListDumpsRequest {} + +message ListDumpsResponse { + repeated Dump dumps = 1; +} + +message DeleteDumpRequest { + repeated string dump_ids = 1 [(validate.rules).repeated = { + min_items: 1, + unique: true + }]; +} + +message DeleteDumpResponse {} + +message GetLogsRequest { + string dump_id = 1 [(validate.rules).string.min_len = 1]; + uint32 offset = 2; + uint32 limit = 3; +} + +message GetLogsResponse { + repeated LogChunk logs = 1; + bool end = 2; +} + +// LogChunk represent one chunk of logs. +message LogChunk { + uint32 chunk_id = 1; + string data = 2; +} + +message SFTPParameters { + string address = 1 [(validate.rules).string.min_len = 1]; + string user = 2 [(validate.rules).string.min_len = 1]; + string password = 3 [(validate.rules).string.min_len = 1]; + string directory = 4; +} + +message UploadDumpRequest { + repeated string dump_ids = 1 [(validate.rules).repeated = { + min_items: 1, + unique: true + }]; + + // SFTP upload parameters. + SFTPParameters sftp_parameters = 2 [(validate.rules).message.required = true]; +} + +message UploadDumpResponse {} + +service Dumps { + // StartDump request creates pmm dump. + rpc StartDump(StartDumpRequest) returns (StartDumpResponse) { + option (google.api.http) = { + post: "/v1/management/dump/Dumps/Start" + body: "*" + }; + } + // ListDumps returns a list of all pmm dumps. + rpc ListDumps(ListDumpsRequest) returns (ListDumpsResponse) { + option (google.api.http) = { + post: "/v1/management/dump/Dumps/List" + body: "*" + }; + } + // DeleteDump deletes specified pmm dump. + rpc DeleteDump(DeleteDumpRequest) returns (DeleteDumpResponse) { + option (google.api.http) = { + post: "/v1/management/dump/Dumps/Delete" + body: "*" + }; + } + // GetLogs returns logs from pmm-dump tool. + rpc GetDumpLogs(GetLogsRequest) returns (GetLogsResponse) { + option (google.api.http) = { + post: "/v1/management/dump/Dumps/GetLogs" + body: "*" + }; + } + // UploadDump uploads selected dumps to remote server. + rpc UploadDump(UploadDumpRequest) returns (UploadDumpResponse) { + option (google.api.http) = { + post: "/v1/management/dump/Dumps/Upload" + body: "*" + }; + } +} diff --git a/api/managementpb/dump/dump_grpc.pb.go b/api/managementpb/dump/dump_grpc.pb.go new file mode 100644 index 0000000000..5b60dc27d6 --- /dev/null +++ b/api/managementpb/dump/dump_grpc.pb.go @@ -0,0 +1,271 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc (unknown) +// source: managementpb/dump/dump.proto + +package dumpv1beta1 + +import ( + context "context" + + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + Dumps_StartDump_FullMethodName = "/dump.v1beta1.Dumps/StartDump" + Dumps_ListDumps_FullMethodName = "/dump.v1beta1.Dumps/ListDumps" + Dumps_DeleteDump_FullMethodName = "/dump.v1beta1.Dumps/DeleteDump" + Dumps_GetDumpLogs_FullMethodName = "/dump.v1beta1.Dumps/GetDumpLogs" + Dumps_UploadDump_FullMethodName = "/dump.v1beta1.Dumps/UploadDump" +) + +// DumpsClient is the client API for Dumps service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type DumpsClient interface { + // StartDump request creates pmm dump. + StartDump(ctx context.Context, in *StartDumpRequest, opts ...grpc.CallOption) (*StartDumpResponse, error) + // ListDumps returns a list of all pmm dumps. + ListDumps(ctx context.Context, in *ListDumpsRequest, opts ...grpc.CallOption) (*ListDumpsResponse, error) + // DeleteDump deletes specified pmm dump. + DeleteDump(ctx context.Context, in *DeleteDumpRequest, opts ...grpc.CallOption) (*DeleteDumpResponse, error) + // GetLogs returns logs from pmm-dump tool. + GetDumpLogs(ctx context.Context, in *GetLogsRequest, opts ...grpc.CallOption) (*GetLogsResponse, error) + // UploadDump uploads selected dumps to remote server. + UploadDump(ctx context.Context, in *UploadDumpRequest, opts ...grpc.CallOption) (*UploadDumpResponse, error) +} + +type dumpsClient struct { + cc grpc.ClientConnInterface +} + +func NewDumpsClient(cc grpc.ClientConnInterface) DumpsClient { + return &dumpsClient{cc} +} + +func (c *dumpsClient) StartDump(ctx context.Context, in *StartDumpRequest, opts ...grpc.CallOption) (*StartDumpResponse, error) { + out := new(StartDumpResponse) + err := c.cc.Invoke(ctx, Dumps_StartDump_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *dumpsClient) ListDumps(ctx context.Context, in *ListDumpsRequest, opts ...grpc.CallOption) (*ListDumpsResponse, error) { + out := new(ListDumpsResponse) + err := c.cc.Invoke(ctx, Dumps_ListDumps_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *dumpsClient) DeleteDump(ctx context.Context, in *DeleteDumpRequest, opts ...grpc.CallOption) (*DeleteDumpResponse, error) { + out := new(DeleteDumpResponse) + err := c.cc.Invoke(ctx, Dumps_DeleteDump_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *dumpsClient) GetDumpLogs(ctx context.Context, in *GetLogsRequest, opts ...grpc.CallOption) (*GetLogsResponse, error) { + out := new(GetLogsResponse) + err := c.cc.Invoke(ctx, Dumps_GetDumpLogs_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *dumpsClient) UploadDump(ctx context.Context, in *UploadDumpRequest, opts ...grpc.CallOption) (*UploadDumpResponse, error) { + out := new(UploadDumpResponse) + err := c.cc.Invoke(ctx, Dumps_UploadDump_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// DumpsServer is the server API for Dumps service. +// All implementations must embed UnimplementedDumpsServer +// for forward compatibility +type DumpsServer interface { + // StartDump request creates pmm dump. + StartDump(context.Context, *StartDumpRequest) (*StartDumpResponse, error) + // ListDumps returns a list of all pmm dumps. + ListDumps(context.Context, *ListDumpsRequest) (*ListDumpsResponse, error) + // DeleteDump deletes specified pmm dump. + DeleteDump(context.Context, *DeleteDumpRequest) (*DeleteDumpResponse, error) + // GetLogs returns logs from pmm-dump tool. + GetDumpLogs(context.Context, *GetLogsRequest) (*GetLogsResponse, error) + // UploadDump uploads selected dumps to remote server. + UploadDump(context.Context, *UploadDumpRequest) (*UploadDumpResponse, error) + mustEmbedUnimplementedDumpsServer() +} + +// UnimplementedDumpsServer must be embedded to have forward compatible implementations. +type UnimplementedDumpsServer struct{} + +func (UnimplementedDumpsServer) StartDump(context.Context, *StartDumpRequest) (*StartDumpResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method StartDump not implemented") +} + +func (UnimplementedDumpsServer) ListDumps(context.Context, *ListDumpsRequest) (*ListDumpsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListDumps not implemented") +} + +func (UnimplementedDumpsServer) DeleteDump(context.Context, *DeleteDumpRequest) (*DeleteDumpResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteDump not implemented") +} + +func (UnimplementedDumpsServer) GetDumpLogs(context.Context, *GetLogsRequest) (*GetLogsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetDumpLogs not implemented") +} + +func (UnimplementedDumpsServer) UploadDump(context.Context, *UploadDumpRequest) (*UploadDumpResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UploadDump not implemented") +} +func (UnimplementedDumpsServer) mustEmbedUnimplementedDumpsServer() {} + +// UnsafeDumpsServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to DumpsServer will +// result in compilation errors. +type UnsafeDumpsServer interface { + mustEmbedUnimplementedDumpsServer() +} + +func RegisterDumpsServer(s grpc.ServiceRegistrar, srv DumpsServer) { + s.RegisterService(&Dumps_ServiceDesc, srv) +} + +func _Dumps_StartDump_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StartDumpRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DumpsServer).StartDump(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Dumps_StartDump_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DumpsServer).StartDump(ctx, req.(*StartDumpRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Dumps_ListDumps_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListDumpsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DumpsServer).ListDumps(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Dumps_ListDumps_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DumpsServer).ListDumps(ctx, req.(*ListDumpsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Dumps_DeleteDump_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteDumpRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DumpsServer).DeleteDump(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Dumps_DeleteDump_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DumpsServer).DeleteDump(ctx, req.(*DeleteDumpRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Dumps_GetDumpLogs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetLogsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DumpsServer).GetDumpLogs(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Dumps_GetDumpLogs_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DumpsServer).GetDumpLogs(ctx, req.(*GetLogsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Dumps_UploadDump_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UploadDumpRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DumpsServer).UploadDump(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Dumps_UploadDump_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DumpsServer).UploadDump(ctx, req.(*UploadDumpRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Dumps_ServiceDesc is the grpc.ServiceDesc for Dumps service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Dumps_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "dump.v1beta1.Dumps", + HandlerType: (*DumpsServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "StartDump", + Handler: _Dumps_StartDump_Handler, + }, + { + MethodName: "ListDumps", + Handler: _Dumps_ListDumps_Handler, + }, + { + MethodName: "DeleteDump", + Handler: _Dumps_DeleteDump_Handler, + }, + { + MethodName: "GetDumpLogs", + Handler: _Dumps_GetDumpLogs_Handler, + }, + { + MethodName: "UploadDump", + Handler: _Dumps_UploadDump_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "managementpb/dump/dump.proto", +} diff --git a/api/managementpb/dump/json/client/dumps/delete_dump_parameters.go b/api/managementpb/dump/json/client/dumps/delete_dump_parameters.go new file mode 100644 index 0000000000..d61a07c02d --- /dev/null +++ b/api/managementpb/dump/json/client/dumps/delete_dump_parameters.go @@ -0,0 +1,144 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package dumps + +// 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" +) + +// NewDeleteDumpParams creates a new DeleteDumpParams 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 NewDeleteDumpParams() *DeleteDumpParams { + return &DeleteDumpParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewDeleteDumpParamsWithTimeout creates a new DeleteDumpParams object +// with the ability to set a timeout on a request. +func NewDeleteDumpParamsWithTimeout(timeout time.Duration) *DeleteDumpParams { + return &DeleteDumpParams{ + timeout: timeout, + } +} + +// NewDeleteDumpParamsWithContext creates a new DeleteDumpParams object +// with the ability to set a context for a request. +func NewDeleteDumpParamsWithContext(ctx context.Context) *DeleteDumpParams { + return &DeleteDumpParams{ + Context: ctx, + } +} + +// NewDeleteDumpParamsWithHTTPClient creates a new DeleteDumpParams object +// with the ability to set a custom HTTPClient for a request. +func NewDeleteDumpParamsWithHTTPClient(client *http.Client) *DeleteDumpParams { + return &DeleteDumpParams{ + HTTPClient: client, + } +} + +/* +DeleteDumpParams contains all the parameters to send to the API endpoint + + for the delete dump operation. + + Typically these are written to a http.Request. +*/ +type DeleteDumpParams struct { + // Body. + Body DeleteDumpBody + + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the delete dump params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *DeleteDumpParams) WithDefaults() *DeleteDumpParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the delete dump params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *DeleteDumpParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the delete dump params +func (o *DeleteDumpParams) WithTimeout(timeout time.Duration) *DeleteDumpParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the delete dump params +func (o *DeleteDumpParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the delete dump params +func (o *DeleteDumpParams) WithContext(ctx context.Context) *DeleteDumpParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the delete dump params +func (o *DeleteDumpParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the delete dump params +func (o *DeleteDumpParams) WithHTTPClient(client *http.Client) *DeleteDumpParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the delete dump params +func (o *DeleteDumpParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WithBody adds the body to the delete dump params +func (o *DeleteDumpParams) WithBody(body DeleteDumpBody) *DeleteDumpParams { + o.SetBody(body) + return o +} + +// SetBody adds the body to the delete dump params +func (o *DeleteDumpParams) SetBody(body DeleteDumpBody) { + o.Body = body +} + +// WriteToRequest writes these params to a swagger request +func (o *DeleteDumpParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + if err := r.SetBodyParam(o.Body); err != nil { + return err + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/api/managementpb/dump/json/client/dumps/delete_dump_responses.go b/api/managementpb/dump/json/client/dumps/delete_dump_responses.go new file mode 100644 index 0000000000..2dc075dfa7 --- /dev/null +++ b/api/managementpb/dump/json/client/dumps/delete_dump_responses.go @@ -0,0 +1,295 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package dumps + +// 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" +) + +// DeleteDumpReader is a Reader for the DeleteDump structure. +type DeleteDumpReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *DeleteDumpReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewDeleteDumpOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + default: + result := NewDeleteDumpDefault(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 + } +} + +// NewDeleteDumpOK creates a DeleteDumpOK with default headers values +func NewDeleteDumpOK() *DeleteDumpOK { + return &DeleteDumpOK{} +} + +/* +DeleteDumpOK describes a response with status code 200, with default header values. + +A successful response. +*/ +type DeleteDumpOK struct { + Payload interface{} +} + +func (o *DeleteDumpOK) Error() string { + return fmt.Sprintf("[POST /v1/management/dump/Dumps/Delete][%d] deleteDumpOk %+v", 200, o.Payload) +} + +func (o *DeleteDumpOK) GetPayload() interface{} { + return o.Payload +} + +func (o *DeleteDumpOK) 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 +} + +// NewDeleteDumpDefault creates a DeleteDumpDefault with default headers values +func NewDeleteDumpDefault(code int) *DeleteDumpDefault { + return &DeleteDumpDefault{ + _statusCode: code, + } +} + +/* +DeleteDumpDefault describes a response with status code -1, with default header values. + +An unexpected error response. +*/ +type DeleteDumpDefault struct { + _statusCode int + + Payload *DeleteDumpDefaultBody +} + +// Code gets the status code for the delete dump default response +func (o *DeleteDumpDefault) Code() int { + return o._statusCode +} + +func (o *DeleteDumpDefault) Error() string { + return fmt.Sprintf("[POST /v1/management/dump/Dumps/Delete][%d] DeleteDump default %+v", o._statusCode, o.Payload) +} + +func (o *DeleteDumpDefault) GetPayload() *DeleteDumpDefaultBody { + return o.Payload +} + +func (o *DeleteDumpDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + o.Payload = new(DeleteDumpDefaultBody) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +/* +DeleteDumpBody delete dump body +swagger:model DeleteDumpBody +*/ +type DeleteDumpBody struct { + // dump ids + DumpIds []string `json:"dump_ids"` +} + +// Validate validates this delete dump body +func (o *DeleteDumpBody) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this delete dump body based on context it is used +func (o *DeleteDumpBody) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (o *DeleteDumpBody) MarshalBinary() ([]byte, error) { + if o == nil { + return nil, nil + } + return swag.WriteJSON(o) +} + +// UnmarshalBinary interface implementation +func (o *DeleteDumpBody) UnmarshalBinary(b []byte) error { + var res DeleteDumpBody + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *o = res + return nil +} + +/* +DeleteDumpDefaultBody delete dump default body +swagger:model DeleteDumpDefaultBody +*/ +type DeleteDumpDefaultBody struct { + // code + Code int32 `json:"code,omitempty"` + + // message + Message string `json:"message,omitempty"` + + // details + Details []*DeleteDumpDefaultBodyDetailsItems0 `json:"details"` +} + +// Validate validates this delete dump default body +func (o *DeleteDumpDefaultBody) 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 *DeleteDumpDefaultBody) 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("DeleteDump default" + "." + "details" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("DeleteDump default" + "." + "details" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// ContextValidate validate this delete dump default body based on the context it is used +func (o *DeleteDumpDefaultBody) 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 *DeleteDumpDefaultBody) 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("DeleteDump default" + "." + "details" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("DeleteDump default" + "." + "details" + "." + strconv.Itoa(i)) + } + return err + } + } + } + + return nil +} + +// MarshalBinary interface implementation +func (o *DeleteDumpDefaultBody) MarshalBinary() ([]byte, error) { + if o == nil { + return nil, nil + } + return swag.WriteJSON(o) +} + +// UnmarshalBinary interface implementation +func (o *DeleteDumpDefaultBody) UnmarshalBinary(b []byte) error { + var res DeleteDumpDefaultBody + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *o = res + return nil +} + +/* +DeleteDumpDefaultBodyDetailsItems0 delete dump default body details items0 +swagger:model DeleteDumpDefaultBodyDetailsItems0 +*/ +type DeleteDumpDefaultBodyDetailsItems0 struct { + // at type + AtType string `json:"@type,omitempty"` +} + +// Validate validates this delete dump default body details items0 +func (o *DeleteDumpDefaultBodyDetailsItems0) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this delete dump default body details items0 based on context it is used +func (o *DeleteDumpDefaultBodyDetailsItems0) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (o *DeleteDumpDefaultBodyDetailsItems0) MarshalBinary() ([]byte, error) { + if o == nil { + return nil, nil + } + return swag.WriteJSON(o) +} + +// UnmarshalBinary interface implementation +func (o *DeleteDumpDefaultBodyDetailsItems0) UnmarshalBinary(b []byte) error { + var res DeleteDumpDefaultBodyDetailsItems0 + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *o = res + return nil +} diff --git a/api/managementpb/dump/json/client/dumps/dumps_client.go b/api/managementpb/dump/json/client/dumps/dumps_client.go new file mode 100644 index 0000000000..b0b7dea6e1 --- /dev/null +++ b/api/managementpb/dump/json/client/dumps/dumps_client.go @@ -0,0 +1,232 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package dumps + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" +) + +// New creates a new dumps API client. +func New(transport runtime.ClientTransport, formats strfmt.Registry) ClientService { + return &Client{transport: transport, formats: formats} +} + +/* +Client for dumps API +*/ +type Client struct { + transport runtime.ClientTransport + formats strfmt.Registry +} + +// ClientOption is the option for Client methods +type ClientOption func(*runtime.ClientOperation) + +// ClientService is the interface for Client methods +type ClientService interface { + DeleteDump(params *DeleteDumpParams, opts ...ClientOption) (*DeleteDumpOK, error) + + GetDumpLogs(params *GetDumpLogsParams, opts ...ClientOption) (*GetDumpLogsOK, error) + + ListDumps(params *ListDumpsParams, opts ...ClientOption) (*ListDumpsOK, error) + + StartDump(params *StartDumpParams, opts ...ClientOption) (*StartDumpOK, error) + + UploadDump(params *UploadDumpParams, opts ...ClientOption) (*UploadDumpOK, error) + + SetTransport(transport runtime.ClientTransport) +} + +/* +DeleteDump deletes dump deletes specified pmm dump +*/ +func (a *Client) DeleteDump(params *DeleteDumpParams, opts ...ClientOption) (*DeleteDumpOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewDeleteDumpParams() + } + op := &runtime.ClientOperation{ + ID: "DeleteDump", + Method: "POST", + PathPattern: "/v1/management/dump/Dumps/Delete", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json"}, + Schemes: []string{"http", "https"}, + Params: params, + Reader: &DeleteDumpReader{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.(*DeleteDumpOK) + if ok { + return success, nil + } + // unexpected success response + unexpectedSuccess := result.(*DeleteDumpDefault) + return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) +} + +/* +GetDumpLogs gets logs returns logs from pmm dump tool +*/ +func (a *Client) GetDumpLogs(params *GetDumpLogsParams, opts ...ClientOption) (*GetDumpLogsOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewGetDumpLogsParams() + } + op := &runtime.ClientOperation{ + ID: "GetDumpLogs", + Method: "POST", + PathPattern: "/v1/management/dump/Dumps/GetLogs", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json"}, + Schemes: []string{"http", "https"}, + Params: params, + Reader: &GetDumpLogsReader{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.(*GetDumpLogsOK) + if ok { + return success, nil + } + // unexpected success response + unexpectedSuccess := result.(*GetDumpLogsDefault) + return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) +} + +/* +ListDumps lists dumps returns a list of all pmm dumps +*/ +func (a *Client) ListDumps(params *ListDumpsParams, opts ...ClientOption) (*ListDumpsOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewListDumpsParams() + } + op := &runtime.ClientOperation{ + ID: "ListDumps", + Method: "POST", + PathPattern: "/v1/management/dump/Dumps/List", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json"}, + Schemes: []string{"http", "https"}, + Params: params, + Reader: &ListDumpsReader{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.(*ListDumpsOK) + if ok { + return success, nil + } + // unexpected success response + unexpectedSuccess := result.(*ListDumpsDefault) + return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) +} + +/* +StartDump starts dump request creates pmm dump +*/ +func (a *Client) StartDump(params *StartDumpParams, opts ...ClientOption) (*StartDumpOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewStartDumpParams() + } + op := &runtime.ClientOperation{ + ID: "StartDump", + Method: "POST", + PathPattern: "/v1/management/dump/Dumps/Start", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json"}, + Schemes: []string{"http", "https"}, + Params: params, + Reader: &StartDumpReader{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.(*StartDumpOK) + if ok { + return success, nil + } + // unexpected success response + unexpectedSuccess := result.(*StartDumpDefault) + return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) +} + +/* +UploadDump uploads dump uploads selected dumps to remote server +*/ +func (a *Client) UploadDump(params *UploadDumpParams, opts ...ClientOption) (*UploadDumpOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewUploadDumpParams() + } + op := &runtime.ClientOperation{ + ID: "UploadDump", + Method: "POST", + PathPattern: "/v1/management/dump/Dumps/Upload", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json"}, + Schemes: []string{"http", "https"}, + Params: params, + Reader: &UploadDumpReader{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.(*UploadDumpOK) + if ok { + return success, nil + } + // unexpected success response + unexpectedSuccess := result.(*UploadDumpDefault) + return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) +} + +// SetTransport changes the transport on the client +func (a *Client) SetTransport(transport runtime.ClientTransport) { + a.transport = transport +} diff --git a/api/managementpb/dump/json/client/dumps/get_dump_logs_parameters.go b/api/managementpb/dump/json/client/dumps/get_dump_logs_parameters.go new file mode 100644 index 0000000000..5664df29c3 --- /dev/null +++ b/api/managementpb/dump/json/client/dumps/get_dump_logs_parameters.go @@ -0,0 +1,144 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package dumps + +// 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" +) + +// NewGetDumpLogsParams creates a new GetDumpLogsParams 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 NewGetDumpLogsParams() *GetDumpLogsParams { + return &GetDumpLogsParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewGetDumpLogsParamsWithTimeout creates a new GetDumpLogsParams object +// with the ability to set a timeout on a request. +func NewGetDumpLogsParamsWithTimeout(timeout time.Duration) *GetDumpLogsParams { + return &GetDumpLogsParams{ + timeout: timeout, + } +} + +// NewGetDumpLogsParamsWithContext creates a new GetDumpLogsParams object +// with the ability to set a context for a request. +func NewGetDumpLogsParamsWithContext(ctx context.Context) *GetDumpLogsParams { + return &GetDumpLogsParams{ + Context: ctx, + } +} + +// NewGetDumpLogsParamsWithHTTPClient creates a new GetDumpLogsParams object +// with the ability to set a custom HTTPClient for a request. +func NewGetDumpLogsParamsWithHTTPClient(client *http.Client) *GetDumpLogsParams { + return &GetDumpLogsParams{ + HTTPClient: client, + } +} + +/* +GetDumpLogsParams contains all the parameters to send to the API endpoint + + for the get dump logs operation. + + Typically these are written to a http.Request. +*/ +type GetDumpLogsParams struct { + // Body. + Body GetDumpLogsBody + + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the get dump logs params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *GetDumpLogsParams) WithDefaults() *GetDumpLogsParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the get dump logs params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *GetDumpLogsParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the get dump logs params +func (o *GetDumpLogsParams) WithTimeout(timeout time.Duration) *GetDumpLogsParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the get dump logs params +func (o *GetDumpLogsParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the get dump logs params +func (o *GetDumpLogsParams) WithContext(ctx context.Context) *GetDumpLogsParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the get dump logs params +func (o *GetDumpLogsParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the get dump logs params +func (o *GetDumpLogsParams) WithHTTPClient(client *http.Client) *GetDumpLogsParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the get dump logs params +func (o *GetDumpLogsParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WithBody adds the body to the get dump logs params +func (o *GetDumpLogsParams) WithBody(body GetDumpLogsBody) *GetDumpLogsParams { + o.SetBody(body) + return o +} + +// SetBody adds the body to the get dump logs params +func (o *GetDumpLogsParams) SetBody(body GetDumpLogsBody) { + o.Body = body +} + +// WriteToRequest writes these params to a swagger request +func (o *GetDumpLogsParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + if err := r.SetBodyParam(o.Body); err != nil { + return err + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/api/managementpb/dump/json/client/dumps/get_dump_logs_responses.go b/api/managementpb/dump/json/client/dumps/get_dump_logs_responses.go new file mode 100644 index 0000000000..b1bbb6db80 --- /dev/null +++ b/api/managementpb/dump/json/client/dumps/get_dump_logs_responses.go @@ -0,0 +1,444 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package dumps + +// 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" +) + +// GetDumpLogsReader is a Reader for the GetDumpLogs structure. +type GetDumpLogsReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *GetDumpLogsReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewGetDumpLogsOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + default: + result := NewGetDumpLogsDefault(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 + } +} + +// NewGetDumpLogsOK creates a GetDumpLogsOK with default headers values +func NewGetDumpLogsOK() *GetDumpLogsOK { + return &GetDumpLogsOK{} +} + +/* +GetDumpLogsOK describes a response with status code 200, with default header values. + +A successful response. +*/ +type GetDumpLogsOK struct { + Payload *GetDumpLogsOKBody +} + +func (o *GetDumpLogsOK) Error() string { + return fmt.Sprintf("[POST /v1/management/dump/Dumps/GetLogs][%d] getDumpLogsOk %+v", 200, o.Payload) +} + +func (o *GetDumpLogsOK) GetPayload() *GetDumpLogsOKBody { + return o.Payload +} + +func (o *GetDumpLogsOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + o.Payload = new(GetDumpLogsOKBody) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewGetDumpLogsDefault creates a GetDumpLogsDefault with default headers values +func NewGetDumpLogsDefault(code int) *GetDumpLogsDefault { + return &GetDumpLogsDefault{ + _statusCode: code, + } +} + +/* +GetDumpLogsDefault describes a response with status code -1, with default header values. + +An unexpected error response. +*/ +type GetDumpLogsDefault struct { + _statusCode int + + Payload *GetDumpLogsDefaultBody +} + +// Code gets the status code for the get dump logs default response +func (o *GetDumpLogsDefault) Code() int { + return o._statusCode +} + +func (o *GetDumpLogsDefault) Error() string { + return fmt.Sprintf("[POST /v1/management/dump/Dumps/GetLogs][%d] GetDumpLogs default %+v", o._statusCode, o.Payload) +} + +func (o *GetDumpLogsDefault) GetPayload() *GetDumpLogsDefaultBody { + return o.Payload +} + +func (o *GetDumpLogsDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + o.Payload = new(GetDumpLogsDefaultBody) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +/* +GetDumpLogsBody get dump logs body +swagger:model GetDumpLogsBody +*/ +type GetDumpLogsBody struct { + // dump id + DumpID string `json:"dump_id,omitempty"` + + // offset + Offset int64 `json:"offset,omitempty"` + + // limit + Limit int64 `json:"limit,omitempty"` +} + +// Validate validates this get dump logs body +func (o *GetDumpLogsBody) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this get dump logs body based on context it is used +func (o *GetDumpLogsBody) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (o *GetDumpLogsBody) MarshalBinary() ([]byte, error) { + if o == nil { + return nil, nil + } + return swag.WriteJSON(o) +} + +// UnmarshalBinary interface implementation +func (o *GetDumpLogsBody) UnmarshalBinary(b []byte) error { + var res GetDumpLogsBody + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *o = res + return nil +} + +/* +GetDumpLogsDefaultBody get dump logs default body +swagger:model GetDumpLogsDefaultBody +*/ +type GetDumpLogsDefaultBody struct { + // code + Code int32 `json:"code,omitempty"` + + // message + Message string `json:"message,omitempty"` + + // details + Details []*GetDumpLogsDefaultBodyDetailsItems0 `json:"details"` +} + +// Validate validates this get dump logs default body +func (o *GetDumpLogsDefaultBody) 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 *GetDumpLogsDefaultBody) 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("GetDumpLogs default" + "." + "details" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("GetDumpLogs default" + "." + "details" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// ContextValidate validate this get dump logs default body based on the context it is used +func (o *GetDumpLogsDefaultBody) 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 *GetDumpLogsDefaultBody) 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("GetDumpLogs default" + "." + "details" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("GetDumpLogs default" + "." + "details" + "." + strconv.Itoa(i)) + } + return err + } + } + } + + return nil +} + +// MarshalBinary interface implementation +func (o *GetDumpLogsDefaultBody) MarshalBinary() ([]byte, error) { + if o == nil { + return nil, nil + } + return swag.WriteJSON(o) +} + +// UnmarshalBinary interface implementation +func (o *GetDumpLogsDefaultBody) UnmarshalBinary(b []byte) error { + var res GetDumpLogsDefaultBody + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *o = res + return nil +} + +/* +GetDumpLogsDefaultBodyDetailsItems0 get dump logs default body details items0 +swagger:model GetDumpLogsDefaultBodyDetailsItems0 +*/ +type GetDumpLogsDefaultBodyDetailsItems0 struct { + // at type + AtType string `json:"@type,omitempty"` +} + +// Validate validates this get dump logs default body details items0 +func (o *GetDumpLogsDefaultBodyDetailsItems0) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this get dump logs default body details items0 based on context it is used +func (o *GetDumpLogsDefaultBodyDetailsItems0) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (o *GetDumpLogsDefaultBodyDetailsItems0) MarshalBinary() ([]byte, error) { + if o == nil { + return nil, nil + } + return swag.WriteJSON(o) +} + +// UnmarshalBinary interface implementation +func (o *GetDumpLogsDefaultBodyDetailsItems0) UnmarshalBinary(b []byte) error { + var res GetDumpLogsDefaultBodyDetailsItems0 + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *o = res + return nil +} + +/* +GetDumpLogsOKBody get dump logs OK body +swagger:model GetDumpLogsOKBody +*/ +type GetDumpLogsOKBody struct { + // logs + Logs []*GetDumpLogsOKBodyLogsItems0 `json:"logs"` + + // end + End bool `json:"end,omitempty"` +} + +// Validate validates this get dump logs OK body +func (o *GetDumpLogsOKBody) Validate(formats strfmt.Registry) error { + var res []error + + if err := o.validateLogs(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (o *GetDumpLogsOKBody) validateLogs(formats strfmt.Registry) error { + if swag.IsZero(o.Logs) { // not required + return nil + } + + for i := 0; i < len(o.Logs); i++ { + if swag.IsZero(o.Logs[i]) { // not required + continue + } + + if o.Logs[i] != nil { + if err := o.Logs[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("getDumpLogsOk" + "." + "logs" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("getDumpLogsOk" + "." + "logs" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// ContextValidate validate this get dump logs OK body based on the context it is used +func (o *GetDumpLogsOKBody) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := o.contextValidateLogs(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (o *GetDumpLogsOKBody) contextValidateLogs(ctx context.Context, formats strfmt.Registry) error { + for i := 0; i < len(o.Logs); i++ { + if o.Logs[i] != nil { + if err := o.Logs[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("getDumpLogsOk" + "." + "logs" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("getDumpLogsOk" + "." + "logs" + "." + strconv.Itoa(i)) + } + return err + } + } + } + + return nil +} + +// MarshalBinary interface implementation +func (o *GetDumpLogsOKBody) MarshalBinary() ([]byte, error) { + if o == nil { + return nil, nil + } + return swag.WriteJSON(o) +} + +// UnmarshalBinary interface implementation +func (o *GetDumpLogsOKBody) UnmarshalBinary(b []byte) error { + var res GetDumpLogsOKBody + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *o = res + return nil +} + +/* +GetDumpLogsOKBodyLogsItems0 LogChunk represent one chunk of logs. +swagger:model GetDumpLogsOKBodyLogsItems0 +*/ +type GetDumpLogsOKBodyLogsItems0 struct { + // chunk id + ChunkID int64 `json:"chunk_id,omitempty"` + + // data + Data string `json:"data,omitempty"` +} + +// Validate validates this get dump logs OK body logs items0 +func (o *GetDumpLogsOKBodyLogsItems0) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this get dump logs OK body logs items0 based on context it is used +func (o *GetDumpLogsOKBodyLogsItems0) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (o *GetDumpLogsOKBodyLogsItems0) MarshalBinary() ([]byte, error) { + if o == nil { + return nil, nil + } + return swag.WriteJSON(o) +} + +// UnmarshalBinary interface implementation +func (o *GetDumpLogsOKBodyLogsItems0) UnmarshalBinary(b []byte) error { + var res GetDumpLogsOKBodyLogsItems0 + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *o = res + return nil +} diff --git a/api/managementpb/dump/json/client/dumps/list_dumps_parameters.go b/api/managementpb/dump/json/client/dumps/list_dumps_parameters.go new file mode 100644 index 0000000000..4963d5c3d2 --- /dev/null +++ b/api/managementpb/dump/json/client/dumps/list_dumps_parameters.go @@ -0,0 +1,146 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package dumps + +// 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" +) + +// NewListDumpsParams creates a new ListDumpsParams 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 NewListDumpsParams() *ListDumpsParams { + return &ListDumpsParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewListDumpsParamsWithTimeout creates a new ListDumpsParams object +// with the ability to set a timeout on a request. +func NewListDumpsParamsWithTimeout(timeout time.Duration) *ListDumpsParams { + return &ListDumpsParams{ + timeout: timeout, + } +} + +// NewListDumpsParamsWithContext creates a new ListDumpsParams object +// with the ability to set a context for a request. +func NewListDumpsParamsWithContext(ctx context.Context) *ListDumpsParams { + return &ListDumpsParams{ + Context: ctx, + } +} + +// NewListDumpsParamsWithHTTPClient creates a new ListDumpsParams object +// with the ability to set a custom HTTPClient for a request. +func NewListDumpsParamsWithHTTPClient(client *http.Client) *ListDumpsParams { + return &ListDumpsParams{ + HTTPClient: client, + } +} + +/* +ListDumpsParams contains all the parameters to send to the API endpoint + + for the list dumps operation. + + Typically these are written to a http.Request. +*/ +type ListDumpsParams struct { + // Body. + Body interface{} + + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the list dumps params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *ListDumpsParams) WithDefaults() *ListDumpsParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the list dumps params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *ListDumpsParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the list dumps params +func (o *ListDumpsParams) WithTimeout(timeout time.Duration) *ListDumpsParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the list dumps params +func (o *ListDumpsParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the list dumps params +func (o *ListDumpsParams) WithContext(ctx context.Context) *ListDumpsParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the list dumps params +func (o *ListDumpsParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the list dumps params +func (o *ListDumpsParams) WithHTTPClient(client *http.Client) *ListDumpsParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the list dumps params +func (o *ListDumpsParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WithBody adds the body to the list dumps params +func (o *ListDumpsParams) WithBody(body interface{}) *ListDumpsParams { + o.SetBody(body) + return o +} + +// SetBody adds the body to the list dumps params +func (o *ListDumpsParams) SetBody(body interface{}) { + o.Body = body +} + +// WriteToRequest writes these params to a swagger request +func (o *ListDumpsParams) 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/managementpb/dump/json/client/dumps/list_dumps_responses.go b/api/managementpb/dump/json/client/dumps/list_dumps_responses.go new file mode 100644 index 0000000000..3cdce74f01 --- /dev/null +++ b/api/managementpb/dump/json/client/dumps/list_dumps_responses.go @@ -0,0 +1,521 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package dumps + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "encoding/json" + "fmt" + "io" + "strconv" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// ListDumpsReader is a Reader for the ListDumps structure. +type ListDumpsReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *ListDumpsReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewListDumpsOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + default: + result := NewListDumpsDefault(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 + } +} + +// NewListDumpsOK creates a ListDumpsOK with default headers values +func NewListDumpsOK() *ListDumpsOK { + return &ListDumpsOK{} +} + +/* +ListDumpsOK describes a response with status code 200, with default header values. + +A successful response. +*/ +type ListDumpsOK struct { + Payload *ListDumpsOKBody +} + +func (o *ListDumpsOK) Error() string { + return fmt.Sprintf("[POST /v1/management/dump/Dumps/List][%d] listDumpsOk %+v", 200, o.Payload) +} + +func (o *ListDumpsOK) GetPayload() *ListDumpsOKBody { + return o.Payload +} + +func (o *ListDumpsOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + o.Payload = new(ListDumpsOKBody) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewListDumpsDefault creates a ListDumpsDefault with default headers values +func NewListDumpsDefault(code int) *ListDumpsDefault { + return &ListDumpsDefault{ + _statusCode: code, + } +} + +/* +ListDumpsDefault describes a response with status code -1, with default header values. + +An unexpected error response. +*/ +type ListDumpsDefault struct { + _statusCode int + + Payload *ListDumpsDefaultBody +} + +// Code gets the status code for the list dumps default response +func (o *ListDumpsDefault) Code() int { + return o._statusCode +} + +func (o *ListDumpsDefault) Error() string { + return fmt.Sprintf("[POST /v1/management/dump/Dumps/List][%d] ListDumps default %+v", o._statusCode, o.Payload) +} + +func (o *ListDumpsDefault) GetPayload() *ListDumpsDefaultBody { + return o.Payload +} + +func (o *ListDumpsDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + o.Payload = new(ListDumpsDefaultBody) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +/* +ListDumpsDefaultBody list dumps default body +swagger:model ListDumpsDefaultBody +*/ +type ListDumpsDefaultBody struct { + // code + Code int32 `json:"code,omitempty"` + + // message + Message string `json:"message,omitempty"` + + // details + Details []*ListDumpsDefaultBodyDetailsItems0 `json:"details"` +} + +// Validate validates this list dumps default body +func (o *ListDumpsDefaultBody) 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 *ListDumpsDefaultBody) 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("ListDumps default" + "." + "details" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("ListDumps default" + "." + "details" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// ContextValidate validate this list dumps default body based on the context it is used +func (o *ListDumpsDefaultBody) 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 *ListDumpsDefaultBody) 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("ListDumps default" + "." + "details" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("ListDumps default" + "." + "details" + "." + strconv.Itoa(i)) + } + return err + } + } + } + + return nil +} + +// MarshalBinary interface implementation +func (o *ListDumpsDefaultBody) MarshalBinary() ([]byte, error) { + if o == nil { + return nil, nil + } + return swag.WriteJSON(o) +} + +// UnmarshalBinary interface implementation +func (o *ListDumpsDefaultBody) UnmarshalBinary(b []byte) error { + var res ListDumpsDefaultBody + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *o = res + return nil +} + +/* +ListDumpsDefaultBodyDetailsItems0 list dumps default body details items0 +swagger:model ListDumpsDefaultBodyDetailsItems0 +*/ +type ListDumpsDefaultBodyDetailsItems0 struct { + // at type + AtType string `json:"@type,omitempty"` +} + +// Validate validates this list dumps default body details items0 +func (o *ListDumpsDefaultBodyDetailsItems0) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this list dumps default body details items0 based on context it is used +func (o *ListDumpsDefaultBodyDetailsItems0) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (o *ListDumpsDefaultBodyDetailsItems0) MarshalBinary() ([]byte, error) { + if o == nil { + return nil, nil + } + return swag.WriteJSON(o) +} + +// UnmarshalBinary interface implementation +func (o *ListDumpsDefaultBodyDetailsItems0) UnmarshalBinary(b []byte) error { + var res ListDumpsDefaultBodyDetailsItems0 + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *o = res + return nil +} + +/* +ListDumpsOKBody list dumps OK body +swagger:model ListDumpsOKBody +*/ +type ListDumpsOKBody struct { + // dumps + Dumps []*ListDumpsOKBodyDumpsItems0 `json:"dumps"` +} + +// Validate validates this list dumps OK body +func (o *ListDumpsOKBody) Validate(formats strfmt.Registry) error { + var res []error + + if err := o.validateDumps(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (o *ListDumpsOKBody) validateDumps(formats strfmt.Registry) error { + if swag.IsZero(o.Dumps) { // not required + return nil + } + + for i := 0; i < len(o.Dumps); i++ { + if swag.IsZero(o.Dumps[i]) { // not required + continue + } + + if o.Dumps[i] != nil { + if err := o.Dumps[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("listDumpsOk" + "." + "dumps" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("listDumpsOk" + "." + "dumps" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// ContextValidate validate this list dumps OK body based on the context it is used +func (o *ListDumpsOKBody) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := o.contextValidateDumps(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (o *ListDumpsOKBody) contextValidateDumps(ctx context.Context, formats strfmt.Registry) error { + for i := 0; i < len(o.Dumps); i++ { + if o.Dumps[i] != nil { + if err := o.Dumps[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("listDumpsOk" + "." + "dumps" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("listDumpsOk" + "." + "dumps" + "." + strconv.Itoa(i)) + } + return err + } + } + } + + return nil +} + +// MarshalBinary interface implementation +func (o *ListDumpsOKBody) MarshalBinary() ([]byte, error) { + if o == nil { + return nil, nil + } + return swag.WriteJSON(o) +} + +// UnmarshalBinary interface implementation +func (o *ListDumpsOKBody) UnmarshalBinary(b []byte) error { + var res ListDumpsOKBody + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *o = res + return nil +} + +/* +ListDumpsOKBodyDumpsItems0 list dumps OK body dumps items0 +swagger:model ListDumpsOKBodyDumpsItems0 +*/ +type ListDumpsOKBodyDumpsItems0 struct { + // dump id + DumpID string `json:"dump_id,omitempty"` + + // status + // Enum: [DUMP_STATUS_INVALID DUMP_STATUS_IN_PROGRESS DUMP_STATUS_SUCCESS DUMP_STATUS_ERROR] + Status *string `json:"status,omitempty"` + + // service names + ServiceNames []string `json:"service_names"` + + // start time + // Format: date-time + StartTime strfmt.DateTime `json:"start_time,omitempty"` + + // end time + // Format: date-time + EndTime strfmt.DateTime `json:"end_time,omitempty"` + + // created at + // Format: date-time + CreatedAt strfmt.DateTime `json:"created_at,omitempty"` +} + +// Validate validates this list dumps OK body dumps items0 +func (o *ListDumpsOKBodyDumpsItems0) Validate(formats strfmt.Registry) error { + var res []error + + if err := o.validateStatus(formats); err != nil { + res = append(res, err) + } + + if err := o.validateStartTime(formats); err != nil { + res = append(res, err) + } + + if err := o.validateEndTime(formats); err != nil { + res = append(res, err) + } + + if err := o.validateCreatedAt(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +var listDumpsOkBodyDumpsItems0TypeStatusPropEnum []interface{} + +func init() { + var res []string + if err := json.Unmarshal([]byte(`["DUMP_STATUS_INVALID","DUMP_STATUS_IN_PROGRESS","DUMP_STATUS_SUCCESS","DUMP_STATUS_ERROR"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + listDumpsOkBodyDumpsItems0TypeStatusPropEnum = append(listDumpsOkBodyDumpsItems0TypeStatusPropEnum, v) + } +} + +const ( + + // ListDumpsOKBodyDumpsItems0StatusDUMPSTATUSINVALID captures enum value "DUMP_STATUS_INVALID" + ListDumpsOKBodyDumpsItems0StatusDUMPSTATUSINVALID string = "DUMP_STATUS_INVALID" + + // ListDumpsOKBodyDumpsItems0StatusDUMPSTATUSINPROGRESS captures enum value "DUMP_STATUS_IN_PROGRESS" + ListDumpsOKBodyDumpsItems0StatusDUMPSTATUSINPROGRESS string = "DUMP_STATUS_IN_PROGRESS" + + // ListDumpsOKBodyDumpsItems0StatusDUMPSTATUSSUCCESS captures enum value "DUMP_STATUS_SUCCESS" + ListDumpsOKBodyDumpsItems0StatusDUMPSTATUSSUCCESS string = "DUMP_STATUS_SUCCESS" + + // ListDumpsOKBodyDumpsItems0StatusDUMPSTATUSERROR captures enum value "DUMP_STATUS_ERROR" + ListDumpsOKBodyDumpsItems0StatusDUMPSTATUSERROR string = "DUMP_STATUS_ERROR" +) + +// prop value enum +func (o *ListDumpsOKBodyDumpsItems0) validateStatusEnum(path, location string, value string) error { + if err := validate.EnumCase(path, location, value, listDumpsOkBodyDumpsItems0TypeStatusPropEnum, true); err != nil { + return err + } + return nil +} + +func (o *ListDumpsOKBodyDumpsItems0) validateStatus(formats strfmt.Registry) error { + if swag.IsZero(o.Status) { // not required + return nil + } + + // value enum + if err := o.validateStatusEnum("status", "body", *o.Status); err != nil { + return err + } + + return nil +} + +func (o *ListDumpsOKBodyDumpsItems0) validateStartTime(formats strfmt.Registry) error { + if swag.IsZero(o.StartTime) { // not required + return nil + } + + if err := validate.FormatOf("start_time", "body", "date-time", o.StartTime.String(), formats); err != nil { + return err + } + + return nil +} + +func (o *ListDumpsOKBodyDumpsItems0) validateEndTime(formats strfmt.Registry) error { + if swag.IsZero(o.EndTime) { // not required + return nil + } + + if err := validate.FormatOf("end_time", "body", "date-time", o.EndTime.String(), formats); err != nil { + return err + } + + return nil +} + +func (o *ListDumpsOKBodyDumpsItems0) validateCreatedAt(formats strfmt.Registry) error { + if swag.IsZero(o.CreatedAt) { // not required + return nil + } + + if err := validate.FormatOf("created_at", "body", "date-time", o.CreatedAt.String(), formats); err != nil { + return err + } + + return nil +} + +// ContextValidate validates this list dumps OK body dumps items0 based on context it is used +func (o *ListDumpsOKBodyDumpsItems0) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (o *ListDumpsOKBodyDumpsItems0) MarshalBinary() ([]byte, error) { + if o == nil { + return nil, nil + } + return swag.WriteJSON(o) +} + +// UnmarshalBinary interface implementation +func (o *ListDumpsOKBodyDumpsItems0) UnmarshalBinary(b []byte) error { + var res ListDumpsOKBodyDumpsItems0 + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *o = res + return nil +} diff --git a/api/managementpb/dump/json/client/dumps/start_dump_parameters.go b/api/managementpb/dump/json/client/dumps/start_dump_parameters.go new file mode 100644 index 0000000000..dbf0f8a734 --- /dev/null +++ b/api/managementpb/dump/json/client/dumps/start_dump_parameters.go @@ -0,0 +1,144 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package dumps + +// 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" +) + +// NewStartDumpParams creates a new StartDumpParams 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 NewStartDumpParams() *StartDumpParams { + return &StartDumpParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewStartDumpParamsWithTimeout creates a new StartDumpParams object +// with the ability to set a timeout on a request. +func NewStartDumpParamsWithTimeout(timeout time.Duration) *StartDumpParams { + return &StartDumpParams{ + timeout: timeout, + } +} + +// NewStartDumpParamsWithContext creates a new StartDumpParams object +// with the ability to set a context for a request. +func NewStartDumpParamsWithContext(ctx context.Context) *StartDumpParams { + return &StartDumpParams{ + Context: ctx, + } +} + +// NewStartDumpParamsWithHTTPClient creates a new StartDumpParams object +// with the ability to set a custom HTTPClient for a request. +func NewStartDumpParamsWithHTTPClient(client *http.Client) *StartDumpParams { + return &StartDumpParams{ + HTTPClient: client, + } +} + +/* +StartDumpParams contains all the parameters to send to the API endpoint + + for the start dump operation. + + Typically these are written to a http.Request. +*/ +type StartDumpParams struct { + // Body. + Body StartDumpBody + + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the start dump params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *StartDumpParams) WithDefaults() *StartDumpParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the start dump params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *StartDumpParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the start dump params +func (o *StartDumpParams) WithTimeout(timeout time.Duration) *StartDumpParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the start dump params +func (o *StartDumpParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the start dump params +func (o *StartDumpParams) WithContext(ctx context.Context) *StartDumpParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the start dump params +func (o *StartDumpParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the start dump params +func (o *StartDumpParams) WithHTTPClient(client *http.Client) *StartDumpParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the start dump params +func (o *StartDumpParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WithBody adds the body to the start dump params +func (o *StartDumpParams) WithBody(body StartDumpBody) *StartDumpParams { + o.SetBody(body) + return o +} + +// SetBody adds the body to the start dump params +func (o *StartDumpParams) SetBody(body StartDumpBody) { + o.Body = body +} + +// WriteToRequest writes these params to a swagger request +func (o *StartDumpParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + if err := r.SetBodyParam(o.Body); err != nil { + return err + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/api/managementpb/dump/json/client/dumps/start_dump_responses.go b/api/managementpb/dump/json/client/dumps/start_dump_responses.go new file mode 100644 index 0000000000..3b4e95fec9 --- /dev/null +++ b/api/managementpb/dump/json/client/dumps/start_dump_responses.go @@ -0,0 +1,386 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package dumps + +// 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" + "github.com/go-openapi/validate" +) + +// StartDumpReader is a Reader for the StartDump structure. +type StartDumpReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *StartDumpReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewStartDumpOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + default: + result := NewStartDumpDefault(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 + } +} + +// NewStartDumpOK creates a StartDumpOK with default headers values +func NewStartDumpOK() *StartDumpOK { + return &StartDumpOK{} +} + +/* +StartDumpOK describes a response with status code 200, with default header values. + +A successful response. +*/ +type StartDumpOK struct { + Payload *StartDumpOKBody +} + +func (o *StartDumpOK) Error() string { + return fmt.Sprintf("[POST /v1/management/dump/Dumps/Start][%d] startDumpOk %+v", 200, o.Payload) +} + +func (o *StartDumpOK) GetPayload() *StartDumpOKBody { + return o.Payload +} + +func (o *StartDumpOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + o.Payload = new(StartDumpOKBody) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewStartDumpDefault creates a StartDumpDefault with default headers values +func NewStartDumpDefault(code int) *StartDumpDefault { + return &StartDumpDefault{ + _statusCode: code, + } +} + +/* +StartDumpDefault describes a response with status code -1, with default header values. + +An unexpected error response. +*/ +type StartDumpDefault struct { + _statusCode int + + Payload *StartDumpDefaultBody +} + +// Code gets the status code for the start dump default response +func (o *StartDumpDefault) Code() int { + return o._statusCode +} + +func (o *StartDumpDefault) Error() string { + return fmt.Sprintf("[POST /v1/management/dump/Dumps/Start][%d] StartDump default %+v", o._statusCode, o.Payload) +} + +func (o *StartDumpDefault) GetPayload() *StartDumpDefaultBody { + return o.Payload +} + +func (o *StartDumpDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + o.Payload = new(StartDumpDefaultBody) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +/* +StartDumpBody start dump body +swagger:model StartDumpBody +*/ +type StartDumpBody struct { + // service names + ServiceNames []string `json:"service_names"` + + // start time + // Format: date-time + StartTime strfmt.DateTime `json:"start_time,omitempty"` + + // end time + // Format: date-time + EndTime strfmt.DateTime `json:"end_time,omitempty"` + + // export qan + ExportQAN bool `json:"export_qan,omitempty"` + + // ignore load + IgnoreLoad bool `json:"ignore_load,omitempty"` +} + +// Validate validates this start dump body +func (o *StartDumpBody) Validate(formats strfmt.Registry) error { + var res []error + + if err := o.validateStartTime(formats); err != nil { + res = append(res, err) + } + + if err := o.validateEndTime(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (o *StartDumpBody) validateStartTime(formats strfmt.Registry) error { + if swag.IsZero(o.StartTime) { // not required + return nil + } + + if err := validate.FormatOf("body"+"."+"start_time", "body", "date-time", o.StartTime.String(), formats); err != nil { + return err + } + + return nil +} + +func (o *StartDumpBody) validateEndTime(formats strfmt.Registry) error { + if swag.IsZero(o.EndTime) { // not required + return nil + } + + if err := validate.FormatOf("body"+"."+"end_time", "body", "date-time", o.EndTime.String(), formats); err != nil { + return err + } + + return nil +} + +// ContextValidate validates this start dump body based on context it is used +func (o *StartDumpBody) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (o *StartDumpBody) MarshalBinary() ([]byte, error) { + if o == nil { + return nil, nil + } + return swag.WriteJSON(o) +} + +// UnmarshalBinary interface implementation +func (o *StartDumpBody) UnmarshalBinary(b []byte) error { + var res StartDumpBody + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *o = res + return nil +} + +/* +StartDumpDefaultBody start dump default body +swagger:model StartDumpDefaultBody +*/ +type StartDumpDefaultBody struct { + // code + Code int32 `json:"code,omitempty"` + + // message + Message string `json:"message,omitempty"` + + // details + Details []*StartDumpDefaultBodyDetailsItems0 `json:"details"` +} + +// Validate validates this start dump default body +func (o *StartDumpDefaultBody) 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 *StartDumpDefaultBody) 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("StartDump default" + "." + "details" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("StartDump default" + "." + "details" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// ContextValidate validate this start dump default body based on the context it is used +func (o *StartDumpDefaultBody) 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 *StartDumpDefaultBody) 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("StartDump default" + "." + "details" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("StartDump default" + "." + "details" + "." + strconv.Itoa(i)) + } + return err + } + } + } + + return nil +} + +// MarshalBinary interface implementation +func (o *StartDumpDefaultBody) MarshalBinary() ([]byte, error) { + if o == nil { + return nil, nil + } + return swag.WriteJSON(o) +} + +// UnmarshalBinary interface implementation +func (o *StartDumpDefaultBody) UnmarshalBinary(b []byte) error { + var res StartDumpDefaultBody + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *o = res + return nil +} + +/* +StartDumpDefaultBodyDetailsItems0 start dump default body details items0 +swagger:model StartDumpDefaultBodyDetailsItems0 +*/ +type StartDumpDefaultBodyDetailsItems0 struct { + // at type + AtType string `json:"@type,omitempty"` +} + +// Validate validates this start dump default body details items0 +func (o *StartDumpDefaultBodyDetailsItems0) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this start dump default body details items0 based on context it is used +func (o *StartDumpDefaultBodyDetailsItems0) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (o *StartDumpDefaultBodyDetailsItems0) MarshalBinary() ([]byte, error) { + if o == nil { + return nil, nil + } + return swag.WriteJSON(o) +} + +// UnmarshalBinary interface implementation +func (o *StartDumpDefaultBodyDetailsItems0) UnmarshalBinary(b []byte) error { + var res StartDumpDefaultBodyDetailsItems0 + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *o = res + return nil +} + +/* +StartDumpOKBody start dump OK body +swagger:model StartDumpOKBody +*/ +type StartDumpOKBody struct { + // dump id + DumpID string `json:"dump_id,omitempty"` +} + +// Validate validates this start dump OK body +func (o *StartDumpOKBody) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this start dump OK body based on context it is used +func (o *StartDumpOKBody) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (o *StartDumpOKBody) MarshalBinary() ([]byte, error) { + if o == nil { + return nil, nil + } + return swag.WriteJSON(o) +} + +// UnmarshalBinary interface implementation +func (o *StartDumpOKBody) UnmarshalBinary(b []byte) error { + var res StartDumpOKBody + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *o = res + return nil +} diff --git a/api/managementpb/dump/json/client/dumps/upload_dump_parameters.go b/api/managementpb/dump/json/client/dumps/upload_dump_parameters.go new file mode 100644 index 0000000000..1aa1d22c60 --- /dev/null +++ b/api/managementpb/dump/json/client/dumps/upload_dump_parameters.go @@ -0,0 +1,144 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package dumps + +// 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" +) + +// NewUploadDumpParams creates a new UploadDumpParams 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 NewUploadDumpParams() *UploadDumpParams { + return &UploadDumpParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewUploadDumpParamsWithTimeout creates a new UploadDumpParams object +// with the ability to set a timeout on a request. +func NewUploadDumpParamsWithTimeout(timeout time.Duration) *UploadDumpParams { + return &UploadDumpParams{ + timeout: timeout, + } +} + +// NewUploadDumpParamsWithContext creates a new UploadDumpParams object +// with the ability to set a context for a request. +func NewUploadDumpParamsWithContext(ctx context.Context) *UploadDumpParams { + return &UploadDumpParams{ + Context: ctx, + } +} + +// NewUploadDumpParamsWithHTTPClient creates a new UploadDumpParams object +// with the ability to set a custom HTTPClient for a request. +func NewUploadDumpParamsWithHTTPClient(client *http.Client) *UploadDumpParams { + return &UploadDumpParams{ + HTTPClient: client, + } +} + +/* +UploadDumpParams contains all the parameters to send to the API endpoint + + for the upload dump operation. + + Typically these are written to a http.Request. +*/ +type UploadDumpParams struct { + // Body. + Body UploadDumpBody + + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the upload dump params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *UploadDumpParams) WithDefaults() *UploadDumpParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the upload dump params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *UploadDumpParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the upload dump params +func (o *UploadDumpParams) WithTimeout(timeout time.Duration) *UploadDumpParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the upload dump params +func (o *UploadDumpParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the upload dump params +func (o *UploadDumpParams) WithContext(ctx context.Context) *UploadDumpParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the upload dump params +func (o *UploadDumpParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the upload dump params +func (o *UploadDumpParams) WithHTTPClient(client *http.Client) *UploadDumpParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the upload dump params +func (o *UploadDumpParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WithBody adds the body to the upload dump params +func (o *UploadDumpParams) WithBody(body UploadDumpBody) *UploadDumpParams { + o.SetBody(body) + return o +} + +// SetBody adds the body to the upload dump params +func (o *UploadDumpParams) SetBody(body UploadDumpBody) { + o.Body = body +} + +// WriteToRequest writes these params to a swagger request +func (o *UploadDumpParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + if err := r.SetBodyParam(o.Body); err != nil { + return err + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/api/managementpb/dump/json/client/dumps/upload_dump_responses.go b/api/managementpb/dump/json/client/dumps/upload_dump_responses.go new file mode 100644 index 0000000000..77372e9d54 --- /dev/null +++ b/api/managementpb/dump/json/client/dumps/upload_dump_responses.go @@ -0,0 +1,396 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package dumps + +// 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" +) + +// UploadDumpReader is a Reader for the UploadDump structure. +type UploadDumpReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *UploadDumpReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewUploadDumpOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + default: + result := NewUploadDumpDefault(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 + } +} + +// NewUploadDumpOK creates a UploadDumpOK with default headers values +func NewUploadDumpOK() *UploadDumpOK { + return &UploadDumpOK{} +} + +/* +UploadDumpOK describes a response with status code 200, with default header values. + +A successful response. +*/ +type UploadDumpOK struct { + Payload interface{} +} + +func (o *UploadDumpOK) Error() string { + return fmt.Sprintf("[POST /v1/management/dump/Dumps/Upload][%d] uploadDumpOk %+v", 200, o.Payload) +} + +func (o *UploadDumpOK) GetPayload() interface{} { + return o.Payload +} + +func (o *UploadDumpOK) 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 +} + +// NewUploadDumpDefault creates a UploadDumpDefault with default headers values +func NewUploadDumpDefault(code int) *UploadDumpDefault { + return &UploadDumpDefault{ + _statusCode: code, + } +} + +/* +UploadDumpDefault describes a response with status code -1, with default header values. + +An unexpected error response. +*/ +type UploadDumpDefault struct { + _statusCode int + + Payload *UploadDumpDefaultBody +} + +// Code gets the status code for the upload dump default response +func (o *UploadDumpDefault) Code() int { + return o._statusCode +} + +func (o *UploadDumpDefault) Error() string { + return fmt.Sprintf("[POST /v1/management/dump/Dumps/Upload][%d] UploadDump default %+v", o._statusCode, o.Payload) +} + +func (o *UploadDumpDefault) GetPayload() *UploadDumpDefaultBody { + return o.Payload +} + +func (o *UploadDumpDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + o.Payload = new(UploadDumpDefaultBody) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +/* +UploadDumpBody upload dump body +swagger:model UploadDumpBody +*/ +type UploadDumpBody struct { + // dump ids + DumpIds []string `json:"dump_ids"` + + // sftp parameters + SftpParameters *UploadDumpParamsBodySftpParameters `json:"sftp_parameters,omitempty"` +} + +// Validate validates this upload dump body +func (o *UploadDumpBody) Validate(formats strfmt.Registry) error { + var res []error + + if err := o.validateSftpParameters(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (o *UploadDumpBody) validateSftpParameters(formats strfmt.Registry) error { + if swag.IsZero(o.SftpParameters) { // not required + return nil + } + + if o.SftpParameters != nil { + if err := o.SftpParameters.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("body" + "." + "sftp_parameters") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("body" + "." + "sftp_parameters") + } + return err + } + } + + return nil +} + +// ContextValidate validate this upload dump body based on the context it is used +func (o *UploadDumpBody) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := o.contextValidateSftpParameters(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (o *UploadDumpBody) contextValidateSftpParameters(ctx context.Context, formats strfmt.Registry) error { + if o.SftpParameters != nil { + if err := o.SftpParameters.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("body" + "." + "sftp_parameters") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("body" + "." + "sftp_parameters") + } + return err + } + } + + return nil +} + +// MarshalBinary interface implementation +func (o *UploadDumpBody) MarshalBinary() ([]byte, error) { + if o == nil { + return nil, nil + } + return swag.WriteJSON(o) +} + +// UnmarshalBinary interface implementation +func (o *UploadDumpBody) UnmarshalBinary(b []byte) error { + var res UploadDumpBody + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *o = res + return nil +} + +/* +UploadDumpDefaultBody upload dump default body +swagger:model UploadDumpDefaultBody +*/ +type UploadDumpDefaultBody struct { + // code + Code int32 `json:"code,omitempty"` + + // message + Message string `json:"message,omitempty"` + + // details + Details []*UploadDumpDefaultBodyDetailsItems0 `json:"details"` +} + +// Validate validates this upload dump default body +func (o *UploadDumpDefaultBody) 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 *UploadDumpDefaultBody) 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("UploadDump default" + "." + "details" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("UploadDump default" + "." + "details" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// ContextValidate validate this upload dump default body based on the context it is used +func (o *UploadDumpDefaultBody) 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 *UploadDumpDefaultBody) 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("UploadDump default" + "." + "details" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("UploadDump default" + "." + "details" + "." + strconv.Itoa(i)) + } + return err + } + } + } + + return nil +} + +// MarshalBinary interface implementation +func (o *UploadDumpDefaultBody) MarshalBinary() ([]byte, error) { + if o == nil { + return nil, nil + } + return swag.WriteJSON(o) +} + +// UnmarshalBinary interface implementation +func (o *UploadDumpDefaultBody) UnmarshalBinary(b []byte) error { + var res UploadDumpDefaultBody + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *o = res + return nil +} + +/* +UploadDumpDefaultBodyDetailsItems0 upload dump default body details items0 +swagger:model UploadDumpDefaultBodyDetailsItems0 +*/ +type UploadDumpDefaultBodyDetailsItems0 struct { + // at type + AtType string `json:"@type,omitempty"` +} + +// Validate validates this upload dump default body details items0 +func (o *UploadDumpDefaultBodyDetailsItems0) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this upload dump default body details items0 based on context it is used +func (o *UploadDumpDefaultBodyDetailsItems0) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (o *UploadDumpDefaultBodyDetailsItems0) MarshalBinary() ([]byte, error) { + if o == nil { + return nil, nil + } + return swag.WriteJSON(o) +} + +// UnmarshalBinary interface implementation +func (o *UploadDumpDefaultBodyDetailsItems0) UnmarshalBinary(b []byte) error { + var res UploadDumpDefaultBodyDetailsItems0 + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *o = res + return nil +} + +/* +UploadDumpParamsBodySftpParameters upload dump params body sftp parameters +swagger:model UploadDumpParamsBodySftpParameters +*/ +type UploadDumpParamsBodySftpParameters struct { + // address + Address string `json:"address,omitempty"` + + // user + User string `json:"user,omitempty"` + + // password + Password string `json:"password,omitempty"` + + // directory + Directory string `json:"directory,omitempty"` +} + +// Validate validates this upload dump params body sftp parameters +func (o *UploadDumpParamsBodySftpParameters) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this upload dump params body sftp parameters based on context it is used +func (o *UploadDumpParamsBodySftpParameters) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (o *UploadDumpParamsBodySftpParameters) MarshalBinary() ([]byte, error) { + if o == nil { + return nil, nil + } + return swag.WriteJSON(o) +} + +// UnmarshalBinary interface implementation +func (o *UploadDumpParamsBodySftpParameters) UnmarshalBinary(b []byte) error { + var res UploadDumpParamsBodySftpParameters + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *o = res + return nil +} diff --git a/api/managementpb/dump/json/client/pmm_dump_api_client.go b/api/managementpb/dump/json/client/pmm_dump_api_client.go new file mode 100644 index 0000000000..de9c0078e7 --- /dev/null +++ b/api/managementpb/dump/json/client/pmm_dump_api_client.go @@ -0,0 +1,112 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package client + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "github.com/go-openapi/runtime" + httptransport "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" + + "github.com/percona/pmm/api/managementpb/dump/json/client/dumps" +) + +// Default PMM dump API HTTP client. +var Default = NewHTTPClient(nil) + +const ( + // DefaultHost is the default Host + // found in Meta (info) section of spec file + DefaultHost string = "localhost" + // DefaultBasePath is the default BasePath + // found in Meta (info) section of spec file + DefaultBasePath string = "/" +) + +// DefaultSchemes are the default schemes found in Meta (info) section of spec file +var DefaultSchemes = []string{"http", "https"} + +// NewHTTPClient creates a new PMM dump API HTTP client. +func NewHTTPClient(formats strfmt.Registry) *PMMDumpAPI { + return NewHTTPClientWithConfig(formats, nil) +} + +// NewHTTPClientWithConfig creates a new PMM dump API HTTP client, +// using a customizable transport config. +func NewHTTPClientWithConfig(formats strfmt.Registry, cfg *TransportConfig) *PMMDumpAPI { + // ensure nullable parameters have default + if cfg == nil { + cfg = DefaultTransportConfig() + } + + // create transport and client + transport := httptransport.New(cfg.Host, cfg.BasePath, cfg.Schemes) + return New(transport, formats) +} + +// New creates a new PMM dump API client +func New(transport runtime.ClientTransport, formats strfmt.Registry) *PMMDumpAPI { + // ensure nullable parameters have default + if formats == nil { + formats = strfmt.Default + } + + cli := new(PMMDumpAPI) + cli.Transport = transport + cli.Dumps = dumps.New(transport, formats) + return cli +} + +// DefaultTransportConfig creates a TransportConfig with the +// default settings taken from the meta section of the spec file. +func DefaultTransportConfig() *TransportConfig { + return &TransportConfig{ + Host: DefaultHost, + BasePath: DefaultBasePath, + Schemes: DefaultSchemes, + } +} + +// TransportConfig contains the transport related info, +// found in the meta section of the spec file. +type TransportConfig struct { + Host string + BasePath string + Schemes []string +} + +// WithHost overrides the default host, +// provided by the meta section of the spec file. +func (cfg *TransportConfig) WithHost(host string) *TransportConfig { + cfg.Host = host + return cfg +} + +// WithBasePath overrides the default basePath, +// provided by the meta section of the spec file. +func (cfg *TransportConfig) WithBasePath(basePath string) *TransportConfig { + cfg.BasePath = basePath + return cfg +} + +// WithSchemes overrides the default schemes, +// provided by the meta section of the spec file. +func (cfg *TransportConfig) WithSchemes(schemes []string) *TransportConfig { + cfg.Schemes = schemes + return cfg +} + +// PMMDumpAPI is a client for PMM dump API +type PMMDumpAPI struct { + Dumps dumps.ClientService + + Transport runtime.ClientTransport +} + +// SetTransport changes the transport on the client and all its subresources +func (c *PMMDumpAPI) SetTransport(transport runtime.ClientTransport) { + c.Transport = transport + c.Dumps.SetTransport(transport) +} diff --git a/api/managementpb/dump/json/dump.json b/api/managementpb/dump/json/dump.json new file mode 100644 index 0000000000..9ac881873e --- /dev/null +++ b/api/managementpb/dump/json/dump.json @@ -0,0 +1,476 @@ +{ + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "https", + "http" + ], + "swagger": "2.0", + "info": { + "title": "PMM Dump API", + "version": "v1beta1" + }, + "paths": { + "/v1/management/dump/Dumps/Delete": { + "post": { + "tags": [ + "Dumps" + ], + "summary": "DeleteDump deletes specified pmm dump.", + "operationId": "DeleteDump", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "dump_ids": { + "type": "array", + "items": { + "type": "string" + }, + "x-order": 0 + } + } + } + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32", + "x-order": 0 + }, + "details": { + "type": "array", + "items": { + "type": "object", + "properties": { + "@type": { + "type": "string", + "x-order": 0 + } + }, + "additionalProperties": false + }, + "x-order": 2 + }, + "message": { + "type": "string", + "x-order": 1 + } + } + } + } + } + } + }, + "/v1/management/dump/Dumps/GetLogs": { + "post": { + "tags": [ + "Dumps" + ], + "summary": "GetLogs returns logs from pmm-dump tool.", + "operationId": "GetDumpLogs", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "dump_id": { + "type": "string", + "x-order": 0 + }, + "limit": { + "type": "integer", + "format": "int64", + "x-order": 2 + }, + "offset": { + "type": "integer", + "format": "int64", + "x-order": 1 + } + } + } + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": { + "end": { + "type": "boolean", + "x-order": 1 + }, + "logs": { + "type": "array", + "items": { + "description": "LogChunk represent one chunk of logs.", + "type": "object", + "properties": { + "chunk_id": { + "type": "integer", + "format": "int64", + "x-order": 0 + }, + "data": { + "type": "string", + "x-order": 1 + } + } + }, + "x-order": 0 + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32", + "x-order": 0 + }, + "details": { + "type": "array", + "items": { + "type": "object", + "properties": { + "@type": { + "type": "string", + "x-order": 0 + } + }, + "additionalProperties": false + }, + "x-order": 2 + }, + "message": { + "type": "string", + "x-order": 1 + } + } + } + } + } + } + }, + "/v1/management/dump/Dumps/List": { + "post": { + "tags": [ + "Dumps" + ], + "summary": "ListDumps returns a list of all pmm dumps.", + "operationId": "ListDumps", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object" + } + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": { + "dumps": { + "type": "array", + "items": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "format": "date-time", + "x-order": 5 + }, + "dump_id": { + "type": "string", + "x-order": 0 + }, + "end_time": { + "type": "string", + "format": "date-time", + "x-order": 4 + }, + "service_names": { + "type": "array", + "items": { + "type": "string" + }, + "x-order": 2 + }, + "start_time": { + "type": "string", + "format": "date-time", + "x-order": 3 + }, + "status": { + "type": "string", + "default": "DUMP_STATUS_INVALID", + "enum": [ + "DUMP_STATUS_INVALID", + "DUMP_STATUS_IN_PROGRESS", + "DUMP_STATUS_SUCCESS", + "DUMP_STATUS_ERROR" + ], + "x-order": 1 + } + } + }, + "x-order": 0 + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32", + "x-order": 0 + }, + "details": { + "type": "array", + "items": { + "type": "object", + "properties": { + "@type": { + "type": "string", + "x-order": 0 + } + }, + "additionalProperties": false + }, + "x-order": 2 + }, + "message": { + "type": "string", + "x-order": 1 + } + } + } + } + } + } + }, + "/v1/management/dump/Dumps/Start": { + "post": { + "tags": [ + "Dumps" + ], + "summary": "StartDump request creates pmm dump.", + "operationId": "StartDump", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "end_time": { + "type": "string", + "format": "date-time", + "x-order": 2 + }, + "export_qan": { + "type": "boolean", + "x-order": 3 + }, + "ignore_load": { + "type": "boolean", + "x-order": 4 + }, + "service_names": { + "type": "array", + "items": { + "type": "string" + }, + "x-order": 0 + }, + "start_time": { + "type": "string", + "format": "date-time", + "x-order": 1 + } + } + } + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": { + "dump_id": { + "type": "string", + "x-order": 0 + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32", + "x-order": 0 + }, + "details": { + "type": "array", + "items": { + "type": "object", + "properties": { + "@type": { + "type": "string", + "x-order": 0 + } + }, + "additionalProperties": false + }, + "x-order": 2 + }, + "message": { + "type": "string", + "x-order": 1 + } + } + } + } + } + } + }, + "/v1/management/dump/Dumps/Upload": { + "post": { + "tags": [ + "Dumps" + ], + "summary": "UploadDump uploads selected dumps to remote server.", + "operationId": "UploadDump", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "dump_ids": { + "type": "array", + "items": { + "type": "string" + }, + "x-order": 0 + }, + "sftp_parameters": { + "type": "object", + "properties": { + "address": { + "type": "string", + "x-order": 0 + }, + "directory": { + "type": "string", + "x-order": 3 + }, + "password": { + "type": "string", + "x-order": 2 + }, + "user": { + "type": "string", + "x-order": 1 + } + }, + "x-order": 1 + } + } + } + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32", + "x-order": 0 + }, + "details": { + "type": "array", + "items": { + "type": "object", + "properties": { + "@type": { + "type": "string", + "x-order": 0 + } + }, + "additionalProperties": false + }, + "x-order": 2 + }, + "message": { + "type": "string", + "x-order": 1 + } + } + } + } + } + } + } + }, + "tags": [ + { + "name": "Dumps" + } + ] +} \ No newline at end of file diff --git a/api/managementpb/dump/json/header.json b/api/managementpb/dump/json/header.json new file mode 100644 index 0000000000..4666b1236e --- /dev/null +++ b/api/managementpb/dump/json/header.json @@ -0,0 +1,11 @@ +{ + "swagger": "2.0", + "info": { + "title": "PMM Dump API", + "version": "v1beta1" + }, + "schemes": [ + "https", + "http" + ] +} diff --git a/api/swagger/swagger-dev.json b/api/swagger/swagger-dev.json index 1466e4107c..b42664e002 100644 --- a/api/swagger/swagger-dev.json +++ b/api/swagger/swagger-dev.json @@ -19348,7 +19348,7 @@ "Agent" ], "summary": "List Agents", - "operationId": "ListAgentsMixin10", + "operationId": "ListAgentsMixin11", "parameters": [ { "description": "Only one of the parameters below must be set.", @@ -26986,7 +26986,7 @@ "MgmtNode" ], "summary": "Get Node", - "operationId": "GetNodeMixin11", + "operationId": "GetNodeMixin12", "parameters": [ { "name": "body", @@ -27203,7 +27203,7 @@ "MgmtNode" ], "summary": "List Nodes", - "operationId": "ListNodesMixin11", + "operationId": "ListNodesMixin12", "parameters": [ { "name": "body", @@ -31316,7 +31316,7 @@ "MgmtService" ], "summary": "List Services", - "operationId": "ListServicesMixin12", + "operationId": "ListServicesMixin13", "parameters": [ { "name": "body", @@ -34985,6 +34985,458 @@ } } }, + "/v1/management/dump/Dumps/Delete": { + "post": { + "tags": [ + "Dumps" + ], + "summary": "DeleteDump deletes specified pmm dump.", + "operationId": "DeleteDump", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "dump_ids": { + "type": "array", + "items": { + "type": "string" + }, + "x-order": 0 + } + } + } + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "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": { + "type": "object", + "properties": { + "@type": { + "type": "string", + "x-order": 0 + } + }, + "additionalProperties": false + }, + "x-order": 2 + } + } + } + } + } + } + }, + "/v1/management/dump/Dumps/GetLogs": { + "post": { + "tags": [ + "Dumps" + ], + "summary": "GetLogs returns logs from pmm-dump tool.", + "operationId": "GetDumpLogs", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "dump_id": { + "type": "string", + "x-order": 0 + }, + "offset": { + "type": "integer", + "format": "int64", + "x-order": 1 + }, + "limit": { + "type": "integer", + "format": "int64", + "x-order": 2 + } + } + } + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": { + "logs": { + "type": "array", + "items": { + "description": "LogChunk represent one chunk of logs.", + "type": "object", + "properties": { + "chunk_id": { + "type": "integer", + "format": "int64", + "x-order": 0 + }, + "data": { + "type": "string", + "x-order": 1 + } + } + }, + "x-order": 0 + }, + "end": { + "type": "boolean", + "x-order": 1 + } + } + } + }, + "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": { + "type": "object", + "properties": { + "@type": { + "type": "string", + "x-order": 0 + } + }, + "additionalProperties": false + }, + "x-order": 2 + } + } + } + } + } + } + }, + "/v1/management/dump/Dumps/List": { + "post": { + "tags": [ + "Dumps" + ], + "summary": "ListDumps returns a list of all pmm dumps.", + "operationId": "ListDumps", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object" + } + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": { + "dumps": { + "type": "array", + "items": { + "type": "object", + "properties": { + "dump_id": { + "type": "string", + "x-order": 0 + }, + "status": { + "type": "string", + "default": "DUMP_STATUS_INVALID", + "enum": [ + "DUMP_STATUS_INVALID", + "DUMP_STATUS_IN_PROGRESS", + "DUMP_STATUS_SUCCESS", + "DUMP_STATUS_ERROR" + ], + "x-order": 1 + }, + "service_names": { + "type": "array", + "items": { + "type": "string" + }, + "x-order": 2 + }, + "start_time": { + "type": "string", + "format": "date-time", + "x-order": 3 + }, + "end_time": { + "type": "string", + "format": "date-time", + "x-order": 4 + }, + "created_at": { + "type": "string", + "format": "date-time", + "x-order": 5 + } + } + }, + "x-order": 0 + } + } + } + }, + "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": { + "type": "object", + "properties": { + "@type": { + "type": "string", + "x-order": 0 + } + }, + "additionalProperties": false + }, + "x-order": 2 + } + } + } + } + } + } + }, + "/v1/management/dump/Dumps/Start": { + "post": { + "tags": [ + "Dumps" + ], + "summary": "StartDump request creates pmm dump.", + "operationId": "StartDump", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "service_names": { + "type": "array", + "items": { + "type": "string" + }, + "x-order": 0 + }, + "start_time": { + "type": "string", + "format": "date-time", + "x-order": 1 + }, + "end_time": { + "type": "string", + "format": "date-time", + "x-order": 2 + }, + "export_qan": { + "type": "boolean", + "x-order": 3 + }, + "ignore_load": { + "type": "boolean", + "x-order": 4 + } + } + } + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": { + "dump_id": { + "type": "string", + "x-order": 0 + } + } + } + }, + "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": { + "type": "object", + "properties": { + "@type": { + "type": "string", + "x-order": 0 + } + }, + "additionalProperties": false + }, + "x-order": 2 + } + } + } + } + } + } + }, + "/v1/management/dump/Dumps/Upload": { + "post": { + "tags": [ + "Dumps" + ], + "summary": "UploadDump uploads selected dumps to remote server.", + "operationId": "UploadDump", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "dump_ids": { + "type": "array", + "items": { + "type": "string" + }, + "x-order": 0 + }, + "sftp_parameters": { + "type": "object", + "properties": { + "address": { + "type": "string", + "x-order": 0 + }, + "user": { + "type": "string", + "x-order": 1 + }, + "password": { + "type": "string", + "x-order": 2 + }, + "directory": { + "type": "string", + "x-order": 3 + } + }, + "x-order": 1 + } + } + } + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "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": { + "type": "object", + "properties": { + "@type": { + "type": "string", + "x-order": 0 + } + }, + "additionalProperties": false + }, + "x-order": 2 + } + } + } + } + } + } + }, "/v1/management/ia/Alerts/List": { "post": { "tags": [ @@ -38196,6 +38648,9 @@ { "name": "RestoreHistory" }, + { + "name": "Dumps" + }, { "name": "Role" }, diff --git a/build/packages/rpm/server/SPECS/pmm-dump.spec b/build/packages/rpm/server/SPECS/pmm-dump.spec index c4175cda2c..c8882aa25b 100644 --- a/build/packages/rpm/server/SPECS/pmm-dump.spec +++ b/build/packages/rpm/server/SPECS/pmm-dump.spec @@ -2,14 +2,14 @@ %global repo pmm-dump %global provider github.com/percona/%{repo} -%global commit 9cebba38ce90114f3199304f9091d620eff1d722 +%global commit 0d49b27729506dc62950f9fa59147d63df194db2 %global shortcommit %(c=%{commit}; echo ${c:0:7}) %define build_timestamp %(date -u +"%y%m%d%H%M") %define release 1 -%define rpm_release %{release}.%{build_timestamp}%{?dist} +%define rpm_release %{release}.%{build_timestamp}.%{shortcommit}%{?dist} Name: pmm-dump -Version: 0.6.0 +Version: 0.7.0 Release: %{rpm_release} Summary: Percona PMM Dump allows to export and import monitoring metrics and query analytics. @@ -37,5 +37,8 @@ install -p -m 0755 pmm-dump %{buildroot}%{_sbindir}/pmm-dump %changelog +* Tue Nov 23 2023 Artem Gavrilov - 0.7.0-ga +- PMM-12460 Update pmm-dump to v0.7.0-ga version + * Tue Mar 29 2022 Alex Tymchuk - 0.6.0-1 - Initial pmm-dump version diff --git a/descriptor.bin b/descriptor.bin index ce14ea92bd97e0f94384d5c69132c9b3edca93a6..91825581b28bbafe5d69802602c072522d5e7b61 100644 GIT binary patch delta 60016 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
  • s^nVv__Q7U7j-6sQ5l6 zqCj)i0H|{t7^gB{&)g+3jA$*@62$L$%KRA{ge=f}Edhw^dtPI6Q5#6L?|J$*lUNMe z?I{y8AVh)YrJX=TcY6)Z3k}T&GuC^?i1dA&q3!LBvR&}n*o97J}5JwI0q2tILN|EKe`uaoa3O!9Se54ltBEj z7cago?Ee6OW*km*%P1Xom2d$f7JOcUm40^ns4CFD7wYf1xCg5Wpi*xx) zE}vyAsL$j7y)L|`>hnZI#_w~_-mRhxBAc^~~@C(RyCGf$#h1Pt;xVQsd&u*M}ALE_vK~6rZgt8ZV2_-t>Kb z)4a>Bc|i1CcAg7F-(}~yK-zcNd9Fa?xtE>i))faD5-++fyaCnXxI_3Cg7ycX z1Vr@^-#RW3)kA#ixB`vpA-;87{fWC_@t6I>2djo9N~OYKiBj>=VWLz#R)5=Wgs(j| zUU>uS?GcHfIO_=CI;;7@-3Z@0D_^hU4JPra8^U`kA5BEWSs(SSvzjm5Ju07i7iZO9 zxEmF3xIJvyli##_R3a$8I?6A7F;0ASly6t2e7sIJPxbEzoAHf0an;d@pt$Pjq;=w| zqosASi`1XF8_O5i*+uGjBmwEMiL|)tSl@0X%@^**`gSYHck9>;#Y^r8JJ#|W8=iDu zUY_LvyiGMjy1ve%`yvwR-l=gfRvr! z*Vm7;$3W?Yru^xU*WwYcgu*YD(_dYW@p zAgZT1R~2YfPjjwXE>q=9an&Eh>uzGvKQqxRwa!d5i@(lv{;I#dHplp@9#ImoYfd69 zPCLgrt@#Yw9Otys<>+&1H`H7&o?tJIE zKpPPR2<&y&6_juV)y%nFhHEdq~vP6OSY(X@xGCuo#0R6ej zH4liwRnBRFC|u>77D)S6Ij0q9JZqJ6+Jd;x8gbfN?p~896?a|ZI-4>mUE|yph|)FA zT?HDYYn;0l#9h~Gcdg+!_O4Gx^+qBn?)ipuPf8;AhI7w?xaXVVo>jGP zI?p5#kvE-Z0ug!Bc_t4c-}Y1QSkIJ?h`sIG_Zl}4KicWr|2%+b2++LD7f9Kie!(o( zfoR?7vsiD7XEww$cl&%R{Gaa(+m!G2?Zn?uH14sU37|9gxRwD?xW{=W5QTf3X98*3 z9_N_?jT7y0p4m_wWv`)AexrJCqFVfOuj_4cQN7psClJ+poqq~6s`rY2%GBCW{Bxh- zCw`-PU!q#7-RH)hTvYFKW8YBF0W-Do8^HsKp!ny3#3u321I_~)ihmxwVrnG;>4S;1 zc*w${YdgGw2dba@sf+PHeK%}d{<)*6#-j10bp`-7pLFd5 zqVS}11|SMgI%fbfVkez52s9&h(m6w88I>=MGw>VLUnZ)>8NPHKPcEvzbj|=o^_R{W z1RB*}I%jAs&T!f|1HVyyI#Dgvo_5YaE~-yEXJ{LbGXN1h@0;Z{DGtR>U4oI%i>Jc9i|KIn*YkxX|2QPY6 z*HrHzMg;Z-Fc7690(%3v8LRUkRn$jH@*${41OC1!OIDs6j|x&x1ZK%9A9ETNuzznu zH5Jl|(Q%{u!glwMcD9p9nbFR6Ak~a^wga(!w6k5Hv3<1IE|1G9#1F>?en;M~*8O<4 zWjz1Bu<1`pFoz{%>wNon8zAb(2Kr>oSIrA*T##y`TOr?Y8W-q|p#Bci_#idOwxR`D z;{%o)(ux++ia2h2Kdp#eD@deF>{wwfZ&9x3l>!!KZ2{f&nCap8t5uYB| z^W#?f&=+Xh2Bfm-u5CbFxX{v^+=8iWdSK6wTL~r22z;|h1i=?Tr%DK5Mxsi3bw*Gy z+Y}0;X-3dO995bOni<%WXb_r!P7|b63${H@HmYU@1#_$h)WY^Cx|LWlE3kh)1R3a5 z34_f_Gzo*v3JT`*j>2e~6>xed7wcQ|RK1-NL*e zQ_g*Rc@j_hbAi7szU2qu9ktJK+9x9@>GaccLFqoZjM#HQ={~vKtX~kQMS;5~lClrs zZK8PyFQpa-YAL0zyjgFPO#R%fUmp1Df}r9$E;C)^qc83LF6>sjJSbh!kn8o!gVGfZ z8P4TF>57J2oL>>Bl_g8u^?I9VERj;H0=2eeiMd{HlX&hyi@9FEA@H|hN!Mb$^Y6l5 zwHt!c9}UE9HUy)#0cT^P~67?1dSdg6_sbhBNq*S`^zZnldLy%Cgdw#&u&?Sa}+GQwT2w~59GDfM=s z-YpqnuGiZnbBT!}y956tjObL1ulZrvvzE`5xl848y?%F4xv0ZAd+X%?+0pc z$q0A7-XBO)VcGa2NgMj{S+CbkSet_!GSK*xs0t+w&&_b*f~9yJK$?jS*7nbfC_bj4*fX zZIT%i9MDEz1PsT(xI_Hxyk=yeZ0`*PF2zSTcCK@B8)TKZT zRxVi09ebN(dV;H&LzO=YBj}0nL3(1SD)ofiu^*~RJt14Cp{mpqa(jN5QX^1r{(<7| z*xN*7gp?Yo)MF(h%pH51RINjVJNBcMA7ezZ7{B~r*tvGJiu4%(J|TDPN2`YVNt{58 z7_E8^vcn;_=f~h{B_rG&dz)yCkW%B6nou&r+_ASw{Am-#Jd>KJ{FxZhqZt3wkHUVn z6ICfr<&OPCRfWi~c_jZ?2u442N8=pPdYcT(6&<42N8tpQF^=k`eBDy-hSmNU3>BEhrgbuGiZn zL*P7qS)}}zFaiSq=*QtL|Gp?04!K^xC>aj9UcV?A4!JnLSgEBYBi!|Rn`n%XQp=QD zQ8L0@ua~41V}26yX5?*?S9JNe@bBAu{~AC1c=)d?>W>cZuE;N$?35^M{`-0Sgc;}_?zB~5?y!lFIcGgz~wLkF9IX#H^7iiYX zK&I9GfjwCU(x&}Ep@IB=O}?#nC}1!DAK!*|eD6@e<4IB2QWPEz>fCG4AF-xP*1HXA6yU>)i5!lJf z+UWBjYaXbYwbADR4^)4o*GAEoLF)AQ+UUVMzYOeKC0a}Gonm6u`atLd8ht?YoeJ!O z2O!Nj6>u4Zz;E#?U@uKU?&jq7!ngGtZ3Unhb1^H5qx*3imO86i=b;(8w{sqbll9 zs#8AaL_*^v0p*XXCdDKH<&P>}-`U$*#d+rA(xdf$mF^N(H;kfqS{Ow&q$O>WgU!dU zY@5vZk1Jl7CwD80^j$Po`P1S(Vf3B)V-+Xh#sxiG@JSW#_M-3nqnE{Ro>V1A1f%tD z6Bu2cU>woI5t#&?BYHU$j8iz`?b>ut9B~3W>WV(`klzLk;sZ0%>G*sUb?r&2GoDgZ zGePAW*)AXf<0mK{2AP5J#Alw4e~@Nip1x90yz%K|U?_*+)5*YiGB8Y{e~E8w5Vd;{ zh`^*|csyCePfmsh%Q{5Zq#ke0Mie0ORxJP2ti zhDYA`P=sxw8OmQ9Ps~I&)}NtrO>8Lp(u1?&+@LAyir(4eApH)pdmvrlc z_h(hpPL?LL$h`P}a?!vCSbaE}AQ|2B5}F_Z-SdPdobCYK^AsCd*E50GJx?`kZ<$1& z0X!G)uOHn`&&Y)G+?Cb~k35$QCM6JmPI$!CAoru5Q;j>7YEV+cbE-*4>(QDPpO5)3 zRI%stSLzn3dH%|A6{>k&#?`QacF;v}t}N=?bCFV+Qk8PDxJWTaIvZr)u}CpX_SLcI z%VUux@qd*?{dz6IT}$PJIhLrVZIT28m#FsgT|wbvUt(yf;ywG2Tf15y0!xz^;&79v zYDo-%i6Ih_!IVcR%aRzP1XV1P7&0vB%f;vyRn)6wgS>Z&gjPucDqd8rtFKB@(D9<` zc}=Nf;G&oQHV*gKAr47PoLcUPy>#W!$x8esp~;6m+fBU5VpZIH~Urktm1a!Zeuz)1!A|)1}o$|O4 z7PtZHWj(O@${M8R%?TG!0`biW7mz>=n-eaO1YM-W1^hLkz=K7&pdU5J;&DsD2qaL$ zmV^;Vkm3m=kU$Mv5=M{&U8KYa@|OmlL&6Am*KWJgdf|d?i9_kXG>}l@0us=@E#U%5 zK=-zU3*?Uv>J1*0Rn#pG{OLdc;t&5({Pj3Q?=@vBp~>v>Q-%|J&2s2`GO%iF5P6iMNwDhf8?OnZ!956(qPgCjk{ZiF0FLbDAo>wKIux z{u{xq$1CH!tS2z(EV*;K>&g+49se!~@k-k@5XJ8JHw~k1HM^B+R_Y1ihTW>dE!8-< zMR2$3)WvZ_AaA+(K)B(de*9+CKDe@CVTcbBhM)xUA0!MB$lEzTbPQ1|!zc_viem^8 z(D7ly5G0`ELtzNn>lB3t4k)_~1z`fvY)ya&A5eB13PkjPxP*N9vM6D8Q2AF^G%nu% z^I!eTZ{qtJN6l+VF^{0h*9DrJFYN-6J}9$*TxuCI|1ay@gRCm< zJbusV(>=@`VVJ{v7>1c)24;qlrw~*iKq!_ah=_=YsDuEkECE6g!3P&o21G1ic(oX! znDr428f&*wW0e)lxTzSQS+_Qc54LnG0oN4`ATJ@netzA3?saSbxmDl3zhA%3ufP5N zb<(LK)vc{0on41YtQ{3iR_e z{v@5hB)#0NO_6oNLxzQ9X_SYP#Gm}GbFLeKepd{L>ft1;iUUzSEVk%tlBzqBT%C*x z$;#H=5w^uLi0+Z3k9RIq=eG8aBqOfKU(YPBJ~f{uxzFv>z09oHpCw_5pkBVs(Igx_ z0g(XEe;XhX9!8`36bqcxDD5k^=gqfXE)t%m7GI$HfdJsR3LtwIB30*L6LWFg+C~WQ{B% zCb@m~>ON*@dN%1)tgQ)n>}C2YdAN#tD72=K!k7eLgPlJdSK z38KD~)b__0qU6p^6*k(RRGW$8H)_mCx>^^E;zr%8e<=XPjT(oUa%Q(tM&Liv@dH|J zr(Pq+g>7ZEsj*}FnwqK~X{PKx{#dfOS>nIi9_VY#@Xe(}_>V+3ml7d>L^ewzGhHH} zt@clS&8*6;nkkt0FKm=uqHT6ejhR!}rs?wK3pYwCJ9KF1AZi%szc7$wc4Uva1BrHr z<_mjO{%qHAtFz=rcSs!QJdO~rwDNXcwcj<6wywMFsf#o43VVGT-`jxpM{}S~sd1BtHf_Ce`5RL8(IWUuzR=!&o0!8q* zlXes#l|c>d(SadAG*zHy2q3z9bYKV|s(W-`2wqlZBB=vIfKUZ`h5(|vR|ke@jIWhW zbgvE+(I_ltbzlgPhn^vT=vo~Z0*I>BFvLolR8wm9>)Z==UaeVCxnFnc=5}P8Xi~|e z_Pts&q4H6&D4CfzNzos-gZi6m)5paoM!P_)@d+LO%s$ZHj4C{#`8?7>ni%>J$CDbK z61#So5)$AWp9>(uCv}*&xd5{Eq-NfhMS;LmI`K0F5s;}L0i`I!NS@MRK-t8mt+SJ- z#Kf{Vu!N`CDJf<{Dw+V_P$g2ahNm^_WLZYV9-h{$l!-kwNjaX$3><_y&>J`q$+HF0)dg6Ao6|BnJMx4p$pMr~uz~`6XLDz)Y=tNwX{cA>`_JA|j@Rh*-w!nmwc-;10VGa9Jq;jn{Q&ztrJ0$nNWzg-ZYeZ)m*N zNE8JZK$Q~GwHSw3$s4-L_ajb%INs3oBUv`6mp3&#i&-|r7zpqNEgF)_o4Sji^}7I~ zcvG`#l|=!5OBcRY6fugozFrhDi?_aB6fumqzFrh6{cVk#ge)6k90ZiIA=dG>?&6!X zWJB!ZZOz2rXQN=GZN3Bb72^;ZB*3qkh>8&5JM#ImRD=oN$r|fG`6Tb^*bnbOXmhN;!kxq8xJyzyZu3P zxp8lV?F^D}$9p>d5LdirPvJc=G${w+l8g6sSh;YRauMKp%LS11`#LNwy8zODUo-B= zqCnsS>A<9%A|TX9Kq(5biw|^#HH+#&c0i*4AKlxJMSxD2v4~W3vvA^PSn?NrAOMW+=Q_;Jnq`9g zMb>+OhzjV(I6!p2$T|-os$WRwA+xh)nHV3l54M?sg=3|^!vt{SGA%)#JKHRt}j1AJ?3G_l0Bze4*0)6Y?Pf|Up zYYP7MEg0RCdZ>S6r&*q1T6Xg)Yz^Is>Bnm9F-#gn{UOrQ3#t(x&VM zmU5h;3e0kxqI#~B;}q3%r5tx-mR2Zbxf{CYOIen~W9Lg*7N1(@y;;Nw&%s~PxgUo& z&|8qaq&xX7F?WlsO6h62#@VH;XX77RiN+mfvD~_q0wqB6rXUPB~yd9A%b_J75?LGfv_NmQc$9!%#Rt8u|g#!;KN-Us69{+FbV|kmL?@ ztRHP=KcO~G&)eD+`Tq9g2j=1UX!nk7ADScO(PVq)VKb)Vqr>K3qw)_&__IS#*=T=R z5Btg|W_q-xqw{~8DieLf_W#11bFR>3n5uZJ@UJw*t6({CSwt8PjIQFyO+Yv>x{57= z;d~kfn5i0TKl-Z~*~$Z0K>~hLWAW1N<)JqlMP9A4YCjRj+I3pJ5x9WvYbL@Y6AW|!hfQS zJCDm7uVfk|8&eWwoWQ$8ZJ|likXJW)Fd)}g;OPs$+qdZ8QnIS z%g;V?2uw|`%nAvAsmaJUIC+z*De+)i5#?)#u<`;BpCVF~aG07>BMjgWT2mms!A`3z zRa5Q6L3+?I+zO*Ll{5QZ4*F74IkF!RT2nc(FE=4_ke8ZfAKRuT*rg{-zI9r$K=78D zR_5JKA%@a4mOwajh*Ei~rrSqPm=R;9GyM0_;%i?`=X^;(XiraNy;(8}bE)agtwB4V zsu}i$yHv9s_NB?U%_vqsq@`xCi|du3EHwj9`2$^&6{(sT_qHFeRW)P$|Lj2!lzlo# zOU*2o%OsK@oTX;==;huJ%0XFbR$NB>9LwS*ht<>y({qvke1a(Q(oVc$Y)?7YiSQk>6Q_Ln$ zgsC~fQJM24tLF5scIytfcTLsYxSxGxQ2B%*m1xZ^YQa%zZqY8GC^fgb#yx<88`f0K zvr|u*i9_Zw%<#pBnAE%~*<_Hs!Aoi$-Ns0lx2jalj~l87+N;i({`QZjOxilXXqB*% znqRa^NJ-6Sl-}$V(^TDR)6=H4_10p>Qsi$9e$Y4~kNmAT;ga>LYOQ%ie)`i7{x}&(TSZ{a}RST&% zxv_$K&r~h4qtBSD+7>ZJ@=>15bgCtBo&B$WRJ9{O)bx^K zIz7=_QZ6n>q~`F>sh03n<$(yiW~S=)xQ}gLu7=aZdZBlFv0&U-Qn&MzjMu}Vrn;Ty zA$*PZPSsL-%~><5b!kz)H}Xr%WWig~VVOWJWymfrj&gCIR4uay&ziBV%Zl<`3R26; zq#qS|+*GP%EYRvnay!o>A-l9Ss z3irBqGNcOXP`H=(xjo_4r+DXk{QdGF!|Tb^`eL=!qqCkDbbTWA=&a`infQmx#SM(* zqqxQndrXZU4V0mOgr+QO0O|iXMBxctARY3Ch_*1n=?+K}9P;(D&s|wQ%FeuC@|Oes ztRWWJKBfczsR67P2LS2Wqhz@P1Q!6h?gYI{jV=1A>X?)}lYc5##zQ$SSqM&bE0Ao++G--)b9CXS!-c`RJ>3l_lx#NP<9x-5#|16Aswg518MmgeSeiMWZS5*gFY99gmH{Nn zhylI5?98WS}^| zZAOMf^1501+uZk07UY)OMRobxsupxg^A{i-_x+Pa@%QY1)a5TMSj6=VqLEN?wusvq z0TIn2E@uQ2VD7v)xm~{H*WPOJK_!Vhl4V8~bR{zR+6e(2!yEGBy2s}xcB~woe>Kh@ z#EkLcK5O#z_O?l7uXbGIZhB&WI;Ct<$FA}D>ZJT|iyz(p*pKe{s`d7ppOp8sue?)U zVP6x-weR=%j}qU-!kky*ueCTx^Mdku*Ye0uK=L1pavl|APOuh#XkL#>Tvtp^ zKJPk?@Ow%5yzBa8$;s4UT`@Vp-SN;MD4+Lkj)QnL`Mh`c^)})>XsWv}9}-ey9x&#f zcwCTVNdpJ3Sy`nT?r9v8{YMJ-Ts_wHx(Uk}>OMQ|hWyyVeWA-C4Eg&)9|c1GK4{IC ziip?8G7XhVlv%*~&_+P0t*>=vBmbecesEh*V-`?|M+G&R1{6XYNn{m675WXkA^Q`( z!pJK?^350sY!FkfyvL8ayTY7k1hZ{rxny#@tQH`|-AF7tYB~PVbL6 z6~ivBXFPh7?e@hv`B8;UQPG{TFx{r8=+0Qkb5m4wXDoLlH%G~X|BvZHV2SCrMmS^r zzf2c`2D|j>aOe*nfpPoR*=0B8n+oj_`)I!D$R*15sB?C8PL|Nxqo$F8ZsmI9&M4Va s%tUsHG6eceh}7OF*;mYjzeMSSigf#FTfQdHZP&T^j;?bnu6h1{0S8*O*Z=?k diff --git a/go.mod b/go.mod index 6f0198c448..6cfcb51155 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ replace github.com/go-openapi/spec => github.com/Percona-Lab/spec v0.20.5-percon replace gopkg.in/alecthomas/kingpin.v2 => github.com/Percona-Lab/kingpin v2.2.6-percona+incompatible -replace golang.org/x/crypto => github.com/percona-lab/crypto v0.0.0-20220811043533-d164de3c7f08 +replace golang.org/x/crypto => github.com/percona-lab/crypto v0.0.0-20231108144114-756dfb24eaf2 replace github.com/ClickHouse/clickhouse-go/151 => github.com/ClickHouse/clickhouse-go v1.5.1 // clickhouse-go/v2 cannot work with 1.5.1 which we need for QAN-API @@ -64,6 +64,7 @@ require ( github.com/percona/promconfig v0.2.5 github.com/pganalyze/pg_query_go/v2 v2.2.0 github.com/pkg/errors v0.9.1 + github.com/pkg/sftp v1.13.6 github.com/pmezard/go-difflib v1.0.0 github.com/prometheus/alertmanager v0.26.0 github.com/prometheus/client_golang v1.17.0 @@ -119,6 +120,7 @@ require ( 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-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 diff --git a/go.sum b/go.sum index bcacdc145b..8b5205467b 100644 --- a/go.sum +++ b/go.sum @@ -507,6 +507,8 @@ github.com/klauspost/pgzip v1.2.3/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -641,8 +643,8 @@ github.com/paulmach/orb v0.10.0 h1:guVYVqzxHE/CQ1KpfGO077TR0ATHSNjp4s6XGLn3W9s= github.com/paulmach/orb v0.10.0/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU= github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= -github.com/percona-lab/crypto v0.0.0-20220811043533-d164de3c7f08 h1:NprWeXddFZJSgtN8hmf/hhIgiZwB3GNaKnI88iAFgEc= -github.com/percona-lab/crypto v0.0.0-20220811043533-d164de3c7f08/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +github.com/percona-lab/crypto v0.0.0-20231108144114-756dfb24eaf2 h1:FaO5loTAkRzVhyKWYRHg/ii9oWXNsqv89H/kGl9Cb5I= +github.com/percona-lab/crypto v0.0.0-20231108144114-756dfb24eaf2/go.mod h1:aSyBXtGhRzSMdne9jbl3+PPMVS0IgOWF6Llc+HB5uUU= github.com/percona-platform/dbaas-api v0.0.0-20230103182808-d79c449a9f4c h1:1JySfwdjVfc9ahl0466OX7nSQ7Z4SjQkLe3ZdLkMOJI= github.com/percona-platform/dbaas-api v0.0.0-20230103182808-d79c449a9f4c/go.mod h1:/jgle33awfHq1va/T6NnNS5wWAETSnl6wUZ1bew+CJ0= github.com/percona-platform/saas v0.0.0-20230728161159-ad6bdeb8a3d9 h1:KkOH+Y4sVRP7qvRtTDmfPFNjjQcwU2054/jNl9DZhEo= @@ -674,6 +676,8 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo= +github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= @@ -873,6 +877,7 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -911,8 +916,9 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -936,6 +942,7 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -995,12 +1002,18 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8= golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1014,6 +1027,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1076,6 +1091,7 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/managed/cmd/pmm-managed/main.go b/managed/cmd/pmm-managed/main.go index 7a3a10cd50..f2aa33e91a 100644 --- a/managed/cmd/pmm-managed/main.go +++ b/managed/cmd/pmm-managed/main.go @@ -67,6 +67,7 @@ import ( azurev1beta1 "github.com/percona/pmm/api/managementpb/azure" backuppb "github.com/percona/pmm/api/managementpb/backup" dbaasv1beta1 "github.com/percona/pmm/api/managementpb/dbaas" + dumpv1beta1 "github.com/percona/pmm/api/managementpb/dump" iav1beta1 "github.com/percona/pmm/api/managementpb/ia" nodev1beta1 "github.com/percona/pmm/api/managementpb/node" rolev1beta1 "github.com/percona/pmm/api/managementpb/role" @@ -83,6 +84,7 @@ import ( "github.com/percona/pmm/managed/services/checks" "github.com/percona/pmm/managed/services/config" "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/inventory" inventorygrpc "github.com/percona/pmm/managed/services/inventory/grpc" @@ -91,6 +93,7 @@ import ( managementbackup "github.com/percona/pmm/managed/services/management/backup" "github.com/percona/pmm/managed/services/management/common" managementdbaas "github.com/percona/pmm/managed/services/management/dbaas" + managementdump "github.com/percona/pmm/managed/services/management/dump" managementgrpc "github.com/percona/pmm/managed/services/management/grpc" "github.com/percona/pmm/managed/services/management/ia" "github.com/percona/pmm/managed/services/minio" @@ -207,6 +210,7 @@ type gRPCServerDeps struct { versionServiceClient *managementdbaas.VersionServiceClient schedulerService *scheduler.Service backupService *backup.Service + dumpService *dump.Service compatibilityService *backup.CompatibilityService backupRemovalService *backup.RemovalService pbmPITRService *backup.PBMPITRService @@ -308,6 +312,8 @@ func runGRPCServer(ctx context.Context, deps *gRPCServerDeps) { backuppb.RegisterArtifactsServer(gRPCServer, mgmtArtifactsService) backuppb.RegisterRestoreHistoryServer(gRPCServer, mgmtRestoreHistoryService) + 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) dbaasv1beta1.RegisterKubernetesServer(gRPCServer, k8sServer) @@ -429,6 +435,8 @@ func runHTTP1Server(ctx context.Context, deps *http1ServerDeps) { backuppb.RegisterArtifactsHandlerFromEndpoint, backuppb.RegisterRestoreHistoryHandlerFromEndpoint, + dumpv1beta1.RegisterDumpsHandlerFromEndpoint, + dbaasv1beta1.RegisterKubernetesHandlerFromEndpoint, dbaasv1beta1.RegisterDBClustersHandlerFromEndpoint, dbaasv1beta1.RegisterPXCClustersHandlerFromEndpoint, @@ -952,6 +960,8 @@ func main() { //nolint:cyclop,maintidx versionCache := versioncache.New(db, versioner) emailer := alertmanager.NewEmailer(logrus.WithField("component", "alertmanager-emailer").Logger) + dumpService := dump.New(db) + kubeStorage := managementdbaas.NewKubeStorage(db) componentsService := managementdbaas.NewComponentsService(db, dbaasClient, versionService, kubeStorage) @@ -1127,6 +1137,7 @@ func main() { //nolint:cyclop,maintidx db: db, dbaasClient: dbaasClient, dbaasInitializer: dbaasInitializer, + dumpService: dumpService, grafanaClient: grafanaClient, handler: agentsHandler, jobsService: jobsService, @@ -1144,8 +1155,8 @@ func main() { //nolint:cyclop,maintidx uieventsService: uieventsService, versionCache: versionCache, versionServiceClient: versionService, - vmalert: vmalert, vmClient: &vmClient, + vmalert: vmalert, vmdb: vmdb, }) }() diff --git a/managed/models/database.go b/managed/models/database.go index e82746d7e9..4880f07f64 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -925,6 +925,30 @@ var databaseSchema = [][]string{ `ALTER TABLE agents ALTER COLUMN expose_exporter DROP DEFAULT`, }, + 87: { + `CREATE TABLE dumps ( + id VARCHAR NOT NULL, + status VARCHAR NOT NULL CHECK (status <> ''), + service_names VARCHAR[], + start_time TIMESTAMP, + end_time TIMESTAMP, + export_qan BOOLEAN NOT NULL, + ignore_load BOOLEAN NOT NULL, + created_at TIMESTAMP NOT NULL, + updated_at TIMESTAMP NOT NULL, + + PRIMARY KEY (id) + )`, + + `CREATE TABLE dump_logs ( + dump_id VARCHAR NOT NULL, + chunk_id INTEGER NOT NULL, + data TEXT NOT NULL, + last_chunk BOOLEAN NOT NULL, + FOREIGN KEY (dump_id) REFERENCES dumps (id) ON DELETE CASCADE, + PRIMARY KEY (dump_id, chunk_id) + )`, + }, } // ^^^ Avoid default values in schema definition. ^^^ diff --git a/managed/models/dump.go b/managed/models/dump.go new file mode 100644 index 0000000000..d7adcee5c7 --- /dev/null +++ b/managed/models/dump.go @@ -0,0 +1,104 @@ +// 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 + +import ( + "time" + + "github.com/lib/pq" + "gopkg.in/reform.v1" +) + +//go:generate ../../bin/reform + +type DumpStatus string + +const ( + DumpStatusInProgress = DumpStatus("in_progress") + DumpStatusSuccess = DumpStatus("success") + DumpStatusError = DumpStatus("error") +) + +// Validate validates Dumps status. +func (ds DumpStatus) Validate() error { + switch ds { + case DumpStatusInProgress: + case DumpStatusSuccess: + case DumpStatusError: + default: + return NewInvalidArgumentError("invalid dump status '%s'", ds) + } + + return nil +} + +// Pointer returns a pointer to status value. +func (ds DumpStatus) Pointer() *DumpStatus { + return &ds +} + +// Dump represents pmm dump artifact. +// +//reform:dumps +type Dump struct { + ID string `reform:"id,pk"` + Status DumpStatus `reform:"status"` + ServiceNames pq.StringArray `reform:"service_names"` + StartTime *time.Time `reform:"start_time"` + EndTime *time.Time `reform:"end_time"` + ExportQAN bool `reform:"export_qan"` + IgnoreLoad bool `reform:"ignore_load"` + CreatedAt time.Time `reform:"created_at"` + UpdatedAt time.Time `reform:"updated_at"` +} + +// BeforeInsert implements reform.BeforeInserter interface. +func (d *Dump) BeforeInsert() error { + now := Now() + d.CreatedAt = now + d.UpdatedAt = now + return nil +} + +// BeforeUpdate implements reform.BeforeUpdater interface. +func (d *Dump) BeforeUpdate() error { + d.UpdatedAt = Now() + return nil +} + +// AfterFind implements reform.AfterFinder interface. +func (d *Dump) AfterFind() error { + d.CreatedAt = d.CreatedAt.UTC() + d.UpdatedAt = d.UpdatedAt.UTC() + return nil +} + +// DumpLog stores chunk of logs from pmm-dump. +// +//reform:dump_logs +type DumpLog struct { + DumpID string `reform:"dump_id"` + ChunkID uint32 `reform:"chunk_id"` + Data string `reform:"data"` + LastChunk bool `reform:"last_chunk"` +} + +// check interfaces. +var ( + _ reform.BeforeInserter = (*Dump)(nil) + _ reform.BeforeUpdater = (*Dump)(nil) + _ reform.AfterFinder = (*Dump)(nil) +) diff --git a/managed/models/dump_helpers.go b/managed/models/dump_helpers.go new file mode 100644 index 0000000000..b5c0626ef1 --- /dev/null +++ b/managed/models/dump_helpers.go @@ -0,0 +1,247 @@ +// 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 + +import ( + "fmt" + "strings" + "time" + + "github.com/google/uuid" + "github.com/pkg/errors" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "gopkg.in/reform.v1" +) + +func checkUniqueDumpID(q *reform.Querier, id string) error { + if id == "" { + panic("empty dump ID") + } + + dump := &Dump{ID: id} + err := q.Reload(dump) + if err != nil { + if errors.Is(err, reform.ErrNoRows) { + return nil + } + return errors.WithStack(err) + } + + return status.Errorf(codes.AlreadyExists, "Dump with id %q already exists.", id) +} + +// DumpFilters represents filters for dumps list. +type DumpFilters struct { + // Return only dumps by specified status. + Status DumpStatus +} + +type CreateDumpParams struct { + ServiceNames []string + StartTime *time.Time + EndTime *time.Time + ExportQAN bool + IgnoreLoad bool +} + +func (p *CreateDumpParams) Validate() error { + if p.StartTime != nil && p.EndTime != nil && p.StartTime.After(*p.EndTime) { + return errors.Errorf("dump start time can't be greater than end time") + } + + return nil +} + +func CreateDump(q *reform.Querier, params CreateDumpParams) (*Dump, error) { + if err := params.Validate(); err != nil { + return nil, errors.Wrap(err, "invalid dump creation params") + } + + id := uuid.New().String() + if err := checkUniqueDumpID(q, id); err != nil { + return nil, err + } + + dump := &Dump{ + ID: id, + Status: DumpStatusInProgress, + ServiceNames: params.ServiceNames, + StartTime: params.StartTime, + EndTime: params.EndTime, + ExportQAN: params.ExportQAN, + IgnoreLoad: params.IgnoreLoad, + } + if err := q.Insert(dump); err != nil { + return nil, errors.WithStack(err) + } + + return dump, nil +} + +// FindDumps returns dumps list sorted by creation time in DESCENDING order. +func FindDumps(q *reform.Querier, filters DumpFilters) ([]*Dump, error) { + var conditions []string + var args []interface{} + var idx int + + if filters.Status != "" { + idx++ + conditions = append(conditions, fmt.Sprintf("status = %s", q.Placeholder(idx))) + args = append(args, filters.Status) + } + + var whereClause string + if len(conditions) != 0 { + whereClause = fmt.Sprintf("WHERE %s", strings.Join(conditions, " AND ")) + } + rows, err := q.SelectAllFrom(DumpTable, fmt.Sprintf("%s ORDER BY created_at DESC", whereClause), args...) + if err != nil { + return nil, errors.Wrap(err, "failed to select dumps") + } + + dumps := make([]*Dump, 0, len(rows)) + for _, r := range rows { + dumps = append(dumps, r.(*Dump)) //nolint:forcetypeassert + } + + return dumps, nil +} + +// FindDumpsByIDs finds dumps by IDs. +func FindDumpsByIDs(q *reform.Querier, ids []string) (map[string]*Dump, error) { + if len(ids) == 0 { + return make(map[string]*Dump), nil + } + + p := strings.Join(q.Placeholders(1, len(ids)), ", ") + tail := fmt.Sprintf("WHERE id IN (%s)", p) + args := make([]interface{}, 0, len(ids)) + for _, id := range ids { + args = append(args, id) + } + + all, err := q.SelectAllFrom(DumpTable, tail, args...) + if err != nil { + return nil, errors.WithStack(err) + } + + dumps := make(map[string]*Dump, len(all)) + for _, l := range all { + dump := l.(*Dump) //nolint:forcetypeassert + dumps[dump.ID] = dump + } + return dumps, nil +} + +// FindDumpByID returns dump by given ID if found, ErrNotFound if not. +func FindDumpByID(q *reform.Querier, id string) (*Dump, error) { + if id == "" { + return nil, errors.New("provided dump id is empty") + } + + dump := &Dump{ID: id} + err := q.Reload(dump) + if err != nil { + if errors.Is(err, reform.ErrNoRows) { + return nil, errors.Wrapf(ErrNotFound, "dump by id '%s'", id) + } + return nil, errors.WithStack(err) + } + + return dump, nil +} + +func UpdateDumpStatus(q *reform.Querier, id string, status DumpStatus) error { + dump, err := FindDumpByID(q, id) + if err != nil { + return err + } + + dump.Status = status + + if err = q.Update(dump); err != nil { + return errors.Wrap(err, "failed to update dump status") + } + + return nil +} + +// DeleteDump removes dump by ID. +func DeleteDump(q *reform.Querier, id string) error { + if _, err := FindDumpByID(q, id); err != nil { + return err + } + + if err := q.Delete(&Dump{ID: id}); err != nil { + return errors.Wrapf(err, "failed to delete dump by id '%s'", id) + } + return nil +} + +// CreateDumpLogParams are params for creating a new pmm-dump log. +type CreateDumpLogParams struct { + DumpID string + ChunkID uint32 + Data string + LastChunk bool +} + +// CreateDumpLog inserts new chunk log. +func CreateDumpLog(q *reform.Querier, params CreateDumpLogParams) (*DumpLog, error) { + log := &DumpLog{ + DumpID: params.DumpID, + ChunkID: params.ChunkID, + Data: params.Data, + LastChunk: params.LastChunk, + } + if err := q.Insert(log); err != nil { + return nil, errors.WithStack(err) + } + return log, nil +} + +// DumpLogsFilter represents filter for dump logs. +type DumpLogsFilter struct { + DumpID string + Offset int + Limit *int +} + +// FindDumpLogs returns logs that belongs to dump. +func FindDumpLogs(q *reform.Querier, filters DumpLogsFilter) ([]*DumpLog, error) { + limit := defaultLimit + tail := "WHERE dump_id = $1 AND chunk_id >= $2 ORDER BY chunk_id LIMIT $3" + if filters.Limit != nil { + limit = *filters.Limit + } + args := []interface{}{ + filters.DumpID, + filters.Offset, + limit, + } + + rows, err := q.SelectAllFrom(DumpLogView, tail, args...) + if err != nil { + return nil, errors.Wrap(err, "failed to select dump logs") + } + + logs := make([]*DumpLog, 0, len(rows)) + for _, r := range rows { + logs = append(logs, r.(*DumpLog)) //nolint:forcetypeassert + } + return logs, nil +} diff --git a/managed/models/dump_helpers_test.go b/managed/models/dump_helpers_test.go new file mode 100644 index 0000000000..5660c41302 --- /dev/null +++ b/managed/models/dump_helpers_test.go @@ -0,0 +1,280 @@ +// 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_test + +import ( + "sort" + "testing" + "time" + + "github.com/AlekSi/pointer" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "gopkg.in/reform.v1" + "gopkg.in/reform.v1/dialects/postgresql" + + "github.com/percona/pmm/managed/models" + "github.com/percona/pmm/managed/utils/testdb" +) + +func TestDumps(t *testing.T) { + sqlDB := testdb.Open(t, models.SkipFixtures, nil) + db := reform.NewDB(sqlDB, postgresql.Dialect, reform.NewPrintfLogger(t.Logf)) + tx, err := db.Begin() + require.NoError(t, err) + t.Cleanup(func() { + require.NoError(t, tx.Rollback()) + }) + + t.Run("create", func(t *testing.T) { + t.Run("normal", func(t *testing.T) { + endTime := time.Now() + startTime := endTime.Add(-10 * time.Minute) + + createDumpParams := models.CreateDumpParams{ + ServiceNames: []string{"foo", "bar"}, + StartTime: &startTime, + EndTime: &endTime, + ExportQAN: false, + IgnoreLoad: true, + } + dump, err := models.CreateDump(tx.Querier, createDumpParams) + require.NoError(t, err) + assert.NotEmpty(t, dump.ID) + assert.Equal(t, models.DumpStatusInProgress, dump.Status) + assert.ElementsMatch(t, createDumpParams.ServiceNames, dump.ServiceNames) + assert.Equal(t, createDumpParams.StartTime, dump.StartTime) + assert.Equal(t, createDumpParams.EndTime, dump.EndTime) + assert.Equal(t, createDumpParams.ExportQAN, dump.ExportQAN) + assert.Equal(t, createDumpParams.IgnoreLoad, dump.IgnoreLoad) + }) + + t.Run("invalid start and end time", func(t *testing.T) { + endTime := time.Now() + startTime := endTime.Add(10 * time.Minute) + + createDumpParams := models.CreateDumpParams{ + ServiceNames: []string{"foo", "bar"}, + StartTime: &startTime, + EndTime: &endTime, + ExportQAN: false, + IgnoreLoad: true, + } + _, err := models.CreateDump(tx.Querier, createDumpParams) + require.EqualError(t, err, "invalid dump creation params: dump start time can't be greater than end time") + }) + }) + + t.Run("find", func(t *testing.T) { + findTX, err := db.Begin() + require.NoError(t, err) + defer findTX.Rollback() //nolint:errcheck + + endTime := time.Now() + startTime := endTime.Add(-10 * time.Minute) + + dump1, err := models.CreateDump(findTX.Querier, models.CreateDumpParams{ + ServiceNames: []string{"foo", "bar"}, + StartTime: &startTime, + EndTime: &endTime, + ExportQAN: false, + IgnoreLoad: true, + }) + require.NoError(t, err) + + dump2, err := models.CreateDump(findTX.Querier, models.CreateDumpParams{ + ServiceNames: []string{"foo", "bar"}, + StartTime: &startTime, + EndTime: &endTime, + ExportQAN: false, + IgnoreLoad: true, + }) + require.NoError(t, err) + dump2.Status = models.DumpStatusSuccess + err = models.UpdateDumpStatus(findTX.Querier, dump2.ID, dump2.Status) + require.NoError(t, err) + + dump3, err := models.CreateDump(findTX.Querier, models.CreateDumpParams{ + ServiceNames: []string{"foo", "bar"}, + StartTime: &startTime, + EndTime: &endTime, + ExportQAN: false, + IgnoreLoad: true, + }) + require.NoError(t, err) + dump3.Status = models.DumpStatusError + err = findTX.Querier.Update(dump3) + require.NoError(t, err) + + type testCase struct { + Filters models.DumpFilters + Expect []string + } + + testCases := []testCase{ + { + Filters: models.DumpFilters{}, + Expect: []string{dump1.ID, dump2.ID, dump3.ID}, + }, + { + Filters: models.DumpFilters{ + Status: models.DumpStatusInProgress, + }, + Expect: []string{dump1.ID}, + }, + { + Filters: models.DumpFilters{ + Status: models.DumpStatusSuccess, + }, + Expect: []string{dump2.ID}, + }, + { + Filters: models.DumpFilters{ + Status: models.DumpStatusError, + }, + Expect: []string{dump3.ID}, + }, + } + + for _, tc := range testCases { + dumps, err := models.FindDumps(findTX.Querier, tc.Filters) + require.NoError(t, err) + ids := make([]string, len(dumps)) + for i := range dumps { + ids[i] = dumps[i].ID + } + sort.Strings(tc.Expect) + sort.Strings(ids) + assert.Equal(t, tc.Expect, ids) + } + }) +} + +func TestDumpLogs(t *testing.T) { + sqlDB := testdb.Open(t, models.SkipFixtures, nil) + db := reform.NewDB(sqlDB, postgresql.Dialect, reform.NewPrintfLogger(t.Logf)) + tx, err := db.Begin() + require.NoError(t, err) + t.Cleanup(func() { + require.NoError(t, tx.Rollback()) + require.NoError(t, sqlDB.Close()) + }) + + dump1, err := models.CreateDump(tx.Querier, models.CreateDumpParams{}) + require.NoError(t, err) + + dump2, err := models.CreateDump(tx.Querier, models.CreateDumpParams{}) + require.NoError(t, err) + + createRequests := []models.CreateDumpLogParams{ + { + DumpID: dump1.ID, + ChunkID: 0, + Data: "some log", + }, + { + DumpID: dump1.ID, + ChunkID: 1, + Data: "another log", + }, + { + DumpID: dump2.ID, + ChunkID: 0, + Data: "some log", + }, + } + + t.Run("create", func(t *testing.T) { + for _, req := range createRequests { + log, err := models.CreateDumpLog(tx.Querier, req) + require.NoError(t, err) + assert.Equal(t, req.DumpID, log.DumpID) + assert.Equal(t, req.ChunkID, log.ChunkID) + assert.Equal(t, req.Data, log.Data) + assert.False(t, log.LastChunk) + } + }) + + t.Run("find", func(t *testing.T) { + type expectLog struct { + DumpID string + ChunkID uint32 + } + type testCase struct { + Name string + Filters models.DumpLogsFilter + Expect []expectLog + } + testCases := []testCase{ + { + Name: "dump filter", + Filters: models.DumpLogsFilter{ + DumpID: dump1.ID, + }, + Expect: []expectLog{ + { + DumpID: dump1.ID, + ChunkID: 0, + }, + { + DumpID: dump1.ID, + ChunkID: 1, + }, + }, + }, + { + Name: "dump filter and limit", + Filters: models.DumpLogsFilter{ + DumpID: dump1.ID, + Limit: pointer.ToInt(1), + }, + Expect: []expectLog{ + { + DumpID: dump1.ID, + ChunkID: 0, + }, + }, + }, + { + Name: "dump filter. limit and offset", + Filters: models.DumpLogsFilter{ + DumpID: dump1.ID, + Offset: 1, + Limit: pointer.ToInt(1), + }, + Expect: []expectLog{ + { + DumpID: dump1.ID, + ChunkID: 1, + }, + }, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.Name, func(t *testing.T) { + logs, err := models.FindDumpLogs(tx.Querier, tc.Filters) + require.NoError(t, err) + require.Len(t, logs, len(tc.Expect)) + for i := range logs { + assert.Equal(t, tc.Expect[i].DumpID, logs[i].DumpID) + assert.Equal(t, tc.Expect[i].ChunkID, logs[i].ChunkID) + } + }) + } + }) +} diff --git a/managed/models/dump_reform.go b/managed/models/dump_reform.go new file mode 100644 index 0000000000..a78ca52e9a --- /dev/null +++ b/managed/models/dump_reform.go @@ -0,0 +1,262 @@ +// Code generated by gopkg.in/reform.v1. DO NOT EDIT. + +package models + +import ( + "fmt" + "strings" + + "gopkg.in/reform.v1" + "gopkg.in/reform.v1/parse" +) + +type dumpTableType struct { + s parse.StructInfo + z []interface{} +} + +// Schema returns a schema name in SQL database (""). +func (v *dumpTableType) Schema() string { + return v.s.SQLSchema +} + +// Name returns a view or table name in SQL database ("dumps"). +func (v *dumpTableType) Name() string { + return v.s.SQLName +} + +// Columns returns a new slice of column names for that view or table in SQL database. +func (v *dumpTableType) Columns() []string { + return []string{ + "id", + "status", + "service_names", + "start_time", + "end_time", + "export_qan", + "ignore_load", + "created_at", + "updated_at", + } +} + +// NewStruct makes a new struct for that view or table. +func (v *dumpTableType) NewStruct() reform.Struct { + return new(Dump) +} + +// NewRecord makes a new record for that table. +func (v *dumpTableType) NewRecord() reform.Record { + return new(Dump) +} + +// PKColumnIndex returns an index of primary key column for that table in SQL database. +func (v *dumpTableType) PKColumnIndex() uint { + return uint(v.s.PKFieldIndex) +} + +// DumpTable represents dumps view or table in SQL database. +var DumpTable = &dumpTableType{ + s: parse.StructInfo{ + Type: "Dump", + SQLName: "dumps", + Fields: []parse.FieldInfo{ + {Name: "ID", Type: "string", Column: "id"}, + {Name: "Status", Type: "DumpStatus", Column: "status"}, + {Name: "ServiceNames", Type: "pq.StringArray", Column: "service_names"}, + {Name: "StartTime", Type: "*time.Time", Column: "start_time"}, + {Name: "EndTime", Type: "*time.Time", Column: "end_time"}, + {Name: "ExportQAN", Type: "bool", Column: "export_qan"}, + {Name: "IgnoreLoad", Type: "bool", Column: "ignore_load"}, + {Name: "CreatedAt", Type: "time.Time", Column: "created_at"}, + {Name: "UpdatedAt", Type: "time.Time", Column: "updated_at"}, + }, + PKFieldIndex: 0, + }, + z: new(Dump).Values(), +} + +// String returns a string representation of this struct or record. +func (s Dump) String() string { + res := make([]string, 9) + res[0] = "ID: " + reform.Inspect(s.ID, true) + res[1] = "Status: " + reform.Inspect(s.Status, true) + res[2] = "ServiceNames: " + reform.Inspect(s.ServiceNames, true) + res[3] = "StartTime: " + reform.Inspect(s.StartTime, true) + res[4] = "EndTime: " + reform.Inspect(s.EndTime, true) + res[5] = "ExportQAN: " + reform.Inspect(s.ExportQAN, true) + res[6] = "IgnoreLoad: " + reform.Inspect(s.IgnoreLoad, true) + res[7] = "CreatedAt: " + reform.Inspect(s.CreatedAt, true) + res[8] = "UpdatedAt: " + reform.Inspect(s.UpdatedAt, true) + return strings.Join(res, ", ") +} + +// Values returns a slice of struct or record field values. +// Returned interface{} values are never untyped nils. +func (s *Dump) Values() []interface{} { + return []interface{}{ + s.ID, + s.Status, + s.ServiceNames, + s.StartTime, + s.EndTime, + s.ExportQAN, + s.IgnoreLoad, + s.CreatedAt, + s.UpdatedAt, + } +} + +// Pointers returns a slice of pointers to struct or record fields. +// Returned interface{} values are never untyped nils. +func (s *Dump) Pointers() []interface{} { + return []interface{}{ + &s.ID, + &s.Status, + &s.ServiceNames, + &s.StartTime, + &s.EndTime, + &s.ExportQAN, + &s.IgnoreLoad, + &s.CreatedAt, + &s.UpdatedAt, + } +} + +// View returns View object for that struct. +func (s *Dump) View() reform.View { + return DumpTable +} + +// Table returns Table object for that record. +func (s *Dump) Table() reform.Table { + return DumpTable +} + +// PKValue returns a value of primary key for that record. +// Returned interface{} value is never untyped nil. +func (s *Dump) PKValue() interface{} { + return s.ID +} + +// PKPointer returns a pointer to primary key field for that record. +// Returned interface{} value is never untyped nil. +func (s *Dump) PKPointer() interface{} { + return &s.ID +} + +// HasPK returns true if record has non-zero primary key set, false otherwise. +func (s *Dump) HasPK() bool { + return s.ID != DumpTable.z[DumpTable.s.PKFieldIndex] +} + +// SetPK sets record primary key, if possible. +// +// Deprecated: prefer direct field assignment where possible: s.ID = pk. +func (s *Dump) SetPK(pk interface{}) { + reform.SetPK(s, pk) +} + +// check interfaces +var ( + _ reform.View = DumpTable + _ reform.Struct = (*Dump)(nil) + _ reform.Table = DumpTable + _ reform.Record = (*Dump)(nil) + _ fmt.Stringer = (*Dump)(nil) +) + +type dumpLogViewType struct { + s parse.StructInfo + z []interface{} +} + +// Schema returns a schema name in SQL database (""). +func (v *dumpLogViewType) Schema() string { + return v.s.SQLSchema +} + +// Name returns a view or table name in SQL database ("dump_logs"). +func (v *dumpLogViewType) Name() string { + return v.s.SQLName +} + +// Columns returns a new slice of column names for that view or table in SQL database. +func (v *dumpLogViewType) Columns() []string { + return []string{ + "dump_id", + "chunk_id", + "data", + "last_chunk", + } +} + +// NewStruct makes a new struct for that view or table. +func (v *dumpLogViewType) NewStruct() reform.Struct { + return new(DumpLog) +} + +// DumpLogView represents dump_logs view or table in SQL database. +var DumpLogView = &dumpLogViewType{ + s: parse.StructInfo{ + Type: "DumpLog", + SQLName: "dump_logs", + Fields: []parse.FieldInfo{ + {Name: "DumpID", Type: "string", Column: "dump_id"}, + {Name: "ChunkID", Type: "uint32", Column: "chunk_id"}, + {Name: "Data", Type: "string", Column: "data"}, + {Name: "LastChunk", Type: "bool", Column: "last_chunk"}, + }, + PKFieldIndex: -1, + }, + z: new(DumpLog).Values(), +} + +// String returns a string representation of this struct or record. +func (s DumpLog) String() string { + res := make([]string, 4) + res[0] = "DumpID: " + reform.Inspect(s.DumpID, true) + res[1] = "ChunkID: " + reform.Inspect(s.ChunkID, true) + res[2] = "Data: " + reform.Inspect(s.Data, true) + res[3] = "LastChunk: " + reform.Inspect(s.LastChunk, true) + return strings.Join(res, ", ") +} + +// Values returns a slice of struct or record field values. +// Returned interface{} values are never untyped nils. +func (s *DumpLog) Values() []interface{} { + return []interface{}{ + s.DumpID, + s.ChunkID, + s.Data, + s.LastChunk, + } +} + +// Pointers returns a slice of pointers to struct or record fields. +// Returned interface{} values are never untyped nils. +func (s *DumpLog) Pointers() []interface{} { + return []interface{}{ + &s.DumpID, + &s.ChunkID, + &s.Data, + &s.LastChunk, + } +} + +// View returns View object for that struct. +func (s *DumpLog) View() reform.View { + return DumpLogView +} + +// check interfaces +var ( + _ reform.View = DumpLogView + _ reform.Struct = (*DumpLog)(nil) + _ fmt.Stringer = (*DumpLog)(nil) +) + +func init() { + parse.AssertUpToDate(&DumpTable.s, new(Dump)) + parse.AssertUpToDate(&DumpLogView.s, new(DumpLog)) +} diff --git a/managed/services/dump/dump.go b/managed/services/dump/dump.go new file mode 100644 index 0000000000..4f028eb452 --- /dev/null +++ b/managed/services/dump/dump.go @@ -0,0 +1,299 @@ +// 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 dump wraps pmm-dump integration. +package dump + +import ( + "bufio" + "context" + "fmt" + "io" + "os" + "os/exec" + "path/filepath" + "sync" + "sync/atomic" + "time" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "gopkg.in/reform.v1" + + "github.com/percona/pmm/managed/models" +) + +var ErrDumpAlreadyRunning = status.Error(codes.FailedPrecondition, "pmm-dump already running.") + +const ( + pmmDumpBin = "pmm-dump" + dumpsDir = "/srv/dump" +) + +type Service struct { + l *logrus.Entry + + db *reform.DB + + running atomic.Bool + + rw sync.RWMutex + cancel context.CancelFunc +} + +func New(db *reform.DB) *Service { + return &Service{ + l: logrus.WithField("component", "management/backup/backup"), + db: db, + } +} + +type Params struct { + APIKey string + Cookie string + User string + Password string + ServiceNames []string + StartTime *time.Time + EndTime *time.Time + ExportQAN bool + IgnoreLoad bool +} + +func (s *Service) StartDump(params *Params) (string, error) { + // Check if some pmm-dump already running. + if !s.running.CompareAndSwap(false, true) { + return "", ErrDumpAlreadyRunning + } + + dump, err := models.CreateDump(s.db.Querier, models.CreateDumpParams{ + ServiceNames: params.ServiceNames, + StartTime: params.StartTime, + EndTime: params.EndTime, + ExportQAN: params.ExportQAN, + IgnoreLoad: params.IgnoreLoad, + }) + if err != nil { + s.running.Store(false) + return "", errors.Wrap(err, "failed to create dump") + } + + l := s.l.WithField("dump_id", dump.ID) + + ctx, cancel := context.WithCancel(context.Background()) + + s.rw.Lock() + s.cancel = cancel + s.rw.Unlock() + + pmmDumpCmd := exec.CommandContext(ctx, //nolint:gosec + pmmDumpBin, + "export", + "--pmm-url=http://127.0.0.1", + fmt.Sprintf("--dump-path=%s", getDumpFilePath(dump.ID))) + + if params.APIKey != "" { + pmmDumpCmd.Args = append(pmmDumpCmd.Args, fmt.Sprintf(`--pmm-token=%s`, params.APIKey)) + } + + if params.Cookie != "" { + pmmDumpCmd.Args = append(pmmDumpCmd.Args, fmt.Sprintf(`--pmm-cookie=%s`, params.Cookie)) + } + + if params.User != "" { + pmmDumpCmd.Args = append(pmmDumpCmd.Args, fmt.Sprintf(`--pmm-user=%s`, params.User)) + pmmDumpCmd.Args = append(pmmDumpCmd.Args, fmt.Sprintf(`--pmm-pass=%s`, params.Password)) + } + + for _, serviceName := range params.ServiceNames { + pmmDumpCmd.Args = append(pmmDumpCmd.Args, fmt.Sprintf("--instance=%s", serviceName)) + } + + if params.StartTime != nil { + pmmDumpCmd.Args = append(pmmDumpCmd.Args, fmt.Sprintf("--start-ts=%s", params.StartTime.Format(time.RFC3339))) + } + + if params.EndTime != nil { + pmmDumpCmd.Args = append(pmmDumpCmd.Args, fmt.Sprintf("--end-ts=%s", params.EndTime.Format(time.RFC3339))) + } + + if params.ExportQAN { + pmmDumpCmd.Args = append(pmmDumpCmd.Args, "--dump-qan") + } + + if params.IgnoreLoad { + pmmDumpCmd.Args = append(pmmDumpCmd.Args, "--ignore-load") + } + + pReader, pWriter := io.Pipe() + pmmDumpCmd.Stdout = pWriter + pmmDumpCmd.Stderr = pWriter + + go func() { + defer pReader.Close() //nolint:errcheck + + err := s.persistLogs(dump.ID, pReader) + if err != nil && !errors.Is(err, context.Canceled) { + l.Errorf("Dump logs persisting failed: %v", err) + } + + l.Info("Dump logs saved.") + }() + + go func() { + // Switch running flag back to false + defer s.running.Store(false) + defer s.cancel() + defer pWriter.Close() //nolint:errcheck + + err := pmmDumpCmd.Run() + if err != nil { + l.Errorf("Failed to execute pmm-dump: %v", err) + + s.setDumpStatus(dump.ID, models.DumpStatusError) + return + } + + s.setDumpStatus(dump.ID, models.DumpStatusSuccess) + l.WithField("dump_id", dump.ID).Info("Dump done.") + }() + + return dump.ID, nil +} + +func (s *Service) DeleteDump(dumpID string) error { + dump, err := models.FindDumpByID(s.db.Querier, dumpID) + if err != nil { + return errors.Wrap(err, "failed to find dump") + } + + filePath := getDumpFilePath(dump.ID) + err = validateFilePath(filePath) + if err != nil && !errors.Is(err, os.ErrNotExist) { + return errors.WithStack(err) + } + + err = os.Remove(filePath) + if err != nil && !errors.Is(err, os.ErrNotExist) { + return errors.Wrap(err, "failed to remove pmm-dump files") + } + + if err = models.DeleteDump(s.db.Querier, dumpID); err != nil { + return errors.Wrap(err, "failed to delete dump") + } + + return nil +} + +func (s *Service) GetFilePathsForDumps(dumpIDs []string) (map[string]string, error) { + dumps, err := models.FindDumpsByIDs(s.db.Querier, dumpIDs) + if err != nil { + return nil, err + } + + res := make(map[string]string, len(dumps)) + for _, d := range dumps { + if d.Status != models.DumpStatusSuccess { + s.l.Warnf("Dump with id %s is in %s state. Skiping it.", d.ID, d.Status) + continue + } + filePath := getDumpFilePath(d.ID) + if err = validateFilePath(filePath); err != nil { + return nil, errors.WithStack(err) + } + + res[d.ID] = filePath + } + + return res, nil +} + +func (s *Service) setDumpStatus(dumpID string, status models.DumpStatus) { + if err := s.db.InTransaction(func(t *reform.TX) error { + return models.UpdateDumpStatus(t.Querier, dumpID, status) + }); err != nil { + s.l.Warnf("Failed to update dupm status: %+v", err) + } +} + +func (s *Service) persistLogs(dumpID string, r io.Reader) error { + scanner := bufio.NewScanner(r) + var err error + var chunkN uint32 + + for scanner.Scan() { + nErr := s.saveLogChunk(dumpID, atomic.AddUint32(&chunkN, 1)-1, scanner.Text(), false) + if nErr != nil { + s.l.Warnf("failed to read pmm-dump logs: %v", err) + return errors.WithStack(nErr) + } + } + + if err = scanner.Err(); err != nil { + s.l.Warnf("Failed to read pmm-dump logs: %+v", err) + nErr := s.saveLogChunk(dumpID, atomic.AddUint32(&chunkN, 1)-1, err.Error(), false) + if nErr != nil { + return errors.WithStack(nErr) + } + } + + nErr := s.saveLogChunk(dumpID, atomic.AddUint32(&chunkN, 1)-1, "", true) + if nErr != nil { + return errors.WithStack(nErr) + } + + return nil +} + +func (s *Service) saveLogChunk(dumpID string, chunkN uint32, text string, last bool) error { + if _, err := models.CreateDumpLog(s.db.Querier, models.CreateDumpLogParams{ + DumpID: dumpID, + ChunkID: atomic.AddUint32(&chunkN, 1) - 1, + Data: text, + LastChunk: last, + }); err != nil { + return errors.Wrap(err, "failed to save pmm-dump log chunk") + } + + return nil +} + +func (s *Service) StopDump() { + s.rw.RLock() + defer s.rw.RUnlock() + + s.cancel() +} + +func getDumpFilePath(id string) string { + return fmt.Sprintf("%s/%s.tar.gz", dumpsDir, id) +} + +func validateFilePath(path string) error { + c := filepath.Clean(path) + r, err := filepath.EvalSymlinks(c) + if err != nil { + return errors.Wrap(err, "unsafe or invalid dump filepath") + } + + if path != r { + return errors.Errorf("actual file path doesn't match expected, that may be caused by symlinks "+ + "of path traversal, expected path: %s, actual: %s", path, r) + } + + return nil +} diff --git a/managed/services/management/dump/deps.go b/managed/services/management/dump/deps.go new file mode 100644 index 0000000000..b774f32065 --- /dev/null +++ b/managed/services/management/dump/deps.go @@ -0,0 +1,26 @@ +// 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 dump + +import "github.com/percona/pmm/managed/services/dump" + +//go:generate ../../../../bin/mockery --name=dumpService --case=snake --inpackage --testonly + +type dumpService interface { + StartDump(params *dump.Params) (string, error) + DeleteDump(dumpID string) error + GetFilePathsForDumps(dumpIDs []string) (map[string]string, error) +} diff --git a/managed/services/management/dump/dump.go b/managed/services/management/dump/dump.go new file mode 100644 index 0000000000..6ea6026c6b --- /dev/null +++ b/managed/services/management/dump/dump.go @@ -0,0 +1,308 @@ +// 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 dump exposes PMM Dump API. +package dump + +import ( + "bufio" + "context" + "encoding/base64" + "os" + "path" + "path/filepath" + "strings" + + "github.com/AlekSi/pointer" + "github.com/pkg/errors" + "github.com/pkg/sftp" + "github.com/sirupsen/logrus" + "golang.org/x/crypto/ssh" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/timestamppb" + "gopkg.in/reform.v1" + + dumpv1beta1 "github.com/percona/pmm/api/managementpb/dump" + "github.com/percona/pmm/managed/models" + "github.com/percona/pmm/managed/services/dump" + "github.com/percona/pmm/managed/services/grafana" +) + +type Service struct { + db *reform.DB + l *logrus.Entry + + dumpService dumpService + grafanaClient *grafana.Client + + dumpv1beta1.UnimplementedDumpsServer +} + +func New(db *reform.DB, grafanaClient *grafana.Client, dumpService dumpService) *Service { + return &Service{ + db: db, + dumpService: dumpService, + grafanaClient: grafanaClient, + l: logrus.WithField("component", "management/dump"), + } +} + +func (s *Service) StartDump(ctx context.Context, req *dumpv1beta1.StartDumpRequest) (*dumpv1beta1.StartDumpResponse, error) { + md, ok := metadata.FromIncomingContext(ctx) + if !ok { + return nil, errors.New("can't get request metadata") + } + + // Here we're trying to extract authentication credentials from incoming request. We need to forward them to pmm-dump tool. + authHeader, cookieHeader := md.Get("grpcgateway-authorization"), md.Get("grpcgateway-cookie") + + // pmm-dump supports user/pass authentication, API token or cookie. + var token, cookie, user, password string + if len(authHeader) != 0 { + // If auth header type is `Basic` try to extract user and password. + if basic, ok := strings.CutPrefix(authHeader[0], "Basic"); ok { + decodedBasic, err := base64.StdEncoding.DecodeString(strings.TrimSpace(basic)) + if err != nil { + return nil, errors.Wrap(err, "failed to decode basic authorization header") + } + + s := strings.Split(string(decodedBasic), ":") + if len(s) < 2 { + return nil, errors.New("failed to parse basic authorization header") + } + user, password = s[0], s[1] + } + + // If auth header type is `Basic` try to extract token. + if bearer, ok := strings.CutPrefix(authHeader[0], "Bearer"); ok { + token = strings.TrimSpace(bearer) + } + } + + // If auth cookie is present try to extract cookie value. + if len(cookieHeader) != 0 { + cookies := strings.Split(cookieHeader[0], ";") + for _, c := range cookies { + if auth, ok := strings.CutPrefix(strings.TrimSpace(c), "grafana_session="); ok { + cookie = auth + } + } + } + + params := &dump.Params{ + APIKey: token, + Cookie: cookie, + User: user, + Password: password, + ServiceNames: req.ServiceNames, + ExportQAN: req.ExportQan, + IgnoreLoad: req.IgnoreLoad, + } + + if req.StartTime != nil { + startTime := req.StartTime.AsTime() + params.StartTime = &startTime + } + + if req.EndTime != nil { + endTime := req.EndTime.AsTime() + params.EndTime = &endTime + } + + if params.StartTime != nil && params.EndTime != nil { + if params.StartTime.After(*params.EndTime) { + return nil, status.Error(codes.InvalidArgument, "Dump start time can't be greater than end time") + } + } + + dumpID, err := s.dumpService.StartDump(params) + if err != nil { + return nil, err + } + + return &dumpv1beta1.StartDumpResponse{DumpId: dumpID}, nil +} + +func (s *Service) ListDumps(_ context.Context, _ *dumpv1beta1.ListDumpsRequest) (*dumpv1beta1.ListDumpsResponse, error) { + dumps, err := models.FindDumps(s.db.Querier, models.DumpFilters{}) + if err != nil { + return nil, err + } + + dumpsResponse := make([]*dumpv1beta1.Dump, 0, len(dumps)) + for _, dump := range dumps { + d, err := convertDump(dump) + if err != nil { + return nil, err + } + + dumpsResponse = append(dumpsResponse, d) + } + + return &dumpv1beta1.ListDumpsResponse{ + Dumps: dumpsResponse, + }, nil +} + +func (s *Service) DeleteDump(_ context.Context, req *dumpv1beta1.DeleteDumpRequest) (*dumpv1beta1.DeleteDumpResponse, error) { + for _, id := range req.DumpIds { + if err := s.dumpService.DeleteDump(id); err != nil { + return nil, err + } + } + + return &dumpv1beta1.DeleteDumpResponse{}, nil +} + +func (s *Service) GetDumpLogs(_ context.Context, req *dumpv1beta1.GetLogsRequest) (*dumpv1beta1.GetLogsResponse, error) { + filter := models.DumpLogsFilter{ + DumpID: req.DumpId, + Offset: int(req.Offset), + } + if req.Limit > 0 { + filter.Limit = pointer.ToInt(int(req.Limit)) + } + + dumpLogs, err := models.FindDumpLogs(s.db.Querier, filter) + if err != nil { + return nil, err + } + + res := &dumpv1beta1.GetLogsResponse{ + Logs: make([]*dumpv1beta1.LogChunk, 0, len(dumpLogs)), + } + for _, log := range dumpLogs { + if log.LastChunk { + res.End = true + break + } + res.Logs = append(res.Logs, &dumpv1beta1.LogChunk{ + ChunkId: log.ChunkID, + Data: log.Data, + }) + } + + return res, nil +} + +func (s *Service) UploadDump(_ context.Context, req *dumpv1beta1.UploadDumpRequest) (*dumpv1beta1.UploadDumpResponse, error) { + filePaths, err := s.dumpService.GetFilePathsForDumps(req.DumpIds) + if err != nil { + return nil, err + } + + if req.SftpParameters == nil { + return nil, status.Error(codes.InvalidArgument, "SFTP parameters are missing.") + } + + var config ssh.Config + config.SetDefaults() + config.KeyExchanges = append(config.KeyExchanges, + "diffie-hellman-group-exchange-sha256", + "diffie-hellman-group-exchange-sha1") + conf := &ssh.ClientConfig{ + User: req.SftpParameters.User, + Auth: []ssh.AuthMethod{ + ssh.Password(req.SftpParameters.Password), + }, + // We can't check host key + HostKeyCallback: ssh.InsecureIgnoreHostKey(), //nolint:gosec + Config: config, + } + + sshClient, err := ssh.Dial("tcp", req.SftpParameters.Address, conf) + if err != nil { + return nil, errors.Wrap(err, "failed to open TCP connection to SFTP server") + } + defer sshClient.Close() //nolint:errcheck + + sftpClient, err := sftp.NewClient(sshClient) + if err != nil { + return nil, errors.Wrap(err, "failed to create SFTP client") + } + defer sftpClient.Close() //nolint:errcheck + + for _, filePath := range filePaths { + if err = s.uploadFile(sftpClient, filePath, req.SftpParameters.Directory); err != nil { + return nil, errors.Wrap(err, "failed to upload file on SFTP server") + } + } + + return &dumpv1beta1.UploadDumpResponse{}, nil +} + +func (s *Service) uploadFile(client *sftp.Client, localFilePath, remoteDir string) error { + fileName := filepath.Base(localFilePath) + remoteFilePath := path.Join(remoteDir, fileName) + + nf, err := client.OpenFile(remoteFilePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC) + if err != nil { + return errors.Wrap(err, "failed to create file on SFTP server") + } + + f, err := os.Open(localFilePath) //nolint:gosec + if err != nil { + return errors.Wrap(err, "failed to open dump file") + } + defer func() { + if err := f.Close(); err != nil { + s.l.Errorf("Failed to close file: %+v", err) + } + }() + if _, err = bufio.NewReader(f).WriteTo(nf); err != nil { + return errors.Wrap(err, "failed to write dump file on SFTP server") + } + + return nil +} + +func convertDump(dump *models.Dump) (*dumpv1beta1.Dump, error) { + ds, err := convertDumpStatus(dump.Status) + if err != nil { + return nil, errors.Wrap(err, "failed to convert dump ds") + } + + d := &dumpv1beta1.Dump{ + DumpId: dump.ID, + Status: ds, + ServiceNames: dump.ServiceNames, + CreatedAt: timestamppb.New(dump.CreatedAt), + } + + if dump.StartTime != nil { + d.StartTime = timestamppb.New(*dump.StartTime) + } + + if dump.EndTime != nil { + d.EndTime = timestamppb.New(*dump.EndTime) + } + + return d, nil +} + +func convertDumpStatus(status models.DumpStatus) (dumpv1beta1.DumpStatus, error) { + switch status { + case models.DumpStatusSuccess: + return dumpv1beta1.DumpStatus_DUMP_STATUS_SUCCESS, nil + case models.DumpStatusError: + return dumpv1beta1.DumpStatus_DUMP_STATUS_ERROR, nil + case models.DumpStatusInProgress: + return dumpv1beta1.DumpStatus_DUMP_STATUS_IN_PROGRESS, nil + default: + return dumpv1beta1.DumpStatus_DUMP_STATUS_INVALID, errors.Errorf("invalid status '%s'", status) + } +} diff --git a/managed/services/management/dump/mock_dump_service_test.go b/managed/services/management/dump/mock_dump_service_test.go new file mode 100644 index 0000000000..051bfa8db8 --- /dev/null +++ b/managed/services/management/dump/mock_dump_service_test.go @@ -0,0 +1,93 @@ +// Code generated by mockery v2.36.0. DO NOT EDIT. + +package dump + +import ( + mock "github.com/stretchr/testify/mock" + + servicesdump "github.com/percona/pmm/managed/services/dump" +) + +// mockDumpService is an autogenerated mock type for the dumpService type +type mockDumpService struct { + mock.Mock +} + +// DeleteDump provides a mock function with given fields: dumpID +func (_m *mockDumpService) DeleteDump(dumpID string) error { + ret := _m.Called(dumpID) + + var r0 error + if rf, ok := ret.Get(0).(func(string) error); ok { + r0 = rf(dumpID) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// GetFilePathsForDumps provides a mock function with given fields: dumpIDs +func (_m *mockDumpService) GetFilePathsForDumps(dumpIDs []string) (map[string]string, error) { + ret := _m.Called(dumpIDs) + + var r0 map[string]string + var r1 error + if rf, ok := ret.Get(0).(func([]string) (map[string]string, error)); ok { + return rf(dumpIDs) + } + if rf, ok := ret.Get(0).(func([]string) map[string]string); ok { + r0 = rf(dumpIDs) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(map[string]string) + } + } + + if rf, ok := ret.Get(1).(func([]string) error); ok { + r1 = rf(dumpIDs) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// StartDump provides a mock function with given fields: params +func (_m *mockDumpService) StartDump(params *servicesdump.Params) (string, error) { + ret := _m.Called(params) + + var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(*servicesdump.Params) (string, error)); ok { + return rf(params) + } + if rf, ok := ret.Get(0).(func(*servicesdump.Params) string); ok { + r0 = rf(params) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(*servicesdump.Params) error); ok { + r1 = rf(params) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// newMockDumpService creates a new instance of mockDumpService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func newMockDumpService(t interface { + mock.TestingT + Cleanup(func()) +}, +) *mockDumpService { + mock := &mockDumpService{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/managed/services/telemetry/config.default.yml b/managed/services/telemetry/config.default.yml index 3d978acae6..301a8a0f10 100644 --- a/managed/services/telemetry/config.default.yml +++ b/managed/services/telemetry/config.default.yml @@ -165,7 +165,7 @@ telemetry: summary: "Runtime for general collector of MongoDB exporter" data: - metric_name: "mongodb_collector_scrape_time_general" - value : 1 + value: 1 - id: MongoDBExporterMetricsDiagnosticDataStats source: VM @@ -173,7 +173,7 @@ telemetry: summary: "Runtime for diagnostic data collector of MongoDB exporter" data: - metric_name: "mongodb_collector_scrape_time_diagnostic_data" - value : 1 + value: 1 - id: MongoDBExporterMetricsCollectionStats source: VM @@ -181,7 +181,7 @@ telemetry: summary: "Runtime for collection statistics collector of MongoDB exporter" data: - metric_name: "mongodb_collector_scrape_time_collstats" - value : 1 + value: 1 - id: MongoDBExporterMetricsDBStats source: VM @@ -189,7 +189,7 @@ telemetry: summary: "Runtime for database statistics collector of MongoDB exporter" data: - metric_name: "mongodb_collector_scrape_time_dbstats" - value : 1 + value: 1 - id: MongoDBExporterMetricsIndexStats source: VM @@ -197,7 +197,7 @@ telemetry: summary: "Runtime for index statistics collector of MongoDB exporter" data: - metric_name: "mongodb_collector_scrape_time_indexstats" - value : 1 + value: 1 - id: MongoDBExporterMetricsTopStats source: VM @@ -205,7 +205,7 @@ telemetry: summary: "Runtime for top metrics collector of MongoDB exporter" data: - metric_name: "mongodb_collector_scrape_time_top" - value : 1 + value: 1 - id: MongoDBExporterMetricsReplicationStatus source: VM @@ -213,7 +213,7 @@ telemetry: summary: "Runtime for replication status collector of MongoDB exporter" data: - metric_name: "mongodb_collector_scrape_time_replset_status" - value : 1 + value: 1 - id: MongoDBEdition source: VM @@ -917,6 +917,38 @@ telemetry: - metric_name: "7" column: "count" + # PMM-Dump information + - id: PMMDumpsPerformed + source: PMMDB_SELECT + query: > + status, + ARRAY_LENGTH(service_names,1) as services_count, + end_time - start_time as timerange, + ignore_load, + export_qan, + count(*) + from dumps + where created_at BETWEEN NOW() - INTERVAL '24 HOURS' AND NOW() + group by status, services_count, timerange, ignore_load, export_qan; + + transform: + type: JSON + metric: pmm_dumps_performed + summary: "How many PMM dumps were created" + data: + - metric_name: "1" + column: "status" + - metric_name: "2" + column: "services_count" + - metric_name: "3" + column: "timerange" + - metric_name: "4" + column: "ignore_load" + - metric_name: "5" + column: "export_qan" + - metric_name: "6" + column: "count" + - id: PXCClusterNodesCount source: VM query: (min by (wsrep_cluster_state_uuid) (mysql_global_status_wsrep_cluster_size * on (service_id) group_left(wsrep_cluster_state_uuid) mysql_galera_status_info)) diff --git a/update/ansible/playbook/tasks/roles/nginx/files/conf.d/pmm.conf b/update/ansible/playbook/tasks/roles/nginx/files/conf.d/pmm.conf index 1450c7c7fe..9a4633467f 100644 --- a/update/ansible/playbook/tasks/roles/nginx/files/conf.d/pmm.conf +++ b/update/ansible/playbook/tasks/roles/nginx/files/conf.d/pmm.conf @@ -239,6 +239,11 @@ proxy_set_header Connection ""; } + # pmm-dump artifacts + location /dump { + alias /srv/dump/; + } + # This localtion stores static content for general pmm-server purposes. # Ex.: local-rss.xml - contains Percona's news when no internet connection. location /pmm-static { 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 04/14] 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 05/14] 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 06/14] 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 07/14] 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 08/14] 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 09/14] 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 10/14] 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 11/14] 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 12/14] 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 13/14] 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 14/14] 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",