From 1062f5a1e221682943b4a328b28faf481dadb700 Mon Sep 17 00:00:00 2001 From: Nurlan Moldomurov Date: Mon, 24 Jun 2024 11:38:36 +0300 Subject: [PATCH] PMM-12762 Get notified about new version. (#2951) * PMM-11261 Update PMM via watchtower * PMM-11261 address comments and fix linter * PMM-11261 fix linters and tests. * PMM-11261 fix linters and tests. * PMM-11261 fix linters. * PMM-11261 fix linters. * Update managed/cmd/pmm-managed/main.go Co-authored-by: Alex Demidoff * Update managed/cmd/pmm-managed/main.go Co-authored-by: Alex Demidoff * PMM-11261 Address comments. * PMM-11261 fix build. * Update docker-compose.yml * Update docker-compose.yml Co-authored-by: Alex Demidoff * Update docker-compose.yml Co-authored-by: Alex Demidoff * PMM-12762 Get notified about new version. * PMM-11261 Replace switch with if-else * PMM-12762 Fix conflicts, tests and linters. * PMM-12762 Add license header. * PMM-12762 Get notified about new version. * PMM-12762 Fix deadlock * PMM-12762 Fix linters. * PMM-12762 add more log info. * PMM-12762 add more log info. * PMM-12762 add more log info. * PMM-12762 add more log info. * PMM-12762 add more log info. * PMM-12762 add more log info. * PMM-12762 set PMM version. * PMM-12762 set PMM version. * Update managed/services/server/updater.go Co-authored-by: Alex Demidoff * PMM-12762 fix merge conflicts. * Update .github/workflows/managed.yml Co-authored-by: Alex Demidoff * PMM-12762 sort env variables. * PMM-13028 Non blocking StartUpgrade operation (#2972) * PMM-13028 Non blocking StartUpgrade operation * PMM-13028 Fix imports * PMM-13028 Fix linters --------- Co-authored-by: Alex Demidoff Co-authored-by: idoko --- .github/workflows/clean.yml | 2 - .github/workflows/managed.yml | 8 + api-tests/server/updates_test.go | 8 +- .../client/server/check_updates_responses.go | 10 +- api/serverpb/json/serverpb.json | 9 +- api/serverpb/server.pb.go | 948 ++++++++++-------- api/serverpb/server.pb.validate.go | 135 +++ api/serverpb/server.proto | 11 +- api/swagger/swagger-dev.json | 9 +- api/swagger/swagger.json | 9 +- descriptor.bin | Bin 705751 -> 708600 bytes docker-compose.yml | 3 +- managed/Makefile | 2 +- managed/cmd/pmm-managed/main.go | 24 +- managed/services/server/deps.go | 17 +- .../services/{supervisord => server}/logs.go | 8 +- .../{supervisord => server}/logs_test.go | 11 +- .../server/mock_supervisord_service_test.go | 155 --- .../{supervisord => server}/pprof_config.go | 2 +- managed/services/server/server.go | 53 +- managed/services/server/updater.go | 372 +++++-- managed/services/server/updater_test.go | 274 +++++ managed/services/supervisord/deps.go | 9 - .../services/supervisord/devcontainer_test.go | 168 +--- managed/services/supervisord/maintail_test.go | 12 - managed/services/supervisord/pmm_config.go | 16 - .../supervisord/pmm_update_checker.go | 170 ---- managed/services/supervisord/supervisord.go | 177 +--- .../services/supervisord/supervisord_test.go | 13 +- .../supervisord.d/pmm-db_disabled.ini | 16 - .../testdata/supervisord.d/pmm-db_enabled.ini | 16 - update/main.go | 92 -- update/pkg/yum/info.go | 117 --- update/pkg/yum/info_test.go | 683 ------------- update/pkg/yum/yum.go | 154 --- update/pkg/yum/yum_test.go | 100 -- version/parsed.go | 13 + version/update.go | 15 +- 38 files changed, 1363 insertions(+), 2478 deletions(-) rename managed/services/{supervisord => server}/logs.go (97%) rename managed/services/{supervisord => server}/logs_test.go (95%) rename managed/services/{supervisord => server}/pprof_config.go (97%) create mode 100644 managed/services/server/updater_test.go delete mode 100644 managed/services/supervisord/pmm_update_checker.go delete mode 100644 update/pkg/yum/info.go delete mode 100644 update/pkg/yum/info_test.go delete mode 100644 update/pkg/yum/yum.go delete mode 100644 update/pkg/yum/yum_test.go diff --git a/.github/workflows/clean.yml b/.github/workflows/clean.yml index c3e0ad8709..f976dee5b8 100644 --- a/.github/workflows/clean.yml +++ b/.github/workflows/clean.yml @@ -15,8 +15,6 @@ jobs: go: - version: 1.22.x may-fail: false - - version: tip - may-fail: true continue-on-error: ${{ matrix.go.may-fail }} runs-on: ubuntu-22.04 diff --git a/.github/workflows/managed.yml b/.github/workflows/managed.yml index 9302d402cc..00f6ae78eb 100644 --- a/.github/workflows/managed.yml +++ b/.github/workflows/managed.yml @@ -121,3 +121,11 @@ jobs: env | sort go env | sort git status + docker exec pmm-server env | sort + docker exec pmm-server go env | sort + docker exec pmm-server supervisorctl status || true + services=$(docker exec pmm-server supervisorctl status | awk '{print $1}') + while IFS= read -r service; do + echo "Logs for $service:" + docker exec pmm-server supervisorctl tail $service + done <<< "$services" \ No newline at end of file diff --git a/api-tests/server/updates_test.go b/api-tests/server/updates_test.go index f4b8c68336..4285182f17 100644 --- a/api-tests/server/updates_test.go +++ b/api-tests/server/updates_test.go @@ -67,7 +67,6 @@ func TestCheckUpdates(t *testing.T) { require.NotEmpty(t, res.Payload.Latest) assert.True(t, strings.HasPrefix(res.Payload.Latest.Version, "2."), "latest.version = %q should have '2.' prefix", res.Payload.Latest.Version) - assert.NotEmpty(t, res.Payload.Latest.FullVersion) require.NotEmpty(t, res.Payload.Latest.Timestamp) ts = time.Time(res.Payload.Latest.Timestamp) hour, min, _ = ts.Clock() @@ -75,12 +74,13 @@ func TestCheckUpdates(t *testing.T) { assert.Zero(t, min, "latest.timestamp should contain only date") if res.Payload.UpdateAvailable { - assert.NotEqual(t, res.Payload.Installed.FullVersion, res.Payload.Latest.FullVersion) + assert.NotEmpty(t, res.Payload.Latest.Tag) + assert.NotEqual(t, res.Payload.Installed.FullVersion, res.Payload.Latest.Version) assert.NotEqual(t, res.Payload.Installed.Timestamp, res.Payload.Latest.Timestamp) assert.True(t, strings.HasPrefix(res.Payload.LatestNewsURL, "https://per.co.na/pmm/2."), "latest_news_url = %q", res.Payload.LatestNewsURL) } else { - assert.Equal(t, res.Payload.Installed.FullVersion, res.Payload.Latest.FullVersion) assert.Equal(t, res.Payload.Installed.Timestamp, res.Payload.Latest.Timestamp) + assert.Empty(t, res.Payload.Installed.FullVersion, res.Payload.Latest.Version) assert.Empty(t, res.Payload.LatestNewsURL, "latest_news_url should be empty") } assert.NotEmpty(t, res.Payload.LastCheck) @@ -108,7 +108,7 @@ func TestCheckUpdates(t *testing.T) { require.NoError(t, err) assert.Equal(t, res.Payload.Installed, resForce.Payload.Installed) - assert.Equal(t, resForce.Payload.Installed.FullVersion != resForce.Payload.Latest.FullVersion, resForce.Payload.UpdateAvailable) + assert.Equal(t, resForce.Payload.Latest.Tag != "", resForce.Payload.UpdateAvailable) assert.NotEqual(t, res.Payload.LastCheck, resForce.Payload.LastCheck) }) } diff --git a/api/serverpb/json/client/server/check_updates_responses.go b/api/serverpb/json/client/server/check_updates_responses.go index 93cda62aae..fb791b2c07 100644 --- a/api/serverpb/json/client/server/check_updates_responses.go +++ b/api/serverpb/json/client/server/check_updates_responses.go @@ -637,17 +637,17 @@ func (o *CheckUpdatesOKBodyInstalled) UnmarshalBinary(b []byte) error { } /* -CheckUpdatesOKBodyLatest VersionInfo describes component version, or PMM Server as a whole. +CheckUpdatesOKBodyLatest check updates OK body latest swagger:model CheckUpdatesOKBodyLatest */ type CheckUpdatesOKBodyLatest struct { - // User-visible version. + // PMM Version. Version string `json:"version,omitempty"` - // Full version for debugging. - FullVersion string `json:"full_version,omitempty"` + // Docker image tag. + Tag string `json:"tag,omitempty"` - // Build or release date. + // Release date. // Format: date-time Timestamp strfmt.DateTime `json:"timestamp,omitempty"` } diff --git a/api/serverpb/json/serverpb.json b/api/serverpb/json/serverpb.json index 3c3baa749c..f425d7fc35 100644 --- a/api/serverpb/json/serverpb.json +++ b/api/serverpb/json/serverpb.json @@ -720,22 +720,21 @@ "x-order": 4 }, "latest": { - "description": "VersionInfo describes component version, or PMM Server as a whole.", "type": "object", "properties": { - "full_version": { - "description": "Full version for debugging.", + "tag": { + "description": "Docker image tag.", "type": "string", "x-order": 1 }, "timestamp": { - "description": "Build or release date.", + "description": "Release date.", "type": "string", "format": "date-time", "x-order": 2 }, "version": { - "description": "User-visible version.", + "description": "PMM Version.", "type": "string", "x-order": 0 } diff --git a/api/serverpb/server.pb.go b/api/serverpb/server.pb.go index ead8dcae93..5798f6e814 100644 --- a/api/serverpb/server.pb.go +++ b/api/serverpb/server.pb.go @@ -484,6 +484,72 @@ func (x *CheckUpdatesRequest) GetOnlyInstalledVersion() bool { return false } +type DockerVersionInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // PMM Version. + Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"` + // Docker image tag. + Tag string `protobuf:"bytes,2,opt,name=tag,proto3" json:"tag,omitempty"` + // Release date. + Timestamp *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` +} + +func (x *DockerVersionInfo) Reset() { + *x = DockerVersionInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_serverpb_server_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DockerVersionInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DockerVersionInfo) ProtoMessage() {} + +func (x *DockerVersionInfo) ProtoReflect() protoreflect.Message { + mi := &file_serverpb_server_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 DockerVersionInfo.ProtoReflect.Descriptor instead. +func (*DockerVersionInfo) Descriptor() ([]byte, []int) { + return file_serverpb_server_proto_rawDescGZIP(), []int{8} +} + +func (x *DockerVersionInfo) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *DockerVersionInfo) GetTag() string { + if x != nil { + return x.Tag + } + return "" +} + +func (x *DockerVersionInfo) GetTimestamp() *timestamppb.Timestamp { + if x != nil { + return x.Timestamp + } + return nil +} + type CheckUpdatesResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -492,7 +558,7 @@ type CheckUpdatesResponse struct { // Currently installed PMM Server version. Installed *VersionInfo `protobuf:"bytes,1,opt,name=installed,proto3" json:"installed,omitempty"` // Latest available PMM Server version. - Latest *VersionInfo `protobuf:"bytes,2,opt,name=latest,proto3" json:"latest,omitempty"` + Latest *DockerVersionInfo `protobuf:"bytes,2,opt,name=latest,proto3" json:"latest,omitempty"` // True if there is a PMM Server update available. UpdateAvailable bool `protobuf:"varint,3,opt,name=update_available,json=updateAvailable,proto3" json:"update_available,omitempty"` // Latest available PMM Server release announcement URL. @@ -504,7 +570,7 @@ type CheckUpdatesResponse struct { func (x *CheckUpdatesResponse) Reset() { *x = CheckUpdatesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_serverpb_server_proto_msgTypes[8] + mi := &file_serverpb_server_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -517,7 +583,7 @@ func (x *CheckUpdatesResponse) String() string { func (*CheckUpdatesResponse) ProtoMessage() {} func (x *CheckUpdatesResponse) ProtoReflect() protoreflect.Message { - mi := &file_serverpb_server_proto_msgTypes[8] + mi := &file_serverpb_server_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -530,7 +596,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{8} + return file_serverpb_server_proto_rawDescGZIP(), []int{9} } func (x *CheckUpdatesResponse) GetInstalled() *VersionInfo { @@ -540,7 +606,7 @@ func (x *CheckUpdatesResponse) GetInstalled() *VersionInfo { return nil } -func (x *CheckUpdatesResponse) GetLatest() *VersionInfo { +func (x *CheckUpdatesResponse) GetLatest() *DockerVersionInfo { if x != nil { return x.Latest } @@ -579,7 +645,7 @@ type StartUpdateRequest struct { func (x *StartUpdateRequest) Reset() { *x = StartUpdateRequest{} if protoimpl.UnsafeEnabled { - mi := &file_serverpb_server_proto_msgTypes[9] + mi := &file_serverpb_server_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -592,7 +658,7 @@ func (x *StartUpdateRequest) String() string { func (*StartUpdateRequest) ProtoMessage() {} func (x *StartUpdateRequest) ProtoReflect() protoreflect.Message { - mi := &file_serverpb_server_proto_msgTypes[9] + mi := &file_serverpb_server_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -605,7 +671,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{9} + return file_serverpb_server_proto_rawDescGZIP(), []int{10} } func (x *StartUpdateRequest) GetNewImage() string { @@ -629,7 +695,7 @@ type StartUpdateResponse struct { func (x *StartUpdateResponse) Reset() { *x = StartUpdateResponse{} if protoimpl.UnsafeEnabled { - mi := &file_serverpb_server_proto_msgTypes[10] + mi := &file_serverpb_server_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -642,7 +708,7 @@ func (x *StartUpdateResponse) String() string { func (*StartUpdateResponse) ProtoMessage() {} func (x *StartUpdateResponse) ProtoReflect() protoreflect.Message { - mi := &file_serverpb_server_proto_msgTypes[10] + mi := &file_serverpb_server_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -655,7 +721,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{10} + return file_serverpb_server_proto_rawDescGZIP(), []int{11} } func (x *StartUpdateResponse) GetAuthToken() string { @@ -686,7 +752,7 @@ type UpdateStatusRequest struct { func (x *UpdateStatusRequest) Reset() { *x = UpdateStatusRequest{} if protoimpl.UnsafeEnabled { - mi := &file_serverpb_server_proto_msgTypes[11] + mi := &file_serverpb_server_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -699,7 +765,7 @@ func (x *UpdateStatusRequest) String() string { func (*UpdateStatusRequest) ProtoMessage() {} func (x *UpdateStatusRequest) ProtoReflect() protoreflect.Message { - mi := &file_serverpb_server_proto_msgTypes[11] + mi := &file_serverpb_server_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -712,7 +778,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{11} + return file_serverpb_server_proto_rawDescGZIP(), []int{12} } func (x *UpdateStatusRequest) GetAuthToken() string { @@ -745,7 +811,7 @@ type UpdateStatusResponse struct { func (x *UpdateStatusResponse) Reset() { *x = UpdateStatusResponse{} if protoimpl.UnsafeEnabled { - mi := &file_serverpb_server_proto_msgTypes[12] + mi := &file_serverpb_server_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -758,7 +824,7 @@ func (x *UpdateStatusResponse) String() string { func (*UpdateStatusResponse) ProtoMessage() {} func (x *UpdateStatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_serverpb_server_proto_msgTypes[12] + mi := &file_serverpb_server_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -771,7 +837,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{12} + return file_serverpb_server_proto_rawDescGZIP(), []int{13} } func (x *UpdateStatusResponse) GetLogLines() []string { @@ -812,7 +878,7 @@ type MetricsResolutions struct { func (x *MetricsResolutions) Reset() { *x = MetricsResolutions{} if protoimpl.UnsafeEnabled { - mi := &file_serverpb_server_proto_msgTypes[13] + mi := &file_serverpb_server_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -825,7 +891,7 @@ func (x *MetricsResolutions) String() string { func (*MetricsResolutions) ProtoMessage() {} func (x *MetricsResolutions) ProtoReflect() protoreflect.Message { - mi := &file_serverpb_server_proto_msgTypes[13] + mi := &file_serverpb_server_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -838,7 +904,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{13} + return file_serverpb_server_proto_rawDescGZIP(), []int{14} } func (x *MetricsResolutions) GetHr() *durationpb.Duration { @@ -879,7 +945,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[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -892,7 +958,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[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -905,7 +971,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{15} } func (x *STTCheckIntervals) GetStandardInterval() *durationpb.Duration { @@ -970,7 +1036,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[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -983,7 +1049,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[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -996,7 +1062,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{16} } func (x *Settings) GetUpdatesDisabled() bool { @@ -1127,7 +1193,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[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1140,7 +1206,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[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1153,7 +1219,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{17} } type GetSettingsResponse struct { @@ -1167,7 +1233,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[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1180,7 +1246,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[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1193,7 +1259,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{18} } func (x *GetSettingsResponse) GetSettings() *Settings { @@ -1247,7 +1313,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[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1260,7 +1326,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[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1273,7 +1339,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{19} } func (x *ChangeSettingsRequest) GetEnableUpdates() bool { @@ -1434,7 +1500,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[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1447,7 +1513,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[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1460,7 +1526,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{20} } func (x *ChangeSettingsResponse) GetSettings() *Settings { @@ -1482,7 +1548,7 @@ type AWSInstanceCheckRequest struct { func (x *AWSInstanceCheckRequest) Reset() { *x = AWSInstanceCheckRequest{} if protoimpl.UnsafeEnabled { - mi := &file_serverpb_server_proto_msgTypes[20] + mi := &file_serverpb_server_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1495,7 +1561,7 @@ func (x *AWSInstanceCheckRequest) String() string { func (*AWSInstanceCheckRequest) ProtoMessage() {} func (x *AWSInstanceCheckRequest) ProtoReflect() protoreflect.Message { - mi := &file_serverpb_server_proto_msgTypes[20] + mi := &file_serverpb_server_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1508,7 +1574,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{20} + return file_serverpb_server_proto_rawDescGZIP(), []int{21} } func (x *AWSInstanceCheckRequest) GetInstanceId() string { @@ -1527,7 +1593,7 @@ type AWSInstanceCheckResponse struct { func (x *AWSInstanceCheckResponse) Reset() { *x = AWSInstanceCheckResponse{} if protoimpl.UnsafeEnabled { - mi := &file_serverpb_server_proto_msgTypes[21] + mi := &file_serverpb_server_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1540,7 +1606,7 @@ func (x *AWSInstanceCheckResponse) String() string { func (*AWSInstanceCheckResponse) ProtoMessage() {} func (x *AWSInstanceCheckResponse) ProtoReflect() protoreflect.Message { - mi := &file_serverpb_server_proto_msgTypes[21] + mi := &file_serverpb_server_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1553,7 +1619,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{21} + return file_serverpb_server_proto_rawDescGZIP(), []int{22} } var File_serverpb_server_proto protoreflect.FileDescriptor @@ -1608,324 +1674,332 @@ var file_serverpb_server_proto_rawDesc = []byte{ 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, 0x31, 0x0a, 0x12, 0x53, 0x74, 0x61, - 0x72, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x1b, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x49, 0x6d, 0x61, 0x67, 0x65, 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, 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, 0x6e, 0x22, 0x79, 0x0a, 0x11, 0x44, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x74, 0x61, 0x67, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 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, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x8a, 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, 0x31, 0x0a, 0x06, 0x6c, 0x61, 0x74, + 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x2e, 0x44, 0x6f, 0x63, 0x6b, 0x65, 0x72, 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, 0x31, 0x0a, 0x12, 0x53, 0x74, + 0x61, 0x72, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x49, 0x6d, 0x61, 0x67, 0x65, 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, 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, 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, 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, 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, + 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, 0xe7, 0x06, + 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, 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, 0xe7, 0x06, 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, 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, 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, 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, 0x4a, 0x04, - 0x08, 0x07, 0x10, 0x08, 0x4a, 0x04, 0x08, 0x08, 0x10, 0x09, 0x4a, 0x04, 0x08, 0x0d, 0x10, 0x0e, - 0x4a, 0x04, 0x08, 0x0e, 0x10, 0x0f, 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, 0xce, 0x08, 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, 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, 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, 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, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x4a, 0x04, 0x08, 0x08, 0x10, 0x09, 0x4a, 0x04, - 0x08, 0x09, 0x10, 0x0a, 0x4a, 0x04, 0x08, 0x0a, 0x10, 0x0b, 0x4a, 0x04, 0x08, 0x0f, 0x10, 0x10, - 0x4a, 0x04, 0x08, 0x10, 0x10, 0x11, 0x4a, 0x04, 0x08, 0x11, 0x10, 0x12, 0x4a, 0x04, 0x08, 0x12, - 0x10, 0x13, 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, 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, 0xe5, 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, 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, + 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, 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, 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, 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, 0x4a, + 0x04, 0x08, 0x07, 0x10, 0x08, 0x4a, 0x04, 0x08, 0x08, 0x10, 0x09, 0x4a, 0x04, 0x08, 0x0d, 0x10, + 0x0e, 0x4a, 0x04, 0x08, 0x0e, 0x10, 0x0f, 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, 0xce, 0x08, 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, 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, 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, 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, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x4a, 0x04, 0x08, 0x08, 0x10, 0x09, 0x4a, + 0x04, 0x08, 0x09, 0x10, 0x0a, 0x4a, 0x04, 0x08, 0x0a, 0x10, 0x0b, 0x4a, 0x04, 0x08, 0x0f, 0x10, + 0x10, 0x4a, 0x04, 0x08, 0x10, 0x10, 0x11, 0x4a, 0x04, 0x08, 0x11, 0x10, 0x12, 0x4a, 0x04, 0x08, + 0x12, 0x10, 0x13, 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, 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, 0xe5, 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, 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, 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, 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, + 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, + 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, - 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, + 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 ( @@ -1942,7 +2016,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, 22) + file_serverpb_server_proto_msgTypes = make([]protoimpl.MessageInfo, 23) file_serverpb_server_proto_goTypes = []interface{}{ (DistributionMethod)(0), // 0: server.DistributionMethod (*VersionInfo)(nil), // 1: server.VersionInfo @@ -1953,70 +2027,72 @@ var ( (*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 - (*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 - (*AWSInstanceCheckRequest)(nil), // 21: server.AWSInstanceCheckRequest - (*AWSInstanceCheckResponse)(nil), // 22: server.AWSInstanceCheckResponse - (*timestamppb.Timestamp)(nil), // 23: google.protobuf.Timestamp - (*durationpb.Duration)(nil), // 24: google.protobuf.Duration + (*DockerVersionInfo)(nil), // 9: server.DockerVersionInfo + (*CheckUpdatesResponse)(nil), // 10: server.CheckUpdatesResponse + (*StartUpdateRequest)(nil), // 11: server.StartUpdateRequest + (*StartUpdateResponse)(nil), // 12: server.StartUpdateResponse + (*UpdateStatusRequest)(nil), // 13: server.UpdateStatusRequest + (*UpdateStatusResponse)(nil), // 14: server.UpdateStatusResponse + (*MetricsResolutions)(nil), // 15: server.MetricsResolutions + (*STTCheckIntervals)(nil), // 16: server.STTCheckIntervals + (*Settings)(nil), // 17: server.Settings + (*GetSettingsRequest)(nil), // 18: server.GetSettingsRequest + (*GetSettingsResponse)(nil), // 19: server.GetSettingsResponse + (*ChangeSettingsRequest)(nil), // 20: server.ChangeSettingsRequest + (*ChangeSettingsResponse)(nil), // 21: server.ChangeSettingsResponse + (*AWSInstanceCheckRequest)(nil), // 22: server.AWSInstanceCheckRequest + (*AWSInstanceCheckResponse)(nil), // 23: server.AWSInstanceCheckResponse + (*timestamppb.Timestamp)(nil), // 24: google.protobuf.Timestamp + (*durationpb.Duration)(nil), // 25: google.protobuf.Duration } ) var file_serverpb_server_proto_depIdxs = []int32{ - 23, // 0: server.VersionInfo.timestamp:type_name -> google.protobuf.Timestamp + 24, // 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 - 23, // 6: server.CheckUpdatesResponse.last_check:type_name -> google.protobuf.Timestamp - 24, // 7: server.MetricsResolutions.hr:type_name -> google.protobuf.Duration - 24, // 8: server.MetricsResolutions.mr:type_name -> google.protobuf.Duration - 24, // 9: server.MetricsResolutions.lr:type_name -> google.protobuf.Duration - 24, // 10: server.STTCheckIntervals.standard_interval:type_name -> google.protobuf.Duration - 24, // 11: server.STTCheckIntervals.rare_interval:type_name -> google.protobuf.Duration - 24, // 12: server.STTCheckIntervals.frequent_interval:type_name -> google.protobuf.Duration - 14, // 13: server.Settings.metrics_resolutions:type_name -> server.MetricsResolutions - 24, // 14: server.Settings.data_retention:type_name -> google.protobuf.Duration - 15, // 15: server.Settings.stt_check_intervals:type_name -> server.STTCheckIntervals - 16, // 16: server.GetSettingsResponse.settings:type_name -> server.Settings - 14, // 17: server.ChangeSettingsRequest.metrics_resolutions:type_name -> server.MetricsResolutions - 24, // 18: server.ChangeSettingsRequest.data_retention:type_name -> google.protobuf.Duration - 15, // 19: server.ChangeSettingsRequest.stt_check_intervals:type_name -> server.STTCheckIntervals - 16, // 20: server.ChangeSettingsResponse.settings:type_name -> server.Settings - 2, // 21: server.Server.Version:input_type -> server.VersionRequest - 4, // 22: server.Server.Readiness:input_type -> server.ReadinessRequest - 6, // 23: server.Server.LeaderHealthCheck:input_type -> server.LeaderHealthCheckRequest - 8, // 24: server.Server.CheckUpdates:input_type -> server.CheckUpdatesRequest - 10, // 25: server.Server.StartUpdate:input_type -> server.StartUpdateRequest - 12, // 26: server.Server.UpdateStatus:input_type -> server.UpdateStatusRequest - 17, // 27: server.Server.GetSettings:input_type -> server.GetSettingsRequest - 19, // 28: server.Server.ChangeSettings:input_type -> server.ChangeSettingsRequest - 21, // 29: server.Server.AWSInstanceCheck:input_type -> server.AWSInstanceCheckRequest - 3, // 30: server.Server.Version:output_type -> server.VersionResponse - 5, // 31: server.Server.Readiness:output_type -> server.ReadinessResponse - 7, // 32: server.Server.LeaderHealthCheck:output_type -> server.LeaderHealthCheckResponse - 9, // 33: server.Server.CheckUpdates:output_type -> server.CheckUpdatesResponse - 11, // 34: server.Server.StartUpdate:output_type -> server.StartUpdateResponse - 13, // 35: server.Server.UpdateStatus:output_type -> server.UpdateStatusResponse - 18, // 36: server.Server.GetSettings:output_type -> server.GetSettingsResponse - 20, // 37: server.Server.ChangeSettings:output_type -> server.ChangeSettingsResponse - 22, // 38: server.Server.AWSInstanceCheck:output_type -> server.AWSInstanceCheckResponse - 30, // [30:39] is the sub-list for method output_type - 21, // [21:30] is the sub-list for method input_type - 21, // [21:21] is the sub-list for extension type_name - 21, // [21:21] is the sub-list for extension extendee - 0, // [0:21] is the sub-list for field type_name + 24, // 4: server.DockerVersionInfo.timestamp:type_name -> google.protobuf.Timestamp + 1, // 5: server.CheckUpdatesResponse.installed:type_name -> server.VersionInfo + 9, // 6: server.CheckUpdatesResponse.latest:type_name -> server.DockerVersionInfo + 24, // 7: server.CheckUpdatesResponse.last_check:type_name -> google.protobuf.Timestamp + 25, // 8: server.MetricsResolutions.hr:type_name -> google.protobuf.Duration + 25, // 9: server.MetricsResolutions.mr:type_name -> google.protobuf.Duration + 25, // 10: server.MetricsResolutions.lr:type_name -> google.protobuf.Duration + 25, // 11: server.STTCheckIntervals.standard_interval:type_name -> google.protobuf.Duration + 25, // 12: server.STTCheckIntervals.rare_interval:type_name -> google.protobuf.Duration + 25, // 13: server.STTCheckIntervals.frequent_interval:type_name -> google.protobuf.Duration + 15, // 14: server.Settings.metrics_resolutions:type_name -> server.MetricsResolutions + 25, // 15: server.Settings.data_retention:type_name -> google.protobuf.Duration + 16, // 16: server.Settings.stt_check_intervals:type_name -> server.STTCheckIntervals + 17, // 17: server.GetSettingsResponse.settings:type_name -> server.Settings + 15, // 18: server.ChangeSettingsRequest.metrics_resolutions:type_name -> server.MetricsResolutions + 25, // 19: server.ChangeSettingsRequest.data_retention:type_name -> google.protobuf.Duration + 16, // 20: server.ChangeSettingsRequest.stt_check_intervals:type_name -> server.STTCheckIntervals + 17, // 21: server.ChangeSettingsResponse.settings:type_name -> server.Settings + 2, // 22: server.Server.Version:input_type -> server.VersionRequest + 4, // 23: server.Server.Readiness:input_type -> server.ReadinessRequest + 6, // 24: server.Server.LeaderHealthCheck:input_type -> server.LeaderHealthCheckRequest + 8, // 25: server.Server.CheckUpdates:input_type -> server.CheckUpdatesRequest + 11, // 26: server.Server.StartUpdate:input_type -> server.StartUpdateRequest + 13, // 27: server.Server.UpdateStatus:input_type -> server.UpdateStatusRequest + 18, // 28: server.Server.GetSettings:input_type -> server.GetSettingsRequest + 20, // 29: server.Server.ChangeSettings:input_type -> server.ChangeSettingsRequest + 22, // 30: server.Server.AWSInstanceCheck:input_type -> server.AWSInstanceCheckRequest + 3, // 31: server.Server.Version:output_type -> server.VersionResponse + 5, // 32: server.Server.Readiness:output_type -> server.ReadinessResponse + 7, // 33: server.Server.LeaderHealthCheck:output_type -> server.LeaderHealthCheckResponse + 10, // 34: server.Server.CheckUpdates:output_type -> server.CheckUpdatesResponse + 12, // 35: server.Server.StartUpdate:output_type -> server.StartUpdateResponse + 14, // 36: server.Server.UpdateStatus:output_type -> server.UpdateStatusResponse + 19, // 37: server.Server.GetSettings:output_type -> server.GetSettingsResponse + 21, // 38: server.Server.ChangeSettings:output_type -> server.ChangeSettingsResponse + 23, // 39: server.Server.AWSInstanceCheck:output_type -> server.AWSInstanceCheckResponse + 31, // [31:40] is the sub-list for method output_type + 22, // [22:31] is the sub-list for method input_type + 22, // [22:22] is the sub-list for extension type_name + 22, // [22:22] is the sub-list for extension extendee + 0, // [0:22] is the sub-list for field type_name } func init() { file_serverpb_server_proto_init() } @@ -2122,7 +2198,7 @@ func file_serverpb_server_proto_init() { } } file_serverpb_server_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CheckUpdatesResponse); i { + switch v := v.(*DockerVersionInfo); i { case 0: return &v.state case 1: @@ -2134,7 +2210,7 @@ func file_serverpb_server_proto_init() { } } file_serverpb_server_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StartUpdateRequest); i { + switch v := v.(*CheckUpdatesResponse); i { case 0: return &v.state case 1: @@ -2146,7 +2222,7 @@ func file_serverpb_server_proto_init() { } } file_serverpb_server_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StartUpdateResponse); i { + switch v := v.(*StartUpdateRequest); i { case 0: return &v.state case 1: @@ -2158,7 +2234,7 @@ func file_serverpb_server_proto_init() { } } file_serverpb_server_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UpdateStatusRequest); i { + switch v := v.(*StartUpdateResponse); i { case 0: return &v.state case 1: @@ -2170,7 +2246,7 @@ func file_serverpb_server_proto_init() { } } file_serverpb_server_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UpdateStatusResponse); i { + switch v := v.(*UpdateStatusRequest); i { case 0: return &v.state case 1: @@ -2182,7 +2258,7 @@ func file_serverpb_server_proto_init() { } } file_serverpb_server_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MetricsResolutions); i { + switch v := v.(*UpdateStatusResponse); i { case 0: return &v.state case 1: @@ -2194,7 +2270,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.(*MetricsResolutions); i { case 0: return &v.state case 1: @@ -2206,7 +2282,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.(*STTCheckIntervals); i { case 0: return &v.state case 1: @@ -2218,7 +2294,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.(*Settings); i { case 0: return &v.state case 1: @@ -2230,7 +2306,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.(*GetSettingsRequest); i { case 0: return &v.state case 1: @@ -2242,7 +2318,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.(*GetSettingsResponse); i { case 0: return &v.state case 1: @@ -2254,7 +2330,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.(*ChangeSettingsRequest); i { case 0: return &v.state case 1: @@ -2266,7 +2342,7 @@ func file_serverpb_server_proto_init() { } } file_serverpb_server_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AWSInstanceCheckRequest); i { + switch v := v.(*ChangeSettingsResponse); i { case 0: return &v.state case 1: @@ -2278,6 +2354,18 @@ func file_serverpb_server_proto_init() { } } file_serverpb_server_proto_msgTypes[21].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[22].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*AWSInstanceCheckResponse); i { case 0: return &v.state @@ -2296,7 +2384,7 @@ func file_serverpb_server_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_serverpb_server_proto_rawDesc, NumEnums: 1, - NumMessages: 22, + NumMessages: 23, NumExtensions: 0, NumServices: 1, }, diff --git a/api/serverpb/server.pb.validate.go b/api/serverpb/server.pb.validate.go index 3bba154397..7b80039780 100644 --- a/api/serverpb/server.pb.validate.go +++ b/api/serverpb/server.pb.validate.go @@ -943,6 +943,141 @@ var _ interface { ErrorName() string } = CheckUpdatesRequestValidationError{} +// Validate checks the field values on DockerVersionInfo 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 *DockerVersionInfo) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on DockerVersionInfo 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 +// DockerVersionInfoMultiError, or nil if none found. +func (m *DockerVersionInfo) ValidateAll() error { + return m.validate(true) +} + +func (m *DockerVersionInfo) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for Version + + // no validation rules for Tag + + if all { + switch v := interface{}(m.GetTimestamp()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, DockerVersionInfoValidationError{ + field: "Timestamp", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, DockerVersionInfoValidationError{ + field: "Timestamp", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetTimestamp()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return DockerVersionInfoValidationError{ + field: "Timestamp", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if len(errors) > 0 { + return DockerVersionInfoMultiError(errors) + } + + return nil +} + +// DockerVersionInfoMultiError is an error wrapping multiple validation errors +// returned by DockerVersionInfo.ValidateAll() if the designated constraints +// aren't met. +type DockerVersionInfoMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m DockerVersionInfoMultiError) 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 DockerVersionInfoMultiError) AllErrors() []error { return m } + +// DockerVersionInfoValidationError is the validation error returned by +// DockerVersionInfo.Validate if the designated constraints aren't met. +type DockerVersionInfoValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e DockerVersionInfoValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e DockerVersionInfoValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e DockerVersionInfoValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e DockerVersionInfoValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e DockerVersionInfoValidationError) ErrorName() string { + return "DockerVersionInfoValidationError" +} + +// Error satisfies the builtin error interface +func (e DockerVersionInfoValidationError) 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 %sDockerVersionInfo.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = DockerVersionInfoValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = DockerVersionInfoValidationError{} + // Validate checks the field values on CheckUpdatesResponse 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 629d06b9a8..284d2ed481 100644 --- a/api/serverpb/server.proto +++ b/api/serverpb/server.proto @@ -66,11 +66,20 @@ message CheckUpdatesRequest { bool only_installed_version = 2; } +message DockerVersionInfo { + // PMM Version. + string version = 1; + // Docker image tag. + string tag = 2; + // Release date. + google.protobuf.Timestamp timestamp = 3; +} + message CheckUpdatesResponse { // Currently installed PMM Server version. VersionInfo installed = 1; // Latest available PMM Server version. - VersionInfo latest = 2; + DockerVersionInfo latest = 2; // True if there is a PMM Server update available. bool update_available = 3; // Latest available PMM Server release announcement URL. diff --git a/api/swagger/swagger-dev.json b/api/swagger/swagger-dev.json index 8d2d80a278..054efed28a 100644 --- a/api/swagger/swagger-dev.json +++ b/api/swagger/swagger-dev.json @@ -3558,21 +3558,20 @@ "x-order": 0 }, "latest": { - "description": "VersionInfo describes component version, or PMM Server as a whole.", "type": "object", "properties": { "version": { - "description": "User-visible version.", + "description": "PMM Version.", "type": "string", "x-order": 0 }, - "full_version": { - "description": "Full version for debugging.", + "tag": { + "description": "Docker image tag.", "type": "string", "x-order": 1 }, "timestamp": { - "description": "Build or release date.", + "description": "Release date.", "type": "string", "format": "date-time", "x-order": 2 diff --git a/api/swagger/swagger.json b/api/swagger/swagger.json index 05406132fc..dd3b3b8091 100644 --- a/api/swagger/swagger.json +++ b/api/swagger/swagger.json @@ -714,21 +714,20 @@ "x-order": 0 }, "latest": { - "description": "VersionInfo describes component version, or PMM Server as a whole.", "type": "object", "properties": { "version": { - "description": "User-visible version.", + "description": "PMM Version.", "type": "string", "x-order": 0 }, - "full_version": { - "description": "Full version for debugging.", + "tag": { + "description": "Docker image tag.", "type": "string", "x-order": 1 }, "timestamp": { - "description": "Build or release date.", + "description": "Release date.", "type": "string", "format": "date-time", "x-order": 2 diff --git a/descriptor.bin b/descriptor.bin index 0fef5fb20602e1bcf3e056f394599305747565da..d80175920082e0ee4af4732999a91a98f4301561 100644 GIT binary patch delta 45107 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%0D(v`H2xzDn9e#?!BoHsQN&(bsW!pzK7} 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 a@8EE7G+2#4b8+@~uYc0tfB7B9>C7K{;%e*w delta 43128 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 @@ -242,7 +243,7 @@ services: - GF_DATABASE_URL=postgres://grafana:grafana@pg:5432/grafana # - GF_DATABASE_SSL_MODE=require # - PMM_DEBUG=1 - - GO_VERSION=1.20 + - GO_VERSION=1.22 - PMM_VM_URL=${PMM_VM_URL:-http://victoriametrics:8428/} - PMM_TEST_HA_ENABLE=1 - PMM_TEST_HA_BOOTSTRAP=1 diff --git a/managed/Makefile b/managed/Makefile index 48005e7d3c..e71b80b79c 100644 --- a/managed/Makefile +++ b/managed/Makefile @@ -8,7 +8,7 @@ help: ## Display this help message # `cut` is used to remove first `v` from `git describe` output PMM_RELEASE_PATH ?= ../bin -PMM_RELEASE_VERSION ?= $(shell git describe --always --dirty | cut -b2-) +PMM_RELEASE_VERSION ?= $(shell git describe --always | cut -b2-) PMM_RELEASE_TIMESTAMP ?= $(shell date '+%s') PMM_RELEASE_FULLCOMMIT ?= $(shell git rev-parse HEAD) PMM_RELEASE_BRANCH ?= $(shell git describe --always --contains --all) diff --git a/managed/cmd/pmm-managed/main.go b/managed/cmd/pmm-managed/main.go index 85b0c512f7..9268365c25 100644 --- a/managed/cmd/pmm-managed/main.go +++ b/managed/cmd/pmm-managed/main.go @@ -137,7 +137,7 @@ const ( var pprofSemaphore = semaphore.NewWeighted(1) -func addLogsHandler(mux *http.ServeMux, logs *supervisord.Logs) { +func addLogsHandler(mux *http.ServeMux, logs *server.Logs) { l := logrus.WithField("component", "logs.zip") mux.HandleFunc("/logs.zip", func(rw http.ResponseWriter, req *http.Request) { @@ -147,7 +147,7 @@ func addLogsHandler(mux *http.ServeMux, logs *supervisord.Logs) { if err != nil { l.Debug("Unable to read 'pprof' query param. Using default: pprof=false") } - var pprofConfig *supervisord.PprofConfig + var pprofConfig *server.PprofConfig if pprofQueryParameter { if !pprofSemaphore.TryAcquire(1) { rw.WriteHeader(http.StatusLocked) @@ -160,7 +160,7 @@ func addLogsHandler(mux *http.ServeMux, logs *supervisord.Logs) { defer pprofSemaphore.Release(1) contextTimeout += pProfProfileDuration + pProfTraceDuration - pprofConfig = &supervisord.PprofConfig{ + pprofConfig = &server.PprofConfig{ ProfileDuration: pProfProfileDuration, TraceDuration: pProfTraceDuration, } @@ -335,7 +335,7 @@ func runGRPCServer(ctx context.Context, deps *gRPCServerDeps) { } type http1ServerDeps struct { - logs *supervisord.Logs + logs *server.Logs authServer *grafana.AuthServer } @@ -876,13 +876,12 @@ func main() { //nolint:cyclop,maintidx connectionCheck := agents.NewConnectionChecker(agentsRegistry) serviceInfoBroker := agents.NewServiceInfoBroker(agentsRegistry) - pmmUpdateCheck := supervisord.NewPMMUpdateChecker(logrus.WithField("component", "supervisord/pmm-update-checker")) + updater := server.NewUpdater(*watchtowerHostF, gRPCMessageMaxSize) - logs := supervisord.NewLogs(version.FullInfo(), pmmUpdateCheck, vmParams) + logs := server.NewLogs(version.FullInfo(), updater, vmParams) supervisord := supervisord.New( *supervisordConfigDirF, - pmmUpdateCheck, &models.Params{ VMParams: vmParams, PGParams: &models.PGParams{ @@ -896,8 +895,7 @@ func main() { //nolint:cyclop,maintidx SSLCertPath: *postgresSSLCertPathF, }, HAParams: haParams, - }, - gRPCMessageMaxSize) + }) haService.AddLeaderService(ha.NewStandardService("pmm-agent-runner", func(ctx context.Context) error { return supervisord.StartSupervisedService("pmm-agent") @@ -972,8 +970,6 @@ func main() { //nolint:cyclop,maintidx dumpService := dump.New(db) - updater := server.NewUpdater(supervisord, *watchtowerHostF) - serverParams := &server.Params{ DB: db, VMDB: vmdb, @@ -1088,6 +1084,12 @@ func main() { //nolint:cyclop,maintidx supervisord.Run(ctx) }() + wg.Add(1) + go func() { + defer wg.Done() + updater.Run(ctx) + }() + wg.Add(1) haService.AddLeaderService(ha.NewContextService("telemetry", func(ctx context.Context) error { defer wg.Done() diff --git a/managed/services/server/deps.go b/managed/services/server/deps.go index b675fba85d..2215551ba7 100644 --- a/managed/services/server/deps.go +++ b/managed/services/server/deps.go @@ -17,11 +17,11 @@ package server import ( "context" + "net/url" "time" "github.com/percona/pmm/api/serverpb" "github.com/percona/pmm/managed/models" - "github.com/percona/pmm/version" ) // healthChecker interface wraps all services that implements the IsReady method to report the @@ -73,14 +73,6 @@ type vmAlertExternalRules interface { // supervisordService is a subset of methods of supervisord.Service used by this package. // We use it instead of real type for testing and to avoid dependency cycle. type supervisordService interface { - InstalledPMMVersion(ctx context.Context) *version.PackageInfo - LastCheckUpdatesResult(ctx context.Context) (*version.UpdateCheckResult, time.Time) - ForceCheckUpdates(ctx context.Context) error - - StartUpdate() (uint32, error) - UpdateRunning() bool - UpdateLog(offset uint32) ([]string, uint32, error) - UpdateConfiguration(settings *models.Settings, ssoDetails *models.PerconaSSODetails) error } @@ -108,3 +100,10 @@ type templatesService interface { type haService interface { IsLeader() bool } + +// victoriaMetricsParams is a subset of methods of models.VMParams used by this package. +// We use it instead of real type to avoid dependency cycle. +type victoriaMetricsParams interface { + ExternalVM() bool + URLFor(path string) (*url.URL, error) +} diff --git a/managed/services/supervisord/logs.go b/managed/services/server/logs.go similarity index 97% rename from managed/services/supervisord/logs.go rename to managed/services/server/logs.go index 207a69947d..a32735e503 100644 --- a/managed/services/supervisord/logs.go +++ b/managed/services/server/logs.go @@ -13,7 +13,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -package supervisord +package server import ( "archive/zip" @@ -56,12 +56,12 @@ type fileContent struct { // Logs is responsible for interactions with logs. type Logs struct { pmmVersion string - pmmUpdateChecker *PMMUpdateChecker + pmmUpdateChecker *Updater vmParams victoriaMetricsParams } // NewLogs creates a new Logs service. -func NewLogs(pmmVersion string, pmmUpdateChecker *PMMUpdateChecker, vmParams victoriaMetricsParams) *Logs { +func NewLogs(pmmVersion string, pmmUpdateChecker *Updater, vmParams victoriaMetricsParams) *Logs { return &Logs{ pmmVersion: pmmVersion, pmmUpdateChecker: pmmUpdateChecker, @@ -204,7 +204,7 @@ func (l *Logs) files(ctx context.Context, pprofConfig *PprofConfig) []fileConten }) // update checker installed info - b, err = json.Marshal(l.pmmUpdateChecker.Installed(ctx)) + b, err = json.Marshal(l.pmmUpdateChecker.InstalledPMMVersion()) files = append(files, fileContent{ Name: "installed.json", Data: b, diff --git a/managed/services/supervisord/logs_test.go b/managed/services/server/logs_test.go similarity index 95% rename from managed/services/supervisord/logs_test.go rename to managed/services/server/logs_test.go index 8fb6f2a7aa..2085f632cb 100644 --- a/managed/services/supervisord/logs_test.go +++ b/managed/services/server/logs_test.go @@ -13,7 +13,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -package supervisord +package server import ( "archive/zip" @@ -27,7 +27,6 @@ import ( "testing" "time" - "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -143,10 +142,10 @@ func TestAddAdminSummary(t *testing.T) { } func TestFiles(t *testing.T) { - checker := NewPMMUpdateChecker(logrus.WithField("test", t.Name())) + updater := &Updater{} params, err := models.NewVictoriaMetricsParams(models.BasePrometheusConfigPath, models.VMBaseURL) require.NoError(t, err) - l := NewLogs("2.4.5", checker, params) + l := NewLogs("2.4.5", updater, params) ctx := logger.Set(context.Background(), t.Name()) files := l.files(ctx, nil) @@ -184,10 +183,10 @@ func TestFiles(t *testing.T) { func TestZip(t *testing.T) { t.Skip("FIXME") - checker := NewPMMUpdateChecker(logrus.WithField("test", t.Name())) + updater := &Updater{} params, err := models.NewVictoriaMetricsParams(models.BasePrometheusConfigPath, models.VMBaseURL) require.NoError(t, err) - l := NewLogs("2.4.5", checker, params) + l := NewLogs("2.4.5", updater, params) ctx := logger.Set(context.Background(), t.Name()) var buf bytes.Buffer diff --git a/managed/services/server/mock_supervisord_service_test.go b/managed/services/server/mock_supervisord_service_test.go index b958fc55b3..792e5987f1 100644 --- a/managed/services/server/mock_supervisord_service_test.go +++ b/managed/services/server/mock_supervisord_service_test.go @@ -3,13 +3,9 @@ package server import ( - context "context" - time "time" - mock "github.com/stretchr/testify/mock" models "github.com/percona/pmm/managed/models" - version "github.com/percona/pmm/version" ) // mockSupervisordService is an autogenerated mock type for the supervisordService type @@ -17,102 +13,6 @@ type mockSupervisordService struct { mock.Mock } -// ForceCheckUpdates provides a mock function with given fields: ctx -func (_m *mockSupervisordService) ForceCheckUpdates(ctx context.Context) error { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for ForceCheckUpdates") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context) error); ok { - r0 = rf(ctx) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// InstalledPMMVersion provides a mock function with given fields: ctx -func (_m *mockSupervisordService) InstalledPMMVersion(ctx context.Context) *version.PackageInfo { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for InstalledPMMVersion") - } - - var r0 *version.PackageInfo - if rf, ok := ret.Get(0).(func(context.Context) *version.PackageInfo); ok { - r0 = rf(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*version.PackageInfo) - } - } - - return r0 -} - -// LastCheckUpdatesResult provides a mock function with given fields: ctx -func (_m *mockSupervisordService) LastCheckUpdatesResult(ctx context.Context) (*version.UpdateCheckResult, time.Time) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for LastCheckUpdatesResult") - } - - var r0 *version.UpdateCheckResult - var r1 time.Time - if rf, ok := ret.Get(0).(func(context.Context) (*version.UpdateCheckResult, time.Time)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) *version.UpdateCheckResult); ok { - r0 = rf(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*version.UpdateCheckResult) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) time.Time); ok { - r1 = rf(ctx) - } else { - r1 = ret.Get(1).(time.Time) - } - - return r0, r1 -} - -// StartUpdate provides a mock function with given fields: -func (_m *mockSupervisordService) StartUpdate() (uint32, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for StartUpdate") - } - - var r0 uint32 - var r1 error - if rf, ok := ret.Get(0).(func() (uint32, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() uint32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint32) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // UpdateConfiguration provides a mock function with given fields: settings, ssoDetails func (_m *mockSupervisordService) UpdateConfiguration(settings *models.Settings, ssoDetails *models.PerconaSSODetails) error { ret := _m.Called(settings, ssoDetails) @@ -131,61 +31,6 @@ func (_m *mockSupervisordService) UpdateConfiguration(settings *models.Settings, return r0 } -// UpdateLog provides a mock function with given fields: offset -func (_m *mockSupervisordService) UpdateLog(offset uint32) ([]string, uint32, error) { - ret := _m.Called(offset) - - if len(ret) == 0 { - panic("no return value specified for UpdateLog") - } - - var r0 []string - var r1 uint32 - var r2 error - if rf, ok := ret.Get(0).(func(uint32) ([]string, uint32, error)); ok { - return rf(offset) - } - if rf, ok := ret.Get(0).(func(uint32) []string); ok { - r0 = rf(offset) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]string) - } - } - - if rf, ok := ret.Get(1).(func(uint32) uint32); ok { - r1 = rf(offset) - } else { - r1 = ret.Get(1).(uint32) - } - - if rf, ok := ret.Get(2).(func(uint32) error); ok { - r2 = rf(offset) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - -// UpdateRunning provides a mock function with given fields: -func (_m *mockSupervisordService) UpdateRunning() bool { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for UpdateRunning") - } - - var r0 bool - if rf, ok := ret.Get(0).(func() bool); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(bool) - } - - return r0 -} - // newMockSupervisordService creates a new instance of mockSupervisordService. 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 newMockSupervisordService(t interface { diff --git a/managed/services/supervisord/pprof_config.go b/managed/services/server/pprof_config.go similarity index 97% rename from managed/services/supervisord/pprof_config.go rename to managed/services/server/pprof_config.go index 52ab238389..9697cb8108 100644 --- a/managed/services/supervisord/pprof_config.go +++ b/managed/services/server/pprof_config.go @@ -13,7 +13,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -package supervisord +package server import "time" diff --git a/managed/services/server/server.go b/managed/services/server/server.go index 0a18699ecb..fecbe93660 100644 --- a/managed/services/server/server.go +++ b/managed/services/server/server.go @@ -189,15 +189,14 @@ func (s *Server) Version(ctx context.Context, req *serverpb.VersionRequest) (*se res.Managed.Timestamp = timestamppb.New(t) } - if v := s.supervisord.InstalledPMMVersion(ctx); v != nil { - res.Version = v.Version - res.Server = &serverpb.VersionInfo{ - Version: v.Version, - FullVersion: v.FullVersion, - } - if v.BuildTime != nil { - res.Server.Timestamp = timestamppb.New(*v.BuildTime) - } + v := s.updater.InstalledPMMVersion() + res.Version = v.Version + res.Server = &serverpb.VersionInfo{ + Version: v.Version, + FullVersion: v.FullVersion, + } + if v.BuildTime != nil { + res.Server.Timestamp = timestamppb.New(*v.BuildTime) } return res, nil @@ -242,7 +241,14 @@ func (s *Server) CheckUpdates(ctx context.Context, req *serverpb.CheckUpdatesReq s.envRW.RUnlock() if req.OnlyInstalledVersion { - return s.updater.onlyInstalledVersionResponse(), nil + installedPMMVersion := s.updater.InstalledPMMVersion() + return &serverpb.CheckUpdatesResponse{ + Installed: &serverpb.VersionInfo{ + Version: installedPMMVersion.Version, + FullVersion: installedPMMVersion.FullVersion, + Timestamp: timestamppb.New(*installedPMMVersion.BuildTime), + }, + }, nil } if req.Force { @@ -260,12 +266,13 @@ func (s *Server) CheckUpdates(ctx context.Context, req *serverpb.CheckUpdatesReq Installed: &serverpb.VersionInfo{ Version: v.Installed.Version, FullVersion: v.Installed.FullVersion, + Timestamp: timestamppb.New(*v.Installed.BuildTime), }, - Latest: &serverpb.VersionInfo{ - Version: v.Latest.Version, - FullVersion: v.Latest.FullVersion, + Latest: &serverpb.DockerVersionInfo{ + Version: v.Latest.Version.String(), + Tag: v.Latest.DockerImage, }, - UpdateAvailable: true, + UpdateAvailable: v.Latest.DockerImage != "", LatestNewsUrl: v.LatestNewsURL, } @@ -280,7 +287,7 @@ func (s *Server) CheckUpdates(ctx context.Context, req *serverpb.CheckUpdatesReq res.Installed.Timestamp = timestamppb.New(t) } - if v.Latest.BuildTime != nil { + if v.Latest.DockerImage != "" { t := v.Latest.BuildTime.UTC().Truncate(24 * time.Hour) // return only date res.Latest.Timestamp = timestamppb.New(t) } @@ -298,7 +305,17 @@ func (s *Server) StartUpdate(ctx context.Context, req *serverpb.StartUpdateReque return nil, status.Error(codes.FailedPrecondition, "Updates are disabled via PMM_DISABLE_UPDATES environment variable.") } - err := s.updater.StartUpdate(ctx, req.GetNewImage()) + newImage := req.GetNewImage() + if newImage == "" { + latest, err := s.updater.latest(ctx) + if err != nil { + s.l.WithError(err).Error("Failed to get latest version") + newImage = defaultLatestPMMImage + } else { + newImage = latest.DockerImage + } + } + err := s.updater.StartUpdate(ctx, newImage) if err != nil { return nil, err } @@ -331,13 +348,13 @@ func (s *Server) UpdateStatus(ctx context.Context, req *serverpb.UpdateStatusReq ctx, cancel := context.WithTimeout(ctx, 30*time.Second) defer cancel() for ctx.Err() == nil { - done = !s.supervisord.UpdateRunning() + done = !s.updater.IsRunning() if done { // give supervisord a second to flush logs to file time.Sleep(time.Second) } - lines, newOffset, err = s.supervisord.UpdateLog(req.LogOffset) + lines, newOffset, err = s.updater.UpdateLog(req.GetLogOffset()) if err != nil { s.l.Warn(err) } diff --git a/managed/services/server/updater.go b/managed/services/server/updater.go index 9fc66da088..0240536e19 100644 --- a/managed/services/server/updater.go +++ b/managed/services/server/updater.go @@ -16,48 +16,88 @@ package server import ( + "bufio" "context" "encoding/json" "fmt" + "io" + "net" "net/http" "net/url" "os" "strings" + "sync" "time" "github.com/pkg/errors" "github.com/sirupsen/logrus" + "google.golang.org/grpc/codes" + grpcstatus "google.golang.org/grpc/status" - "github.com/percona/pmm/api/serverpb" "github.com/percona/pmm/version" ) // defaultLatestPMMImage is the default image name to use when the latest version cannot be determined. -const defaultLatestPMMImage = "perconalab/pmm-server:3-dev-latest" +const ( + defaultLatestPMMImage = "perconalab/pmm-server:3-dev-latest" + pmmUpdatePerformLog = "/srv/logs/pmm-update-perform-init.log" + updateCheckInterval = 24 * time.Hour + updateCheckResultFresh = updateCheckInterval + 10*time.Minute + updateDefaultTimeout = 30 * time.Second +) + +var fileName = "/etc/pmm-server-update-version.json" // Updater is a service to check for updates and trigger the update process. type Updater struct { - l *logrus.Entry - supervisord supervisordService - watchtowerHost *url.URL + l *logrus.Entry + watchtowerHost *url.URL + gRPCMessageMaxSize uint32 + + performM sync.Mutex + running bool + + checkRW sync.RWMutex + lastCheckResult *version.DockerVersionInfo + lastCheckTime time.Time } // NewUpdater creates a new Updater service. -func NewUpdater(supervisord supervisordService, watchtowerHost *url.URL) *Updater { - return &Updater{ - l: logrus.WithField("service", "updater"), - supervisord: supervisord, - watchtowerHost: watchtowerHost, +func NewUpdater(watchtowerHost *url.URL, gRPCMessageMaxSize uint32) *Updater { + u := &Updater{ + l: logrus.WithField("service", "updater"), + watchtowerHost: watchtowerHost, + gRPCMessageMaxSize: gRPCMessageMaxSize, + } + return u +} + +// Run runs check for updates loop until ctx is canceled. +func (up *Updater) Run(ctx context.Context) { + up.l.Info("Starting...") + ticker := time.NewTicker(updateCheckInterval) + defer ticker.Stop() + + for { + _ = up.check(ctx) + + select { + case <-ticker.C: + // continue with next loop iteration + case <-ctx.Done(): + up.l.Info("Done.") + return + } } } -func (s *Updater) sendRequestToWatchtower(ctx context.Context, newImageName string) error { +func (up *Updater) sendRequestToWatchtower(ctx context.Context, newImageName string) error { hostname, err := os.Hostname() if err != nil { return errors.Wrap(err, "failed to get hostname") } - u, err := s.watchtowerHost.Parse("/v1/update") + u, err := up.watchtowerHost.Parse("/v1/update") if err != nil { return errors.Wrap(err, "failed to parse URL") } @@ -91,135 +131,293 @@ func (s *Updater) sendRequestToWatchtower(ctx context.Context, newImageName stri return errors.Errorf("received non-OK response: %v", resp.StatusCode) } - s.l.Info("Successfully triggered update") + up.l.Info("Successfully triggered update") return nil } -func (s *Updater) currentVersion() string { - return version.Version +func (up *Updater) currentVersion() *version.Parsed { + return version.MustParse(version.Version) } // StartUpdate triggers the update process. -func (s *Updater) StartUpdate(ctx context.Context, newImageName string) error { +func (up *Updater) StartUpdate(ctx context.Context, newImageName string) error { + up.performM.Lock() + defer up.performM.Unlock() + if up.running { + return errors.New("update already in progress") + } + up.running = true + up.performM.Unlock() if newImageName == "" { - latest, err := s.latest(ctx) - if err != nil { - s.l.WithError(err).Error("Failed to get latest version") - newImageName = defaultLatestPMMImage - } else { - newImageName = fmt.Sprintf("%s:%s", latest.Repo, latest.Version) - } + return errors.New("newImageName is empty") } - if err := s.sendRequestToWatchtower(ctx, newImageName); err != nil { - s.l.WithError(err).Error("Failed to trigger update") - return errors.Wrap(err, "failed to trigger update") + err := up.checkWatchtowerHost() + if err != nil { + up.l.WithError(err).Error("Failed to check watchtower host") + return grpcstatus.Errorf(codes.FailedPrecondition, "failed to check watchtower host") } - return nil -} -func (s *Updater) onlyInstalledVersionResponse() *serverpb.CheckUpdatesResponse { - return &serverpb.CheckUpdatesResponse{ - Installed: &serverpb.VersionInfo{ - Version: s.currentVersion(), - }, + if err := up.sendRequestToWatchtower(ctx, newImageName); err != nil { + up.l.WithError(err).Error("Failed to trigger update") + return errors.Wrap(err, "failed to trigger update") } + return nil } // ForceCheckUpdates forces an update check. -func (s *Updater) ForceCheckUpdates(_ context.Context) error { - // TODO: PMM-11261 Implement this method - return nil +func (up *Updater) ForceCheckUpdates(ctx context.Context) error { + return up.check(ctx) } -// TagsResponse is a response from DockerHub. -type TagsResponse struct { - Results []struct { - Name string `json:"name"` - } `json:"results"` +// LastCheckUpdatesResult returns the result of the last update check. +func (up *Updater) LastCheckUpdatesResult(ctx context.Context) (*version.UpdateCheckResult, time.Time) { + installed := up.InstalledPMMVersion() + latest, lastCheckTime := up.checkResult(ctx) + return &version.UpdateCheckResult{ + Installed: installed, + Latest: *latest, + UpdateAvailable: latest.DockerImage != "", + LatestNewsURL: "https://per.co.na/pmm/" + latest.Version.String(), + }, lastCheckTime } -// LastCheckUpdatesResult returns the result of the last update check. -func (s *Updater) LastCheckUpdatesResult(ctx context.Context) (*version.UpdateCheckResult, time.Time) { - buildTime, err := version.Time() +func (up *Updater) latest(ctx context.Context) (*version.DockerVersionInfo, error) { + info, err := up.readFromFile() if err != nil { - s.l.WithError(err).Error("Failed to get build time") - return nil, time.Now() + return nil, errors.Wrap(err, "failed to read from file") } - latest, err := s.latest(ctx) - if err != nil { - s.l.WithError(err).Error("Failed to get latest version") - return nil, time.Now() + if info != nil { + return info, nil } - return &version.UpdateCheckResult{ - Installed: version.PackageInfo{ - Version: s.currentVersion(), - FullVersion: version.PMMVersion, - BuildTime: &buildTime, - Repo: "local", - }, - Latest: *latest, - UpdateAvailable: true, - LatestNewsURL: "", - }, time.Now() + if os.Getenv("PMM_DEV_UPDATE_DOCKER_IMAGE") != "" { + return up.parseDockerTag(os.Getenv("PMM_DEV_UPDATE_DOCKER_IMAGE")) + } + + // If file does not exist, and ENV variable is not set, go get the latest tag from DockerHub + return up.latestAvailableFromDockerHub(ctx) } -func (s *Updater) latest(ctx context.Context) (*version.PackageInfo, error) { +func (up *Updater) readFromFile() (*version.DockerVersionInfo, error) { // Read from file, if it's not exist read from ENV variable, if it's not exist get the latest tag from DockerHub. - fileName := "/etc/pmm-server-update-version.json" content, err := os.ReadFile(fileName) if err != nil && !os.IsNotExist(err) { - s.l.WithError(err).Error("Failed to read file") + up.l.WithError(err).Error("Failed to read file") return nil, errors.Wrap(err, "failed to read file") } - if err == nil { - info := version.PackageInfo{} - err = json.Unmarshal(content, &info) - if err != nil { - s.l.WithError(err).Error("Failed to unmarshal file") - return nil, errors.Wrap(err, "failed to unmarshal file") - } - return &info, nil + if os.IsNotExist(err) { + return nil, nil //nolint:nilnil } - if os.Getenv("PMM_SERVER_UPDATE_VERSION") != "" { - return s.parseDockerTag(os.Getenv("PMM_SERVER_UPDATE_VERSION")), nil + info := version.DockerVersionInfo{} + err = json.Unmarshal(content, &info) + if err != nil { + up.l.WithError(err).Error("Failed to unmarshal file") + return nil, errors.Wrap(err, "failed to unmarshal file") } + return &info, nil +} - // If file does not exist, and ENV variable is not set, go get the latest tag from DockerHub - u := "https://registry.hub.docker.com/v2/repositories/percona/pmm-server/tags/" +type result struct { + Name string `json:"name"` + TagLastPushed time.Time `json:"tag_last_pushed"` +} + +// TagsResponse is a response from DockerHub. +type TagsResponse struct { + Results []result `json:"results"` +} + +// latestAvailableFromDockerHub returns the latest available version from DockerHub. +// It returns the latest minor version for the current major version. +// If the current version is the latest minor version, it returns the next major version. +// If the current version is the latest version, it returns the current version. +func (up *Updater) latestAvailableFromDockerHub(ctx context.Context) (*version.DockerVersionInfo, error) { + repo := os.Getenv("PMM_DEV_UPDATE_DOCKER_REPO") + if repo == "" { + repo = "percona/pmm-server" + } + u := "https://registry.hub.docker.com/v2/repositories/" + repo + "/tags/?page_size=100" req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil) if err != nil { - s.l.WithError(err).Error("Failed to create request") + up.l.WithError(err).Error("Failed to create request") return nil, errors.Wrap(err, "failed to create request") } resp, err := http.DefaultClient.Do(req) if err != nil { - s.l.WithError(err).Error("Failed to get tags from DockerHub") + up.l.WithError(err).Error("Failed to get tags from DockerHub") return nil, errors.Wrap(err, "failed to get tags from DockerHub") } defer resp.Body.Close() //nolint:errcheck var tagsResponse TagsResponse if err := json.NewDecoder(resp.Body).Decode(&tagsResponse); err != nil { - s.l.WithError(err).Error("Failed to decode response") + up.l.WithError(err).Error("Failed to decode response") return nil, errors.Wrap(err, "failed to decode response") } if len(tagsResponse.Results) != 0 { - // Assuming the first tag is the latest - return s.parseDockerTag(tagsResponse.Results[0].Name), nil + up.l.Infof("Found %d tags", len(tagsResponse.Results)) + next := up.next(*up.currentVersion(), tagsResponse.Results) + if next.DockerImage != "" { + next.DockerImage = repo + ":" + next.DockerImage + } + return next, err } return nil, errors.New("no tags found") } -func (s *Updater) parseDockerTag(tag string) *version.PackageInfo { +func (up *Updater) parseDockerTag(tag string) (*version.DockerVersionInfo, error) { splitTag := strings.Split(tag, ":") if len(splitTag) != 2 { - return nil + return nil, fmt.Errorf("invalid tag: %s", tag) + } + parsed, err := version.Parse(splitTag[1]) + if err != nil { + up.l.Debugf("Failed to parse version: %s", splitTag[1]) + return &version.DockerVersionInfo{DockerImage: tag}, nil //nolint:nilerr + } + return &version.DockerVersionInfo{ + Version: *parsed, + DockerImage: tag, + }, nil +} + +func (up *Updater) next(currentVersion version.Parsed, results []result) *version.DockerVersionInfo { + nextMinor := &version.DockerVersionInfo{ + Version: currentVersion, + } + var nextMajor *version.DockerVersionInfo + for _, result := range results { + v, err := version.Parse(result.Name) + if err != nil { + up.l.Debugf("Failed to parse version: %s", result.Name) + continue + } + if !currentVersion.Less(v) { + continue + } + if v.Major == currentVersion.Major && nextMinor.Version.Less(v) { // next major + nextMinor = &version.DockerVersionInfo{ + Version: *v, + DockerImage: result.Name, + BuildTime: result.TagLastPushed, + } + } + if v.Major > currentVersion.Major && + (nextMajor == nil || (nextMajor.Version.Less(v) && nextMajor.Version.Major == v.Major) || v.Major < nextMajor.Version.Major) { + nextMajor = &version.DockerVersionInfo{ + Version: *v, + DockerImage: result.Name, + BuildTime: result.TagLastPushed, + } + } + } + if nextMinor.Version == currentVersion && nextMajor != nil { + return nextMajor + } + return nextMinor +} + +// InstalledPMMVersion returns the currently installed PMM version. +func (up *Updater) InstalledPMMVersion() version.PackageInfo { + t, _ := version.Time() + return version.PackageInfo{ + Version: version.Version, + FullVersion: version.PMMVersion, + BuildTime: &t, + } +} + +// IsRunning returns true if the update process is running. +func (up *Updater) IsRunning() bool { + up.performM.Lock() + defer up.performM.Unlock() + return up.running +} + +// UpdateLog returns the log of the update process. +func (up *Updater) UpdateLog(offset uint32) ([]string, uint32, error) { + up.performM.Lock() + defer up.performM.Unlock() + + f, err := os.Open(pmmUpdatePerformLog) + if err != nil { + return nil, 0, errors.WithStack(err) + } + defer f.Close() //nolint:errcheck,gosec,nolintlint + + if _, err = f.Seek(int64(offset), io.SeekStart); err != nil { + return nil, 0, errors.WithStack(err) + } + + lines := make([]string, 0, 10) + reader := bufio.NewReader(f) + newOffset := offset + for { + line, err := reader.ReadString('\n') + if err == nil { + newOffset += uint32(len(line)) + if newOffset-offset > up.gRPCMessageMaxSize { + return lines, newOffset - uint32(len(line)), nil + } + lines = append(lines, strings.TrimSuffix(line, "\n")) + continue + } + if err == io.EOF { + err = nil + } + return lines, newOffset, errors.WithStack(err) + } +} + +// checkResult returns the result of the last update check. +// It may force re-check if last result is empty or too old. +func (up *Updater) checkResult(ctx context.Context) (*version.DockerVersionInfo, time.Time) { + up.checkRW.RLock() + defer up.checkRW.RUnlock() + + if time.Since(up.lastCheckTime) > updateCheckResultFresh { + up.checkRW.RUnlock() + _ = up.check(ctx) + up.checkRW.RLock() + } + + return up.lastCheckResult, up.lastCheckTime +} + +// check performs update check. +func (up *Updater) check(ctx context.Context) error { + up.checkRW.Lock() + defer up.checkRW.Unlock() + + latest, err := up.latest(ctx) + if err != nil { + return errors.Wrap(err, "failed to get latest version") + } + up.lastCheckResult = latest + up.lastCheckTime = time.Now() + return nil +} + +func (up *Updater) checkWatchtowerHost() error { + // Check if watchtower host is available + if up.watchtowerHost == nil { + return errors.New("watchtower host is not set") + } + if !isHostAvailable(up.watchtowerHost.Hostname(), up.watchtowerHost.Port(), updateDefaultTimeout) { + return errors.New("watchtower host is not available") + } + return nil +} + +func isHostAvailable(host string, port string, timeout time.Duration) bool { + conn, err := net.DialTimeout("tcp", net.JoinHostPort(host, port), timeout) + if err != nil { + return false } - return &version.PackageInfo{ - Version: splitTag[1], - FullVersion: tag, - Repo: splitTag[0], + if conn != nil { + defer conn.Close() //nolint:errcheck + return true } + return false } diff --git a/managed/services/server/updater_test.go b/managed/services/server/updater_test.go new file mode 100644 index 0000000000..68bb6e69f4 --- /dev/null +++ b/managed/services/server/updater_test.go @@ -0,0 +1,274 @@ +// 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 server + +import ( + "context" + "net/url" + "os" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/AlekSi/pointer" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/percona/pmm/version" +) + +func TestUpdater(t *testing.T) { + gRPCMessageMaxSize := uint32(100 * 1024 * 1024) + watchtowerURL, _ := url.Parse("http://watchtower:8080") + + t.Run("TestNextVersion", func(t *testing.T) { + type args struct { + currentVersion string + results []result + } + type versionInfo struct { + Version string + DockerImage string + BuildTime *time.Time + } + tests := []struct { + name string + args args + want *versionInfo + }{ + { + name: "no results", + args: args{ + currentVersion: "3.0.0", + results: nil, + }, + want: &versionInfo{ + Version: "3.0.0", + DockerImage: "", + }, + }, + { + name: "no new version", + args: args{ + currentVersion: "3.0.0", + results: []result{ + {Name: "3.0.0"}, + }, + }, + want: &versionInfo{ + Version: "3.0.0", + DockerImage: "", + }, + }, + { + name: "new minor versions", + args: args{ + currentVersion: "3.0.0", + results: []result{ + {Name: "3.2.0"}, + {Name: "3.1.0"}, + {Name: "3.0.0"}, + }, + }, + want: &versionInfo{ + Version: "3.2.0", + DockerImage: "3.2.0", + }, + }, + { + name: "new patch version", + args: args{ + currentVersion: "3.0.0", + results: []result{ + {Name: "3.0.0"}, + {Name: "3.0.1", TagLastPushed: time.Date(2024, 3, 20, 15, 48, 7, 145620000, time.UTC)}, + }, + }, + want: &versionInfo{ + Version: "3.0.1", + DockerImage: "3.0.1", + BuildTime: pointer.To(time.Date(2024, 3, 20, 15, 48, 7, 145620000, time.UTC)), + }, + }, + { + name: "new major version", + args: args{ + currentVersion: "3.0.0", + results: []result{ + {Name: "4.0.0"}, + {Name: "3.0.0"}, + }, + }, + want: &versionInfo{ + Version: "4.0.0", + DockerImage: "4.0.0", + }, + }, + { + name: "new major version with rc version", + args: args{ + currentVersion: "3.0.0", + results: []result{ + {Name: "4.0.0"}, + {Name: "3.0.0"}, + {Name: "4.0.0-rc"}, + }, + }, + want: &versionInfo{ + Version: "4.0.0", + DockerImage: "4.0.0", + }, + }, + { + name: "multiple new major versions", + args: args{ + currentVersion: "3.3.0", + results: []result{ + {Name: "4.1.0"}, + {Name: "4.0.0"}, + {Name: "3.0.0"}, + {Name: "5.1.0"}, + }, + }, + want: &versionInfo{ + Version: "4.1.0", + DockerImage: "4.1.0", + }, + }, + { + name: "new major version with minor version", + args: args{ + currentVersion: "3.0.0", + results: []result{ + {Name: "4.1.0"}, + {Name: "4.0.0"}, + {Name: "3.0.0"}, + {Name: "3.1.0"}, + }, + }, + want: &versionInfo{ + Version: "3.1.0", + DockerImage: "3.1.0", + }, + }, + { + name: "invalid version", + args: args{ + currentVersion: "3.0.0", + results: []result{ + {Name: "3.0.0"}, + {Name: "3.1.0"}, + {Name: "invalid"}, + }, + }, + want: &versionInfo{ + Version: "3.1.0", + DockerImage: "3.1.0", + }, + }, + { + name: "non semver version", + args: args{ + currentVersion: "3.0.0", + results: []result{ + {Name: "3.0.0"}, + {Name: "3.1"}, + }, + }, + want: &versionInfo{ + Version: "3.0.0", + DockerImage: "", + }, + }, + { + name: "rc version", + args: args{ + currentVersion: "3.0.0", + results: []result{ + {Name: "3.0.0"}, + {Name: "3.1.0-rc"}, + {Name: "3.1.0-rc757"}, + }, + }, + want: &versionInfo{ + Version: "3.1.0-rc757", + DockerImage: "3.1.0-rc757", + }, + }, + { + name: "rc version and release version", + args: args{ + currentVersion: "3.0.0", + results: []result{ + {Name: "3.0.0"}, + {Name: "3.1.0"}, + {Name: "3.1.0-rc"}, + {Name: "3.1.0-rc757"}, + }, + }, + want: &versionInfo{ + Version: "3.1.0", + DockerImage: "3.1.0", + }, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + u := NewUpdater(watchtowerURL, gRPCMessageMaxSize) + parsed, err := version.Parse(tt.args.currentVersion) + require.NoError(t, err) + next := u.next(*parsed, tt.args.results) + require.NoError(t, err) + assert.Equal(t, tt.want.Version, next.Version.String()) + assert.Equal(t, tt.want.DockerImage, next.DockerImage) + if tt.want.BuildTime != nil { + assert.NotNil(t, next.BuildTime) + assert.Equal(t, *tt.want.BuildTime, next.BuildTime) + } + }) + } + }) + + t.Run("TestLatest", func(t *testing.T) { + // Used PMM 2, because PMM 3 is not released yet. + version.Version = "2.41.0" + u := NewUpdater(watchtowerURL, gRPCMessageMaxSize) + latest, err := u.latest(context.Background()) + require.NoError(t, err) + assert.NotNil(t, latest) + assert.True(t, strings.HasPrefix(latest.Version.String(), "2.41."), "latest version of PMM 2 should have prefix 2.41.") + }) + + t.Run("TestParseFile", func(t *testing.T) { + fileBody := `{ "version": "2.41.1" , "docker_image": "2.41.1" , "build_time": "2024-03-20T15:48:07.14562Z" }` + oldFileName := fileName + fileName = filepath.Join(os.TempDir(), "pmm-update.json") + defer func() { fileName = oldFileName }() + + err := os.WriteFile(fileName, []byte(fileBody), 0o600) + require.NoError(t, err) + + u := NewUpdater(watchtowerURL, gRPCMessageMaxSize) + latest, err := u.latest(context.Background()) + require.NoError(t, err) + assert.Equal(t, "2.41.1", latest.Version.String()) + assert.Equal(t, "2.41.1", latest.DockerImage) + assert.Equal(t, time.Date(2024, 3, 20, 15, 48, 7, 145620000, time.UTC), latest.BuildTime) + }) +} diff --git a/managed/services/supervisord/deps.go b/managed/services/supervisord/deps.go index 9de477c334..9176f8b1bb 100644 --- a/managed/services/supervisord/deps.go +++ b/managed/services/supervisord/deps.go @@ -14,12 +14,3 @@ // along with this program. If not, see . package supervisord - -import "net/url" - -// victoriaMetricsParams is a subset of methods of models.VMParams used by this package. -// We use it instead of real type to avoid dependency cycle. -type victoriaMetricsParams interface { - ExternalVM() bool - URLFor(path string) (*url.URL, error) -} diff --git a/managed/services/supervisord/devcontainer_test.go b/managed/services/supervisord/devcontainer_test.go index e7b688c011..d2679a729f 100644 --- a/managed/services/supervisord/devcontainer_test.go +++ b/managed/services/supervisord/devcontainer_test.go @@ -19,105 +19,23 @@ import ( "context" "os" "path/filepath" - "strconv" - "strings" "testing" "time" - "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" "github.com/percona/pmm/managed/models" - "github.com/percona/pmm/version" ) // TODO move tests to other files and remove this one. func TestDevContainer(t *testing.T) { - gRPCMessageMaxSize := uint32(100 * 1024 * 1024) - gaReleaseDate := time.Date(2019, 9, 18, 0, 0, 0, 0, time.UTC) - - t.Run("Installed", func(t *testing.T) { - ctx := context.TODO() - checker := NewPMMUpdateChecker(logrus.WithField("test", t.Name())) - - info := checker.Installed(ctx) - require.NotNil(t, info) - - assert.True(t, strings.HasPrefix(info.Version, "3."), "version should start with `3.`. Actual value is: %s", info.Version) - fullVersion, _ := normalizeFullversion(info) - assert.True(t, strings.HasPrefix(fullVersion, "3."), "full version should start with `3.`. Actual value is: %s", fullVersion) - require.NotEmpty(t, info.BuildTime) - assert.True(t, info.BuildTime.After(gaReleaseDate), "BuildTime = %s", info.BuildTime) - assert.Equal(t, "local", info.Repo) - - info2 := checker.Installed(ctx) - assert.Equal(t, info, info2) - }) - - t.Run("Check", func(t *testing.T) { - t.Skip("This test is to be deprecated or completely rewritten") - - ctx := context.TODO() - checker := NewPMMUpdateChecker(logrus.WithField("test", t.Name())) - - res, resT := checker.checkResult(ctx) - assert.WithinDuration(t, time.Now(), resT, time.Second) - - assert.True(t, strings.HasPrefix(res.Installed.Version, "3."), "installed version should start with `3.`. Actual value is: %s", res.Installed.Version) - installedFullVersion, _ := normalizeFullversion(&res.Installed) - assert.True(t, strings.HasPrefix(installedFullVersion, "3."), "installed full version should start with `3.`. Actual value is: %s", installedFullVersion) - require.NotEmpty(t, res.Installed.BuildTime) - assert.True(t, res.Installed.BuildTime.After(gaReleaseDate), "Installed.BuildTime = %s", res.Installed.BuildTime) - assert.Equal(t, "local", res.Installed.Repo) - - assert.True(t, strings.HasPrefix(res.Latest.Version, "3."), "The latest available version should start with `3.`. Actual value is: %s", res.Latest.Version) - latestFullVersion, isFeatureBranch := normalizeFullversion(&res.Latest) - if isFeatureBranch { - t.Skip("Skipping check latest version.") - } - assert.True(t, strings.HasPrefix(latestFullVersion, "3."), "The latest available versions full value should start with `3.`. Actual value is: %s", latestFullVersion) - require.NotEmpty(t, res.Latest.BuildTime) - assert.True(t, res.Latest.BuildTime.After(gaReleaseDate), "Latest.BuildTime = %s", res.Latest.BuildTime) - assert.NotEmpty(t, res.Latest.Repo) - - // We assume that the latest perconalab/pmm-server:3-dev-latest image - // always contains the latest pmm-update package versions. - // If this test fails, re-pull them and recreate devcontainer. - t.Log("Assuming the latest pmm-update version.") - assert.False(t, res.UpdateAvailable, "update should not be available") - assert.Empty(t, res.LatestNewsURL, "latest_news_url should be empty") - assert.Equal(t, res.Installed, res.Latest, "version should be the same (latest)") - assert.Equal(t, *res.Installed.BuildTime, *res.Latest.BuildTime, "build times should be the same") - assert.Equal(t, "local", res.Latest.Repo) - - // cached result - res2, resT2 := checker.checkResult(ctx) - assert.Equal(t, res, res2) - assert.Equal(t, resT, resT2) - - time.Sleep(100 * time.Millisecond) - ctx, cancel := context.WithTimeout(context.Background(), updateDefaultTimeout) - defer cancel() - go checker.run(ctx) - time.Sleep(100 * time.Millisecond) - - // should block and wait for run to finish one iteration - res3, resT3 := checker.checkResult(ctx) - assert.Equal(t, res2, res3) - assert.NotEqual(t, resT2, resT3, "%s", resT2) - assert.WithinDuration(t, resT2, resT3, 10*time.Second) - }) - t.Run("UpdateConfiguration", func(t *testing.T) { // logrus.SetLevel(logrus.DebugLevel) - checker := NewPMMUpdateChecker(logrus.WithField("test", t.Name())) vmParams, err := models.NewVictoriaMetricsParams(models.BasePrometheusConfigPath, models.VMBaseURL) require.NoError(t, err) - s := New("/etc/supervisord.d", checker, &models.Params{VMParams: vmParams, PGParams: &models.PGParams{}, HAParams: &models.HAParams{}}, gRPCMessageMaxSize) + s := New("/etc/supervisord.d", &models.Params{VMParams: vmParams, PGParams: &models.PGParams{}, HAParams: &models.HAParams{}}) require.NotEmpty(t, s.supervisorctlPath) ctx, cancel := context.WithCancel(context.Background()) @@ -159,88 +77,4 @@ func TestDevContainer(t *testing.T) { err = s.UpdateConfiguration(settings, nil) require.NoError(t, err) }) - - t.Run("Update", func(t *testing.T) { - // This test can be run only once as it breaks assumptions of other tests. - // It also should be the last test in devcontainer. - if ok, _ := strconv.ParseBool(os.Getenv("PMM_TEST_RUN_UPDATE")); !ok { - t.Skip("skipping update test") - } - - // logrus.SetLevel(logrus.DebugLevel) - checker := NewPMMUpdateChecker(logrus.WithField("test", t.Name())) - vmParams := &models.VictoriaMetricsParams{} - 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()) - defer cancel() - go s.Run(ctx) - - require.Equal(t, false, s.UpdateRunning()) - - offset, err := s.StartUpdate() - require.NoError(t, err) - assert.Zero(t, offset) - - assert.True(t, s.UpdateRunning()) - - _, err = s.StartUpdate() - assert.Equal(t, status.Errorf(codes.FailedPrecondition, "Update is already running."), err) - - // get logs as often as possible to increase a chance for race detector to spot something - var lastLine string - for { - done := s.UpdateRunning() - if done { - // give supervisord a second to flush logs to file - time.Sleep(time.Second) - } - - lines, newOffset, err := s.UpdateLog(offset) - require.NoError(t, err) - if newOffset == offset { - assert.Empty(t, lines, "lines:\n%s", strings.Join(lines, "\n")) - if done { - continue - } - break - } - - assert.NotEmpty(t, lines) - t.Logf("%s", strings.Join(lines, "\n")) - lastLine = lines[len(lines)-1] - - assert.NotZero(t, newOffset) - assert.True(t, newOffset > offset, "expected newOffset = %d > offset = %d", newOffset, offset) - offset = newOffset - } - - t.Logf("lastLine = %q", lastLine) - assert.Contains(t, lastLine, "Waiting for Grafana dashboards update to finish...") - - // extra checks that we did not miss `pmp-update -perform` self-update and restart by supervisord - const wait = 3 * time.Second - const delay = 200 * time.Millisecond - for i := 0; i < int(wait/delay); i++ { - time.Sleep(delay) - require.False(t, s.UpdateRunning()) - lines, newOffset, err := s.UpdateLog(offset) - require.NoError(t, err) - require.Empty(t, lines, "lines:\n%s", strings.Join(lines, "\n")) - require.Equal(t, offset, newOffset, "offset = %d, newOffset = %d", offset, newOffset) - } - }) -} - -func normalizeFullversion(info *version.PackageInfo) (version string, isFeatureBranch bool) { - fullVersion := info.FullVersion - - epochPrefix := "1:" // set by RPM_EPOCH in PMM Server build scripts - isFeatureBranch = strings.HasPrefix(fullVersion, epochPrefix) - if isFeatureBranch { - fullVersion = strings.TrimPrefix(fullVersion, epochPrefix) - } - - return fullVersion, isFeatureBranch } diff --git a/managed/services/supervisord/maintail_test.go b/managed/services/supervisord/maintail_test.go index 2e108a66f6..61d9373e9a 100644 --- a/managed/services/supervisord/maintail_test.go +++ b/managed/services/supervisord/maintail_test.go @@ -29,11 +29,6 @@ func TestParseEvent(t *testing.T) { t.Parallel() log := strings.Split(` - 2019-08-08 17:09:41,806 INFO spawned: 'pmm-update-perform' with pid 12983 - 2019-08-08 17:09:43,509 INFO success: pmm-update-perform entered RUNNING state, process has stayed up for > than 1 seconds (startsecs) - 2019-08-08 17:09:48,494 INFO exited: pmm-update-perform (exit status 1; not expected) - 2019-08-08 17:09:48,506 INFO spawned: 'pmm-update-perform' with pid 13000 - 2019-08-08 17:09:49,506 INFO success: pmm-update-perform entered RUNNING state, process has stayed up for > than 1 seconds (startsecs) 2019-08-08 17:09:57,284 INFO received SIGUSR2 indicating log reopen request 2019-08-08 17:09:57,284 INFO supervisord logreopen 2019-08-08 17:09:57,854 INFO waiting for pmm-managed to stop @@ -45,7 +40,6 @@ func TestParseEvent(t *testing.T) { 2019-08-08 17:10:27,686 INFO spawned: 'dashboard-upgrade' with pid 13888 2019-08-08 17:10:27,686 INFO success: dashboard-upgrade entered RUNNING state, process has stayed up for > than 0 seconds (startsecs) 2019-08-08 17:10:27,761 INFO exited: dashboard-upgrade (exit status 0; expected) - 2019-08-08 17:10:28,975 INFO exited: pmm-update-perform (exit status 0; expected) `, "\n") var actual []*event @@ -59,11 +53,6 @@ func TestParseEvent(t *testing.T) { } } expected := []*event{ - {Time: time.Date(2019, 8, 8, 17, 9, 41, 806000000, time.UTC), Type: starting, Program: "pmm-update-perform"}, - {Time: time.Date(2019, 8, 8, 17, 9, 43, 509000000, time.UTC), Type: running, Program: "pmm-update-perform"}, - {Time: time.Date(2019, 8, 8, 17, 9, 48, 494000000, time.UTC), Type: exitedUnexpected, Program: "pmm-update-perform"}, - {Time: time.Date(2019, 8, 8, 17, 9, 48, 506000000, time.UTC), Type: starting, Program: "pmm-update-perform"}, - {Time: time.Date(2019, 8, 8, 17, 9, 49, 506000000, time.UTC), Type: running, Program: "pmm-update-perform"}, {Time: time.Date(2019, 8, 8, 17, 9, 57, 284000000, time.UTC), Type: logReopen, Program: "supervisord"}, {Time: time.Date(2019, 8, 8, 17, 9, 57, 854000000, time.UTC), Type: stopping, Program: "pmm-managed"}, {Time: time.Date(2019, 8, 8, 17, 9, 59, 854000000, time.UTC), Type: stopping, Program: "pmm-managed"}, @@ -73,7 +62,6 @@ func TestParseEvent(t *testing.T) { {Time: time.Date(2019, 8, 8, 17, 10, 27, 686000000, time.UTC), Type: starting, Program: "dashboard-upgrade"}, {Time: time.Date(2019, 8, 8, 17, 10, 27, 686000000, time.UTC), Type: running, Program: "dashboard-upgrade"}, {Time: time.Date(2019, 8, 8, 17, 10, 27, 761000000, time.UTC), Type: exitedExpected, Program: "dashboard-upgrade"}, - {Time: time.Date(2019, 8, 8, 17, 10, 28, 975000000, time.UTC), Type: exitedExpected, Program: "pmm-update-perform"}, } assert.Equal(t, expected, actual) }) diff --git a/managed/services/supervisord/pmm_config.go b/managed/services/supervisord/pmm_config.go index 4ef71c0fdd..432e22ea32 100644 --- a/managed/services/supervisord/pmm_config.go +++ b/managed/services/supervisord/pmm_config.go @@ -196,20 +196,4 @@ stdout_logfile = /srv/logs/pmm-agent.log stdout_logfile_maxbytes = 50MB stdout_logfile_backups = 2 redirect_stderr = true - -[program:pmm-update-perform] -command = /usr/sbin/pmm-update -perform -playbook=/opt/ansible/pmm-docker/update.yml -user = pmm -directory = / -autorestart = unexpected -exitcodes = 0 -autostart = false -startretries = 10 -startsecs = 1 -stopsignal = TERM -stopwaitsecs = 300 -stdout_logfile = /srv/logs/pmm-update-perform.log -stdout_logfile_maxbytes = 50MB -stdout_logfile_backups = 3 -redirect_stderr = true `)) diff --git a/managed/services/supervisord/pmm_update_checker.go b/managed/services/supervisord/pmm_update_checker.go deleted file mode 100644 index fe062da7fe..0000000000 --- a/managed/services/supervisord/pmm_update_checker.go +++ /dev/null @@ -1,170 +0,0 @@ -// 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 ( - "bytes" - "context" - "encoding/json" - "os/exec" - "strings" - "sync" - "time" - - "github.com/pkg/errors" - "github.com/sirupsen/logrus" - "golang.org/x/sys/unix" - - "github.com/percona/pmm/utils/pdeathsig" - "github.com/percona/pmm/version" -) - -const ( - updateCheckInterval = 24 * time.Hour - updateCheckResultFresh = updateCheckInterval + 10*time.Minute - updateDefaultTimeout = 30 * time.Second -) - -// PMMUpdateChecker wraps `pmm-update -installed` and `pmm-update -check` with caching. -// -// We almost could use `supervisorctl start pmm-update-check` and then get output from stdout log file, -// but that is too painful, and, unlike with `pmm-update -perform`, we don't have to do it. -type PMMUpdateChecker struct { - l *logrus.Entry - - checkRW sync.RWMutex - installedRW sync.RWMutex - cmdMutex sync.Mutex - lastInstalledPackageInfo *version.PackageInfo - lastCheckResult *version.UpdateCheckResult - lastCheckTime time.Time -} - -// NewPMMUpdateChecker returns a PMMUpdateChecker instance that can be shared across different parts of the code. -// Since this is used inside this package, it could be a singleton, but it would make things mode difficult to test. -func NewPMMUpdateChecker(l *logrus.Entry) *PMMUpdateChecker { - return &PMMUpdateChecker{ - l: l, - } -} - -// run runs check for updates loop until ctx is canceled. -func (p *PMMUpdateChecker) run(ctx context.Context) { - p.l.Info("Starting...") - ticker := time.NewTicker(updateCheckInterval) - defer ticker.Stop() - - for { - _ = p.check(ctx) - - select { - case <-ticker.C: - // continue with next loop iteration - case <-ctx.Done(): - p.l.Info("Done.") - return - } - } -} - -// Installed returns currently installed version information. -// It is always cached since pmm-update RPM package is always updated before pmm-managed update/restart. -func (p *PMMUpdateChecker) Installed(ctx context.Context) *version.PackageInfo { - p.installedRW.RLock() - if p.lastInstalledPackageInfo != nil { - res := p.lastInstalledPackageInfo - p.installedRW.RUnlock() - return res - } - p.installedRW.RUnlock() - - // use -installed since it is much faster - cmdLine := "pmm-update -installed" - b, stderr, err := p.cmdRun(ctx, cmdLine) - if err != nil { - p.l.Errorf("%s output: %s. Error: %s", cmdLine, stderr.Bytes(), err) - return nil - } - - var res version.UpdateInstalledResult - if err = json.Unmarshal(b, &res); err != nil { - p.l.Errorf("%s output: %s", cmdLine, stderr.Bytes()) - return nil - } - - p.installedRW.Lock() - p.lastInstalledPackageInfo = &res.Installed - p.installedRW.Unlock() - - return &res.Installed -} - -func (p *PMMUpdateChecker) cmdRun(ctx context.Context, cmdLine string) ([]byte, bytes.Buffer, error) { - args := strings.Split(cmdLine, " ") - p.cmdMutex.Lock() - timeoutCtx, cancel := context.WithTimeout(ctx, updateDefaultTimeout) - defer cancel() - cmd := exec.CommandContext(timeoutCtx, args[0], args[1:]...) //nolint:gosec - var stderr bytes.Buffer - cmd.Stderr = &stderr - pdeathsig.Set(cmd, unix.SIGKILL) - - b, err := cmd.Output() - p.cmdMutex.Unlock() - return b, stderr, err -} - -// checkResult returns last `pmm-update -check` result and check time. -// It may force re-check if last result is empty or too old. -func (p *PMMUpdateChecker) checkResult(ctx context.Context) (*version.UpdateCheckResult, time.Time) { - p.checkRW.RLock() - defer p.checkRW.RUnlock() - - if time.Since(p.lastCheckTime) > updateCheckResultFresh { - p.checkRW.RUnlock() - _ = p.check(ctx) - p.checkRW.RLock() - } - - return p.lastCheckResult, p.lastCheckTime -} - -// check calls `pmm-update -check` and fills lastInstalledPackageInfo/lastCheckResult/lastCheckTime on success. -func (p *PMMUpdateChecker) check(ctx context.Context) error { - p.checkRW.Lock() - defer p.checkRW.Unlock() - - cmdLine := "pmm-update -check" - b, stderr, err := p.cmdRun(ctx, cmdLine) - if err != nil { - p.l.Errorf("%s output: %s. Error: %s", cmdLine, stderr.Bytes(), err) - return errors.WithStack(err) - } - - var res version.UpdateCheckResult - if err = json.Unmarshal(b, &res); err != nil { - p.l.Errorf("%s output: %s", cmdLine, stderr.Bytes()) - return errors.WithStack(err) - } - - p.l.Debugf("%s output: %s", cmdLine, stderr.Bytes()) - p.installedRW.Lock() - p.lastInstalledPackageInfo = &res.Installed - p.installedRW.Unlock() - p.lastCheckResult = &res - p.lastCheckTime = time.Now() - return nil -} diff --git a/managed/services/supervisord/supervisord.go b/managed/services/supervisord/supervisord.go index 49a8963d9c..bbf6a85498 100644 --- a/managed/services/supervisord/supervisord.go +++ b/managed/services/supervisord/supervisord.go @@ -21,7 +21,6 @@ import ( "bytes" "context" "fmt" - "io" "io/fs" "net/url" "os" @@ -38,13 +37,10 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" "github.com/percona/pmm/managed/models" "github.com/percona/pmm/managed/utils/envvars" "github.com/percona/pmm/utils/pdeathsig" - "github.com/percona/pmm/version" ) const ( @@ -63,18 +59,15 @@ const ( // Service is responsible for interactions with Supervisord via supervisorctl. type Service struct { - configDir string - supervisorctlPath string - gRPCMessageMaxSize uint32 - l *logrus.Entry - pmmUpdateCheck *PMMUpdateChecker + configDir string + supervisorctlPath string + l *logrus.Entry eventsM sync.Mutex subs map[chan *event]sub lastEvents map[string]eventType - pmmUpdatePerformLogM sync.Mutex - supervisordConfigsM sync.Mutex + supervisordConfigsM sync.Mutex vmParams *models.VictoriaMetricsParams pgParams *models.PGParams @@ -88,52 +81,26 @@ type sub struct { // values from supervisord configuration. const ( - pmmUpdatePerformProgram = "pmm-update-perform" - pmmUpdatePerformLog = "/srv/logs/pmm-update-perform.log" - pmmConfig = "/etc/supervisord.d/pmm.ini" + pmmConfig = "/etc/supervisord.d/pmm.ini" ) // New creates new service. -func New(configDir string, pmmUpdateCheck *PMMUpdateChecker, params *models.Params, gRPCMessageMaxSize uint32) *Service { +func New(configDir string, params *models.Params) *Service { path, _ := exec.LookPath("supervisorctl") return &Service{ - configDir: configDir, - supervisorctlPath: path, - gRPCMessageMaxSize: gRPCMessageMaxSize, - l: logrus.WithField("component", "supervisord"), - pmmUpdateCheck: pmmUpdateCheck, - subs: make(map[chan *event]sub), - lastEvents: make(map[string]eventType), - vmParams: params.VMParams, - pgParams: params.PGParams, - haParams: params.HAParams, + configDir: configDir, + supervisorctlPath: path, + l: logrus.WithField("component", "supervisord"), + subs: make(map[chan *event]sub), + lastEvents: make(map[string]eventType), + vmParams: params.VMParams, + pgParams: params.PGParams, + haParams: params.HAParams, } } // Run reads supervisord's log (maintail) and sends events to subscribers. func (s *Service) Run(ctx context.Context) { - var wg sync.WaitGroup - wg.Add(1) - go func() { - defer wg.Done() - - // pre-set installed packages info to cache it. - s.pmmUpdateCheck.Installed(ctx) - - // Do not check for updates for the first 10 minutes. - // That solves PMM Server building problems when we start pmm-managed. - // TODO https://jira.percona.com/browse/PMM-4429 - sleepCtx, sleepCancel := context.WithTimeout(ctx, 10*time.Minute) - <-sleepCtx.Done() - sleepCancel() - if ctx.Err() != nil { - return - } - - s.pmmUpdateCheck.run(ctx) - }() - defer wg.Wait() - if s.supervisorctlPath == "" { s.l.Errorf("supervisorctl not found, updates are disabled.") return @@ -210,21 +177,6 @@ func (s *Service) Run(ctx context.Context) { } } -// InstalledPMMVersion returns currently installed PMM version information. -func (s *Service) InstalledPMMVersion(ctx context.Context) *version.PackageInfo { - return s.pmmUpdateCheck.Installed(ctx) -} - -// LastCheckUpdatesResult returns last PMM update check result and last check time. -func (s *Service) LastCheckUpdatesResult(ctx context.Context) (*version.UpdateCheckResult, time.Time) { - return s.pmmUpdateCheck.checkResult(ctx) -} - -// ForceCheckUpdates forces check for PMM updates. Result can be obtained via LastCheckUpdatesResult. -func (s *Service) ForceCheckUpdates(ctx context.Context) error { - return s.pmmUpdateCheck.check(ctx) -} - func (s *Service) subscribe(program string, eventTypes ...eventType) chan *event { ch := make(chan *event, 1) s.eventsM.Lock() @@ -249,66 +201,6 @@ func (s *Service) supervisorctl(args ...string) ([]byte, error) { return b, errors.Wrapf(err, "%s failed", cmdLine) } -// StartUpdate starts pmm-update-perform supervisord program with some preparations. -// It returns initial log file offset. -func (s *Service) StartUpdate() (uint32, error) { - if s.UpdateRunning() { - return 0, status.Errorf(codes.FailedPrecondition, "Update is already running.") - } - - // We need to remove and reopen log file for UpdateStatus API to be able to read it without it being rotated. - // Additionally, SIGUSR2 is expected by our Ansible playbook. - - s.pmmUpdatePerformLogM.Lock() - defer s.pmmUpdatePerformLogM.Unlock() - - // remove existing log file - err := os.Remove(pmmUpdatePerformLog) - if err != nil && errors.Is(err, fs.ErrNotExist) { - err = nil - } - if err != nil { - s.l.Warn(err) - } - - // send SIGUSR2 to supervisord and wait for it to reopen log file - ch := s.subscribe("supervisord", logReopen) - b, err := s.supervisorctl("pid") - if err != nil { - return 0, err - } - pid, err := strconv.Atoi(strings.TrimSpace(string(b))) - if err != nil { - return 0, errors.WithStack(err) - } - p, err := os.FindProcess(pid) - if err != nil { - return 0, errors.WithStack(err) - } - if err = p.Signal(unix.SIGUSR2); err != nil { - s.l.Warnf("Failed to send SIGUSR2: %s", err) - } - s.l.Debug("Waiting for log reopen...") - <-ch - - var offset uint32 - fi, err := os.Stat(pmmUpdatePerformLog) - switch { - case err == nil: - if fi.Size() != 0 { - s.l.Warnf("Unexpected log file size: %+v", fi) - } - offset = uint32(fi.Size()) - case errors.Is(err, fs.ErrNotExist): - // that's expected as we remove this file above - default: - s.l.Warn(err) - } - - _, err = s.supervisorctl("start", pmmUpdatePerformProgram) - return offset, err -} - // parseStatus parses `supervisorctl status ` output, returns true if is running, // false if definitely not, and nil if status can't be determined. func parseStatus(status string) *bool { @@ -327,11 +219,6 @@ func parseStatus(status string) *bool { return nil } -// UpdateRunning returns true if pmm-update-perform is not done yet. -func (s *Service) UpdateRunning() bool { - return s.programRunning(pmmUpdatePerformProgram) -} - // UpdateRunning returns true if given supervisord program is running or being restarted, // false if it is not running / failed. func (s *Service) programRunning(program string) bool { @@ -367,42 +254,6 @@ func (s *Service) programRunning(program string) bool { } } -// UpdateLog returns some lines and a new offset from pmm-update-perform log starting from the given offset. -// It may return zero lines and the same offset. Caller is expected to handle this. -func (s *Service) UpdateLog(offset uint32) ([]string, uint32, error) { - s.pmmUpdatePerformLogM.Lock() - defer s.pmmUpdatePerformLogM.Unlock() - - f, err := os.Open(pmmUpdatePerformLog) - if err != nil { - return nil, 0, errors.WithStack(err) - } - defer f.Close() //nolint:errcheck,gosec,nolintlint - - if _, err = f.Seek(int64(offset), io.SeekStart); err != nil { - return nil, 0, errors.WithStack(err) - } - - lines := make([]string, 0, 10) - reader := bufio.NewReader(f) - newOffset := offset - for { - line, err := reader.ReadString('\n') - if err == nil { - newOffset += uint32(len(line)) - if newOffset-offset > s.gRPCMessageMaxSize { - return lines, newOffset - uint32(len(line)), nil - } - lines = append(lines, strings.TrimSuffix(line, "\n")) - continue - } - if err == io.EOF { - err = nil - } - return lines, newOffset, errors.WithStack(err) - } -} - // reload asks supervisord to reload configuration. func (s *Service) reload(name string) error { if _, err := s.supervisorctl("reread"); err != nil { diff --git a/managed/services/supervisord/supervisord_test.go b/managed/services/supervisord/supervisord_test.go index b0fe8ef8b3..b6cee7d384 100644 --- a/managed/services/supervisord/supervisord_test.go +++ b/managed/services/supervisord/supervisord_test.go @@ -22,19 +22,15 @@ import ( "time" "github.com/AlekSi/pointer" - "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/percona/pmm/managed/models" ) -const gRPCMessageMaxSize = uint32(100 * 1024 * 1024) - func TestConfig(t *testing.T) { t.Parallel() - pmmUpdateCheck := NewPMMUpdateChecker(logrus.WithField("component", "supervisord/pmm-update-checker_logs")) configDir := filepath.Join("..", "..", "testdata", "supervisord.d") vmParams, err := models.NewVictoriaMetricsParams(models.BasePrometheusConfigPath, models.VMBaseURL) require.NoError(t, err) @@ -48,7 +44,7 @@ func TestConfig(t *testing.T) { SSLKeyPath: "path-to-key", SSLCertPath: "path-to-cert", } - s := New(configDir, pmmUpdateCheck, &models.Params{VMParams: vmParams, PGParams: pgParams, HAParams: &models.HAParams{}}, gRPCMessageMaxSize) + s := New(configDir, &models.Params{VMParams: vmParams, PGParams: pgParams, HAParams: &models.HAParams{}}) settings := &models.Settings{ DataRetention: 30 * 24 * time.Hour, PMMPublicAddress: "192.168.0.42:8443", @@ -73,7 +69,6 @@ func TestConfig(t *testing.T) { } func TestConfigVictoriaMetricsEnvvars(t *testing.T) { - pmmUpdateCheck := NewPMMUpdateChecker(logrus.WithField("component", "supervisord/pmm-update-checker_logs")) configDir := filepath.Join("..", "..", "testdata", "supervisord.d") vmParams, err := models.NewVictoriaMetricsParams(models.BasePrometheusConfigPath, models.VMBaseURL) require.NoError(t, err) @@ -87,7 +82,7 @@ func TestConfigVictoriaMetricsEnvvars(t *testing.T) { SSLKeyPath: "path-to-key", SSLCertPath: "path-to-cert", } - s := New(configDir, pmmUpdateCheck, &models.Params{VMParams: vmParams, PGParams: pgParams, HAParams: &models.HAParams{}}, gRPCMessageMaxSize) + s := New(configDir, &models.Params{VMParams: vmParams, PGParams: pgParams, HAParams: &models.HAParams{}}) settings := &models.Settings{ DataRetention: 30 * 24 * time.Hour, PMMPublicAddress: "192.168.0.42:8443", @@ -127,8 +122,8 @@ func TestParseStatus(t *testing.T) { for str, expected := range map[string]*bool{ `pmm-agent STOPPED Sep 20 08:55 AM`: pointer.ToBool(false), `pmm-managed RUNNING pid 826, uptime 0:19:36`: pointer.ToBool(true), - `pmm-update-perform EXITED Sep 20 07:42 AM`: nil, - `pmm-update-perform STARTING`: pointer.ToBool(true), // no last column in that case + `pmm-update-perform-init EXITED Sep 20 07:42 AM`: nil, + `pmm-update-perform-init STARTING`: pointer.ToBool(true), // no last column in that case } { assert.Equal(t, expected, parseStatus(str), "%q", str) } diff --git a/managed/testdata/supervisord.d/pmm-db_disabled.ini b/managed/testdata/supervisord.d/pmm-db_disabled.ini index a9191eb4b5..5e5f482897 100644 --- a/managed/testdata/supervisord.d/pmm-db_disabled.ini +++ b/managed/testdata/supervisord.d/pmm-db_disabled.ini @@ -89,19 +89,3 @@ stdout_logfile = /srv/logs/pmm-agent.log stdout_logfile_maxbytes = 50MB stdout_logfile_backups = 2 redirect_stderr = true - -[program:pmm-update-perform] -command = /usr/sbin/pmm-update -perform -playbook=/opt/ansible/pmm-docker/update.yml -user = pmm -directory = / -autorestart = unexpected -exitcodes = 0 -autostart = false -startretries = 10 -startsecs = 1 -stopsignal = TERM -stopwaitsecs = 300 -stdout_logfile = /srv/logs/pmm-update-perform.log -stdout_logfile_maxbytes = 50MB -stdout_logfile_backups = 3 -redirect_stderr = true diff --git a/managed/testdata/supervisord.d/pmm-db_enabled.ini b/managed/testdata/supervisord.d/pmm-db_enabled.ini index bb28208342..bd0ca87ab8 100644 --- a/managed/testdata/supervisord.d/pmm-db_enabled.ini +++ b/managed/testdata/supervisord.d/pmm-db_enabled.ini @@ -113,19 +113,3 @@ stdout_logfile = /srv/logs/pmm-agent.log stdout_logfile_maxbytes = 50MB stdout_logfile_backups = 2 redirect_stderr = true - -[program:pmm-update-perform] -command = /usr/sbin/pmm-update -perform -playbook=/opt/ansible/pmm-docker/update.yml -user = pmm -directory = / -autorestart = unexpected -exitcodes = 0 -autostart = false -startretries = 10 -startsecs = 1 -stopsignal = TERM -stopwaitsecs = 300 -stdout_logfile = /srv/logs/pmm-update-perform.log -stdout_logfile_maxbytes = 50MB -stdout_logfile_backups = 3 -redirect_stderr = true diff --git a/update/main.go b/update/main.go index 4ecffaf352..68e40ab755 100644 --- a/update/main.go +++ b/update/main.go @@ -17,7 +17,6 @@ package main import ( "context" - "encoding/json" "flag" "log" "os" @@ -27,67 +26,9 @@ import ( "golang.org/x/sys/unix" "github.com/percona/pmm/update/pkg/ansible" - "github.com/percona/pmm/update/pkg/yum" "github.com/percona/pmm/version" ) -const ( - pmmManagedPackageName = "pmm-managed" -) - -func installed(ctx context.Context) { - v, err := yum.Installed(ctx, pmmManagedPackageName) - if err != nil { - logrus.Tracef("%+v", err) - logrus.Fatalf("Installed failed: %s", err) - } - if err = json.NewEncoder(os.Stdout).Encode(v); err != nil { - logrus.Fatal(err) - } -} - -func check(ctx context.Context) { - pmmManagedPackage, err := yum.Check(ctx, pmmManagedPackageName) - if err != nil { - logrus.Tracef("%+v", err) - logrus.Fatalf("Check failed: %s", err) - } - - if err = json.NewEncoder(os.Stdout).Encode(pmmManagedPackage); err != nil { - logrus.Fatal(err) - } -} - -func performStage1SelfUpdate(ctx context.Context) { - const name = "pmm-update" - v, err := yum.Installed(ctx, name) - if err != nil { - logrus.Tracef("%+v", err) - logrus.Fatalf("Installed failed before update: %s", err) - } - before := v.Installed - - if err = yum.Update(ctx, name); err != nil { - logrus.Tracef("%+v", err) - logrus.Fatalf("Update failed: %s", err) - } - - v, err = yum.Installed(ctx, name) - if err != nil { - logrus.Tracef("%+v", err) - logrus.Fatalf("Installed failed after update: %s", err) - } - after := v.Installed - - logrus.Infof("%s:\nbefore update = %+v\n after update = %+v", name, before, after) - if before.FullVersion != after.FullVersion { - // exit with non-zero code to let supervisord restart `pmm-update -perform` from the start - logrus.Info("Version changed, exiting.") - os.Exit(42) - } - logrus.Info("Version did not change.") -} - func performStage2Ansible(ctx context.Context, playbook string, opts *ansible.RunPlaybookOpts) { err := ansible.RunPlaybook(ctx, playbook, opts) if err != nil { @@ -99,22 +40,10 @@ func runAnsible(ctx context.Context, playbook string, opts *ansible.RunPlaybookO performStage2Ansible(ctx, playbook, opts) } -func perform(ctx context.Context, playbook string, opts *ansible.RunPlaybookOpts) { - performStage1SelfUpdate(ctx) - performStage2Ansible(ctx, playbook, opts) - - // pmm-managed will still wait for dashboard-upgrade to finish; - // that string is expected by various automated tests. - logrus.Info("Waiting for Grafana dashboards update to finish...") -} - // Flags have to be global variables for maincover_test.go to work. // //nolint:gochecknoglobals var ( - installedF = flag.Bool("installed", false, "Return installed version") - checkF = flag.Bool("check", false, "Check for updates") - performF = flag.Bool("perform", false, "Perform update") runPlaybookF = flag.Bool("run-playbook", false, "Run playbook without self-update") playbookF = flag.String("playbook", "", "Ansible playbook for -perform") debugF = flag.Bool("debug", false, "Enable debug logging") @@ -137,15 +66,6 @@ func main() { } var modes int - if *installedF { - modes++ - } - if *checkF { - modes++ - } - if *performF { - modes++ - } if *runPlaybookF { modes++ } @@ -165,10 +85,6 @@ func main() { }() switch { - case *installedF: - installed(ctx) - case *checkF: - check(ctx) case *runPlaybookF: if *playbookF == "" { logrus.Fatalf("-playbook flag must be set.") @@ -178,13 +94,5 @@ func main() { Debug: *debugF, Trace: *traceF, }) - case *performF: - if *playbookF == "" { - logrus.Fatalf("-playbook flag must be set.") - } - perform(ctx, *playbookF, &ansible.RunPlaybookOpts{ - Debug: *debugF, - Trace: *traceF, - }) } } diff --git a/update/pkg/yum/info.go b/update/pkg/yum/info.go deleted file mode 100644 index 95728e928d..0000000000 --- a/update/pkg/yum/info.go +++ /dev/null @@ -1,117 +0,0 @@ -// 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 yum provides functionality for yum dependency manager. -package yum - -import ( - "os/exec" - "regexp" - "strings" - "time" - - "github.com/pkg/errors" -) - -// parseInfo parses `yum info` stdout for a single version of a single package. -// Also used to parse `yum repoinfo`. -func parseInfo(lines []string, firstKey string) (map[string]string, error) { - res := make(map[string]string) - var prevKey string - var keyFound bool - for _, line := range lines { - // separate progress output from data - line = strings.TrimSpace(line) - if line == "" { - continue - } - parts := strings.SplitN(line, ":", 2) - if len(parts) != 2 { - continue - } - key := strings.TrimSpace(parts[0]) - if key == firstKey { - // sanity check that we do not try to parse multiple packages - if keyFound { - return res, errors.New("second `Name` encountered") - } - keyFound = true - } - if !keyFound { - continue - } - - // parse data while handling multiline values - value := strings.TrimSpace(parts[1]) - if key == "" { - if prevKey != "" { - res[prevKey] += " " + value - } - continue - } - res[key] = value - prevKey = key - } - return res, nil -} - -func parseInfoTime(s string) (time.Time, error) { - layout := "Mon 2 Jan 2006 15:04:05 PM UTC" // layout for EL9, default - v, err := getRHELVersion() - if err == nil && v == "7" { - layout = "Mon Jan 2 15:04:05 2006" // change the layout for EL7 - } - return time.Parse(layout, s) -} - -// fullVersion returns full (ugly) package version. -func fullVersion(info map[string]string) string { - var res string - if e := info["Epoch"]; e != "" { - res = e + ":" - } - res += info["Version"] - res += "-" + info["Release"] - return res -} - -// niceVersion returns nice user-visible package version. -func niceVersion(info map[string]string) string { - // cut suffixes from full release - release := info["Release"] - for _, re := range []*regexp.Regexp{ - regexp.MustCompile(`^(.*)\.el\d+$`), // el7 suffix - regexp.MustCompile(`^(.*)\.[0-9a-f]{7}$`), // abbreviated commit suffix - regexp.MustCompile(`^(.*)\.\d{10}$`), // timestamp suffix - } { - release = re.ReplaceAllString(release, "$1") - } - - // if there is more than just release digits (like `9.beta5` or `18.rc4`), return them; - // return only version otherwise. - if !regexp.MustCompile(`^\d+$`).MatchString(release) { - return info["Version"] + "-" + release - } - return info["Version"] -} - -func getRHELVersion() (string, error) { - raw, err := exec.Command("rpm", "--eval", "%{rhel}").Output() - if err != nil { - return "", errors.Wrap(err, "couldn't get RHEL version") - } - - return strings.TrimSpace(string(raw)), nil -} diff --git a/update/pkg/yum/info_test.go b/update/pkg/yum/info_test.go deleted file mode 100644 index 1f06c1e249..0000000000 --- a/update/pkg/yum/info_test.go +++ /dev/null @@ -1,683 +0,0 @@ -// 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 yum - -import ( - "strings" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestParseInfoEL7(t *testing.T) { - v, _ := getRHELVersion() - if v == "9" { - t.Skip("Skip running EL7 tests on EL9") - } - t.Run("Installed", func(t *testing.T) { - stdout := strings.Split(` - Loading "fastestmirror" plugin - Loading "ovl" plugin - Config time: 0.005 - rpmdb time: 0.000 - ovl: Copying up (0) files from OverlayFS lower layer - Yum version: 3.4.3 - Installed Packages - Name : pmm-managed - Arch : x86_64 - Version : 2.0.0 - Release : 9.beta5.1907301101.74f8a67.el7 - Size : 18 M - Repo : installed - From repo : local - Committer : Mykola Marzhan - Committime : Thu Sep 21 12:00:00 2017 - Buildtime : Tue Jul 30 11:02:19 2019 - Install time: Tue Jul 30 18:43:02 2019 - Installed by: 500 - Changed by : System - Summary : Percona Monitoring and Management management daemon - URL : https://github.com/percona/pmm-managed - License : AGPLv3 - Description : pmm-managed manages configuration of PMM server components (Prometheus, - : Grafana, etc.) and exposes API for that. Those APIs are used by pmm-admin tool. - : See the PMM docs for more information. - `, "\n") - expected := map[string]string{ - "Name": "pmm-managed", - "Arch": "x86_64", - "Version": "2.0.0", - "Release": "9.beta5.1907301101.74f8a67.el7", - "Size": "18 M", - "Repo": "installed", - "From repo": "local", - "Committer": "Mykola Marzhan ", - "Committime": "Thu Sep 21 12:00:00 2017", - "Buildtime": "Tue Jul 30 11:02:19 2019", - "Install time": "Tue Jul 30 18:43:02 2019", - "Installed by": "500", - "Changed by": "System ", - "Summary": "Percona Monitoring and Management management daemon", - "URL": "https://github.com/percona/pmm-managed", - "License": "AGPLv3", - "Description": "pmm-managed manages configuration of PMM server components (Prometheus, " + - "Grafana, etc.) and exposes API for that. Those APIs are used by pmm-admin tool. " + - "See the PMM docs for more information.", - } - actual, err := parseInfo(stdout, "Name") - require.NoError(t, err) - assert.Equal(t, expected, actual) - buildtime, err := parseInfoTime(actual["Buildtime"]) - require.NoError(t, err) - assert.Equal(t, time.Date(2019, 7, 30, 11, 2, 19, 0, time.UTC), buildtime) - assert.Equal(t, "2.0.0-9.beta5.1907301101.74f8a67.el7", fullVersion(actual)) - assert.Equal(t, "2.0.0-9.beta5", niceVersion(actual)) - }) - - t.Run("Updates", func(t *testing.T) { - stdout := strings.Split(` - Loading "fastestmirror" plugin - Loading "ovl" plugin - Config time: 0.017 - rpmdb time: 0.000 - ovl: Copying up (14) files from OverlayFS lower layer - Yum version: 3.4.3 - Building updates object - Setting up Package Sacks - Determining fastest mirrors - * base: mirror.reconn.ru - * epel: mirror.yandex.ru - * extras: mirror.reconn.ru - * updates: mirror.reconn.ru - pkgsack time: 14.667 - up:Obs Init time: 0.235 - up:simple updates time: 0.004 - up:obs time: 0.003 - up:condense time: 0.000 - updates time: 15.139 - Updated Packages - Name : pmm-update - Arch : noarch - Version : 2.0.0 - Release : 9.beta5.1907301223.90149dd.el7 - Size : 1.5 M - Repo : pmm-laboratory - Committer : Mykola Marzhan - Committime : Fri Jun 30 12:00:00 2017 - Buildtime : Tue Jul 30 12:23:10 2019 - Summary : Tool for updating packages and OS configuration for PMM Server - URL : https://github.com/percona/pmm/update - License : AGPLv3 - Description : Tool for updating packages and OS configuration for PMM Server - `, "\n") - expected := map[string]string{ - "Name": "pmm-update", - "Arch": "noarch", - "Version": "2.0.0", - "Release": "9.beta5.1907301223.90149dd.el7", - "Size": "1.5 M", - "Repo": "pmm-laboratory", - "Committer": "Mykola Marzhan ", - "Committime": "Fri Jun 30 12:00:00 2017", - "Buildtime": "Tue Jul 30 12:23:10 2019", - "Summary": "Tool for updating packages and OS configuration for PMM Server", - "URL": "https://github.com/percona/pmm/update", - "License": "AGPLv3", - "Description": "Tool for updating packages and OS configuration for PMM Server", - } - actual, err := parseInfo(stdout, "Name") - require.NoError(t, err) - assert.Equal(t, expected, actual) - buildtime, err := parseInfoTime(actual["Buildtime"]) - require.NoError(t, err) - assert.Equal(t, time.Date(2019, 7, 30, 12, 23, 10, 0, time.UTC), buildtime) - assert.Equal(t, "2.0.0-9.beta5.1907301223.90149dd.el7", fullVersion(actual)) - assert.Equal(t, "2.0.0-9.beta5", niceVersion(actual)) - }) - - t.Run("Available", func(t *testing.T) { - // yum --verbose --showduplicates info available pmm-update, abbreviated - stdout := strings.Split(` - Loading "fastestmirror" plugin - Loading "ovl" plugin - Config time: 0.007 - rpmdb time: 0.000 - ovl: Copying up (0) files from OverlayFS lower layer - Yum version: 3.4.3 - Setting up Package Sacks - Loading mirror speeds from cached hostfile - * base: mirror.logol.ru - * epel: fedora-mirror01.rbc.ru - * extras: mirror.logol.ru - * updates: mirror.logol.ru - pkgsack time: 0.027 - Available Packages - Name : pmm-update - Arch : noarch - Version : PMM - Release : 7.4358.1907161009.7685dba.el7 - Size : 20 k - Repo : pmm-laboratory - Committer : Mykola Marzhan - Committime : Fri Jun 30 12:00:00 2017 - Buildtime : Tue Jul 16 10:09:01 2019 - Summary : Tool for updating packages and OS configuration for PMM Server - URL : https://github.com/percona/pmm/update - License : AGPLv3 - Description : Tool for updating packages and OS configuration for PMM Server - - Name : pmm-update - Arch : noarch - Version : 2.0.0 - Release : 1.1903221448.2e245f9.el7 - Size : 20 k - Repo : pmm-laboratory - Committer : Mykola Marzhan - Committime : Fri Jun 30 12:00:00 2017 - Buildtime : Fri Mar 22 14:48:42 2019 - Summary : Tool for updating packages and OS configuration for PMM Server - URL : https://github.com/percona/pmm/update - License : AGPLv3 - Description : Tool for updating packages and OS configuration for PMM Server - - … - `, "\n") - expected := map[string]string{ - "Name": "pmm-update", - "Arch": "noarch", - "Version": "PMM", - "Release": "7.4358.1907161009.7685dba.el7", - "Size": "20 k", - "Repo": "pmm-laboratory", - "Committer": "Mykola Marzhan ", - "Committime": "Fri Jun 30 12:00:00 2017", - "Buildtime": "Tue Jul 16 10:09:01 2019", - "Summary": "Tool for updating packages and OS configuration for PMM Server", - "URL": "https://github.com/percona/pmm/update", - "License": "AGPLv3", - "Description": "Tool for updating packages and OS configuration for PMM Server", - } - actual, err := parseInfo(stdout, "Name") - assert.EqualError(t, err, "second `Name` encountered") - assert.Equal(t, expected, actual) - buildtime, err := parseInfoTime(actual["Buildtime"]) - require.NoError(t, err) - assert.Equal(t, time.Date(2019, 7, 16, 10, 9, 1, 0, time.UTC), buildtime) - assert.Equal(t, "PMM-7.4358.1907161009.7685dba.el7", fullVersion(actual)) - assert.Equal(t, "PMM-7.4358", niceVersion(actual)) // yes, that one is broken - }) - - t.Run("AvailableGA", func(t *testing.T) { - // yum --verbose --showduplicates info available pmm-update, abbreviated - stdout := strings.Split(` - Available Packages - Name : pmm-update - Arch : noarch - Version : 2.0.0 - Release : 18.1909180550.6de91ea.el7 - Size : 857 k - Repo : pmm-server - Committer : Alexey Palazhchenko - Committime : Wed Sep 18 12:00:00 2019 - Buildtime : Wed Sep 18 05:51:01 2019 - Summary : Tool for updating packages and OS configuration for PMM Server - URL : https://github.com/percona/pmm/update - License : AGPLv3 - Description : Tool for updating packages and OS configuration for PMM Server - - … - `, "\n") - expected := map[string]string{ - "Name": "pmm-update", - "Arch": "noarch", - "Version": "2.0.0", - "Release": "18.1909180550.6de91ea.el7", - "Size": "857 k", - "Repo": "pmm-server", - "Committer": "Alexey Palazhchenko ", - "Committime": "Wed Sep 18 12:00:00 2019", - "Buildtime": "Wed Sep 18 05:51:01 2019", - "Summary": "Tool for updating packages and OS configuration for PMM Server", - "URL": "https://github.com/percona/pmm/update", - "License": "AGPLv3", - "Description": "Tool for updating packages and OS configuration for PMM Server", - } - actual, err := parseInfo(stdout, "Name") - require.NoError(t, err) - assert.Equal(t, expected, actual) - buildtime, err := parseInfoTime(actual["Buildtime"]) - require.NoError(t, err) - assert.Equal(t, time.Date(2019, 9, 18, 5, 51, 1, 0, time.UTC), buildtime) - assert.Equal(t, "2.0.0-18.1909180550.6de91ea.el7", fullVersion(actual)) - assert.Equal(t, "2.0.0", niceVersion(actual)) - }) - - t.Run("Empty", func(t *testing.T) { - // "Error: No matching Packages to list" goes to stderr. - stdout := strings.Split(` - Loading "fastestmirror" plugin - Loading "ovl" plugin - Config time: 0.007 - rpmdb time: 0.000 - ovl: Copying up (0) files from OverlayFS lower layer - Yum version: 3.4.3 - Building updates object - Setting up Package Sacks - Loading mirror speeds from cached hostfile - * base: mirror.logol.ru - * epel: fedora-mirror01.rbc.ru - * extras: mirror.logol.ru - * updates: mirror.logol.ru - pkgsack time: 0.030 - up:Obs Init time: 0.217 - up:simple updates time: 0.008 - up:obs time: 0.004 - up:condense time: 0.000 - updates time: 0.469 - `, "\n") - actual, err := parseInfo(stdout, "Name") - require.NoError(t, err) - assert.Empty(t, actual) - }) - - t.Run("RepoInfo", func(t *testing.T) { - // yum repoinfo pmm-server. - stdout := strings.Split(` - Loaded plugins: changelog, fastestmirror, ovl - Loading mirror speeds from cached hostfile - * base: centos.schlundtech.de - * epel: mirror.netcologne.de - * extras: centos.mirror.iphh.net - * updates: mirror.netcologne.de - Repo-id : pmm-server - Repo-name : PMM Server YUM repository - x86_64 - Repo-status : enabled - Repo-revision: 1622561436 - Repo-updated : Tue Jun 1 15:30:45 2021 - Repo-pkgs : 237 - Repo-size : 2.4 G - Repo-baseurl : https://repo.percona.com/pmm3-components/yum/experimental/7/RPMS/x86_64/ - Repo-expire : 21600 second(s) (last: Thu Jun 10 16:08:12 2021) - Filter : read-only:present - Repo-filename: /etc/yum.repos.d/pmm-server.repo - - repolist: 237 - … - `, "\n") - expected := map[string]string{ - "Repo-id": "pmm-server", - "Repo-name": "PMM Server YUM repository - x86_64", - "Repo-status": "enabled", - "Repo-revision": "1622561436", - "Repo-updated": "Tue Jun 1 15:30:45 2021", - "Repo-pkgs": "237", - "Repo-size": "2.4 G", - "Repo-baseurl": "https://repo.percona.com/pmm3-components/yum/experimental/7/RPMS/x86_64/", - "Repo-expire": "21600 second(s) (last: Thu Jun 10 16:08:12 2021)", - "Filter": "read-only:present", - "Repo-filename": "/etc/yum.repos.d/pmm-server.repo", - "repolist": "237", - } - actual, err := parseInfo(stdout, "Repo-id") - require.NoError(t, err) - assert.Equal(t, expected, actual) - releasetime, err := parseInfoTime(actual["Repo-updated"]) - require.NoError(t, err) - assert.Equal(t, time.Date(2021, 6, 1, 15, 30, 45, 0, time.UTC), releasetime) - }) -} - -func TestParseInfoEL9(t *testing.T) { - v, _ := getRHELVersion() - if v == "7" { - t.Skip("Skip running EL9 tests on EL7") - } - t.Run("Installed EL9", func(t *testing.T) { - stdout := strings.Split(` - Starting "yum --verbose info installed pmm-managed" ... - Loaded plugins: builddep, changelog, config-manager, copr, debug, debuginfo-install, download, generate_completion_cache, groups-manager, needs-restarting, playground, repoclosure, repodiff, repograph, repomanage, reposync, system-upgrade - YUM version: 4.14.0 - cachedir: /var/cache/dnf - Unknown configuration option: async = 1 in /etc/yum.repos.d/clickhouse.repo - Unknown configuration option: async = 1 in /etc/yum.repos.d/local.repo - Unknown configuration option: async = 1 in /etc/yum.repos.d/nginx.repo - Unknown configuration option: async = 1 in /etc/yum.repos.d/percona-ppg-11.repo - Unknown configuration option: async = 1 in /etc/yum.repos.d/percona-ppg-14.repo - Unknown configuration option: async = 1 in /etc/yum.repos.d/pmm-server.repo - User-Agent: constructed: 'libdnf (Oracle Linux Server 9.2; server; Linux.x86_64)' - Installed Packages - Name : pmm-managed - Version : 2.39.0 - Release : 20.2306271313.b6d58b6.el9 - Architecture : x86_64 - Size : 125 M - Source : pmm-managed-2.39.0-20.2306271313.b6d58b6.el9.src.rpm - Repository : @System - From repo : local - Packager : None - Buildtime : Tue 27 Jun 2023 01:13:03 PM UTC - Install time : Tue 27 Jun 2023 01:31:05 PM UTC - Installed by : System - Summary : Percona Monitoring and Management management daemon - URL : https://github.com/percona/pmm - License : AGPLv3 - Description : pmm-managed manages configuration of PMM server components (VictoriaMetrics, - : Grafana, etc.) and exposes API for that. Those APIs are used by pmm-admin tool. - : See PMM docs for more information. - `, "\n") - expected := map[string]string{ - "Name": "pmm-managed", - "Version": "2.39.0", - "Release": "20.2306271313.b6d58b6.el9", - "Architecture": "x86_64", - "Size": "125 M", - "Source": "pmm-managed-2.39.0-20.2306271313.b6d58b6.el9.src.rpm", - "Repository": "@System", - "From repo": "local", - "Packager": "None", - "Buildtime": "Tue 27 Jun 2023 01:13:03 PM UTC", - "Install time": "Tue 27 Jun 2023 01:31:05 PM UTC", - "Installed by": "System ", - "Summary": "Percona Monitoring and Management management daemon", - "URL": "https://github.com/percona/pmm", - "License": "AGPLv3", - "Description": "pmm-managed manages configuration of PMM server components (VictoriaMetrics, " + - "Grafana, etc.) and exposes API for that. Those APIs are used by pmm-admin tool. " + - "See PMM docs for more information.", - } - actual, err := parseInfo(stdout, "Name") - require.NoError(t, err) - assert.Equal(t, expected, actual) - buildtime, err := parseInfoTime(actual["Buildtime"]) - require.NoError(t, err) - assert.Equal(t, time.Date(2023, 6, 27, 13, 13, 3, 0, time.UTC), buildtime) - assert.Equal(t, "2.39.0-20.2306271313.b6d58b6.el9", fullVersion(actual)) - assert.Equal(t, "2.39.0", niceVersion(actual)) - }) - - t.Run("Updates EL9", func(t *testing.T) { - // yum --verbose info updates pmm-update - stdout := strings.Split(` - Loaded plugins: builddep, changelog, config-manager, copr, debug, debuginfo-install, download, generate_completion_cache, groups-manager, needs-restarting, playground, repoclosure, repodiff, repograph, repomanage, reposync, system-upgrade - YUM version: 4.14.0 - cachedir: /var/cache/dnf - Unknown configuration option: async = 1 in /etc/yum.repos.d/clickhouse.repo - Unknown configuration option: async = 1 in /etc/yum.repos.d/local.repo - Unknown configuration option: async = 1 in /etc/yum.repos.d/nginx.repo - Unknown configuration option: async = 1 in /etc/yum.repos.d/percona-ppg-11.repo - Unknown configuration option: async = 1 in /etc/yum.repos.d/percona-ppg-14.repo - Unknown configuration option: async = 1 in /etc/yum.repos.d/pmm-server.repo - User-Agent: constructed: 'libdnf (Oracle Linux Server 9.2; server; Linux.x86_64)' - repo: using cache for: ol9_developer_EPEL - ol9_developer_EPEL: using metadata from Wed 28 Jun 2023 02:28:50 PM UTC. - repo: using cache for: ol9_baseos_latest - ol9_baseos_latest: using metadata from Fri 23 Jun 2023 04:59:24 AM UTC. - repo: using cache for: ol9_appstream - ol9_appstream: using metadata from Fri 23 Jun 2023 05:03:17 AM UTC. - repo: using cache for: percona-release-x86_64 - percona-release-x86_64: using metadata from Mon 26 Jun 2023 01:02:27 PM UTC. - repo: using cache for: percona-release-noarch - percona-release-noarch: using metadata from Wed 06 Jul 2022 08:25:44 PM UTC. - repo: using cache for: percona-testing-x86_64 - percona-testing-x86_64: using metadata from Wed 28 Jun 2023 05:27:06 PM UTC. - repo: using cache for: percona-testing-noarch - percona-testing-noarch: using metadata from Wed 06 Jul 2022 08:20:55 PM UTC. - repo: using cache for: percona-ppg-11 - percona-ppg-11: using metadata from Mon 22 May 2023 08:40:15 AM UTC. - repo: using cache for: percona-ppg-14 - percona-ppg-14: using metadata from Wed 28 Jun 2023 02:57:51 PM UTC. - repo: using cache for: prel-release-noarch - prel-release-noarch: using metadata from Thu 16 Sep 2021 06:35:55 AM UTC. - repo: using cache for: pmm-server - pmm-server: using metadata from Wed 28 Jun 2023 02:46:09 PM UTC. - Last metadata expiration check: 1:42:51 ago on Wed 28 Jun 2023 07:06:43 PM UTC. - Available Upgrades - Name : pmm-update - Version : 2.39.0 - Release : 67.2306281336.d0d7fcb.el9 - Architecture : noarch - Size : 886 k - Source : pmm-update-2.39.0-67.2306281336.d0d7fcb.el9.src.rpm - Repository : pmm-server - Packager : None - Buildtime : Wed 28 Jun 2023 01:36:03 PM UTC - Summary : Tool for updating packages and OS configuration for PMM Server - URL : https://github.com/percona/pmm - License : AGPLv3 - Description : Tool for updating packages and OS configuration for PMM Server - `, "\n") - expected := map[string]string{ - "Name": "pmm-update", - "Architecture": "noarch", - "Version": "2.39.0", - "Release": "67.2306281336.d0d7fcb.el9", - "Size": "886 k", - "Source": "pmm-update-2.39.0-67.2306281336.d0d7fcb.el9.src.rpm", - "Repository": "pmm-server", - "Packager": "None", - "Buildtime": "Wed 28 Jun 2023 01:36:03 PM UTC", - "Summary": "Tool for updating packages and OS configuration for PMM Server", - "URL": "https://github.com/percona/pmm", - "License": "AGPLv3", - "Description": "Tool for updating packages and OS configuration for PMM Server", - } - actual, err := parseInfo(stdout, "Name") - require.NoError(t, err) - assert.Equal(t, expected, actual) - buildtime, err := parseInfoTime(actual["Buildtime"]) - require.NoError(t, err) - assert.Equal(t, time.Date(2023, 6, 28, 13, 36, 3, 0, time.UTC), buildtime) - assert.Equal(t, "2.39.0-67.2306281336.d0d7fcb.el9", fullVersion(actual)) - assert.Equal(t, "2.39.0", niceVersion(actual)) - }) - - t.Run("AvailableGA EL9", func(t *testing.T) { - // yum --verbose --showduplicates info available pmm-update (just two versions) - stdout := strings.Split(` - Loaded plugins: builddep, changelog, config-manager, copr, debug, debuginfo-install, download, generate_completion_cache, groups-manager, needs-restarting, playground, repoclosure, repodiff, repograph, repomanage, reposync, system-upgrade - YUM version: 4.14.0 - cachedir: /var/cache/dnf - Unknown configuration option: async = 1 in /etc/yum.repos.d/clickhouse.repo - Unknown configuration option: async = 1 in /etc/yum.repos.d/local.repo - Unknown configuration option: async = 1 in /etc/yum.repos.d/nginx.repo - Unknown configuration option: async = 1 in /etc/yum.repos.d/percona-ppg-11.repo - Unknown configuration option: async = 1 in /etc/yum.repos.d/percona-ppg-14.repo - Unknown configuration option: async = 1 in /etc/yum.repos.d/pmm-server.repo - User-Agent: constructed: 'libdnf (Oracle Linux Server 9.2; server; Linux.x86_64)' - repo: using cache for: ol9_developer_EPEL - ol9_developer_EPEL: using metadata from Wed 28 Jun 2023 02:28:50 PM UTC. - repo: using cache for: ol9_baseos_latest - ol9_baseos_latest: using metadata from Fri 23 Jun 2023 04:59:24 AM UTC. - repo: using cache for: ol9_appstream - ol9_appstream: using metadata from Fri 23 Jun 2023 05:03:17 AM UTC. - repo: using cache for: percona-release-x86_64 - percona-release-x86_64: using metadata from Mon 26 Jun 2023 01:02:27 PM UTC. - repo: using cache for: percona-release-noarch - percona-release-noarch: using metadata from Wed 06 Jul 2022 08:25:44 PM UTC. - repo: using cache for: percona-testing-x86_64 - percona-testing-x86_64: using metadata from Wed 28 Jun 2023 05:27:06 PM UTC. - repo: using cache for: percona-testing-noarch - percona-testing-noarch: using metadata from Wed 06 Jul 2022 08:20:55 PM UTC. - repo: using cache for: percona-ppg-11 - percona-ppg-11: using metadata from Mon 22 May 2023 08:40:15 AM UTC. - repo: using cache for: percona-ppg-14 - percona-ppg-14: using metadata from Wed 28 Jun 2023 02:57:51 PM UTC. - repo: using cache for: prel-release-noarch - prel-release-noarch: using metadata from Thu 16 Sep 2021 06:35:55 AM UTC. - repo: using cache for: pmm-server - pmm-server: using metadata from Wed 28 Jun 2023 02:46:09 PM UTC. - Last metadata expiration check: 1:18:00 ago on Wed 28 Jun 2023 07:06:43 PM UTC. - Available Packages - Name : pmm-update - Version : 2.39.0 - Release : 67.2306280932.70f3748.el9 - Architecture : noarch - Size : 887 k - Source : pmm-update-2.39.0-67.2306280932.70f3748.el9.src.rpm - Repository : pmm-server - Packager : None - Buildtime : Wed 28 Jun 2023 09:32:21 AM UTC - Summary : Tool for updating packages and OS configuration for PMM Server - URL : https://github.com/percona/pmm - License : AGPLv3 - Description : Tool for updating packages and OS configuration for PMM Server - - Name : pmm-update - Version : 2.39.0 - Release : 67.2306281012.fe8e947.el9 - Architecture : noarch - Size : 887 k - Source : pmm-update-2.39.0-67.2306281012.fe8e947.el9.src.rpm - Repository : pmm-server - Packager : None - Buildtime : Wed 28 Jun 2023 10:12:02 AM UTC - Summary : Tool for updating packages and OS configuration for PMM Server - URL : https://github.com/percona/pmm - License : AGPLv3 - Description : Tool for updating packages and OS configuration for PMM Server - `, "\n") - expected := map[string]string{ - "Name": "pmm-update", - "Architecture": "noarch", - "Version": "2.39.0", - "Release": "67.2306280932.70f3748.el9", - "Size": "887 k", - "Source": "pmm-update-2.39.0-67.2306280932.70f3748.el9.src.rpm", - "Repository": "pmm-server", - "Packager": "None", - "Buildtime": "Wed 28 Jun 2023 09:32:21 AM UTC", - "Summary": "Tool for updating packages and OS configuration for PMM Server", - "URL": "https://github.com/percona/pmm", - "License": "AGPLv3", - "Description": "Tool for updating packages and OS configuration for PMM Server", - } - actual, err := parseInfo(stdout, "Name") - assert.EqualError(t, err, "second `Name` encountered") - assert.Equal(t, expected, actual) - buildtime, err := parseInfoTime(actual["Buildtime"]) - require.NoError(t, err) - assert.Equal(t, time.Date(2023, 6, 28, 9, 32, 21, 0, time.UTC), buildtime) - assert.Equal(t, "2.39.0-67.2306280932.70f3748.el9", fullVersion(actual)) - assert.Equal(t, "2.39.0", niceVersion(actual)) - }) - - t.Run("Empty EL9", func(t *testing.T) { - // yum --verbose info updates pmm-managed - // "Error: No matching Packages to list" goes to stderr. - // The output below is generated when there are no updates available. - stdout := strings.Split(` - Loaded plugins: builddep, changelog, config-manager, copr, debug, debuginfo-install, download, generate_completion_cache, groups-manager, needs-restarting, playground, repoclosure, repodiff, repograph, repomanage, reposync, system-upgrade - YUM version: 4.14.0 - cachedir: /var/cache/dnf - Unknown configuration option: async = 1 in /etc/yum.repos.d/clickhouse.repo - Unknown configuration option: async = 1 in /etc/yum.repos.d/local.repo - Unknown configuration option: async = 1 in /etc/yum.repos.d/nginx.repo - Unknown configuration option: async = 1 in /etc/yum.repos.d/percona-ppg-11.repo - Unknown configuration option: async = 1 in /etc/yum.repos.d/percona-ppg-14.repo - Unknown configuration option: async = 1 in /etc/yum.repos.d/pmm-server.repo - User-Agent: constructed: 'libdnf (Oracle Linux Server 9.2; server; Linux.x86_64)' - repo: using cache for: ol9_developer_EPEL - ol9_developer_EPEL: using metadata from Wed 28 Jun 2023 02:28:50 PM UTC. - repo: using cache for: ol9_baseos_latest - ol9_baseos_latest: using metadata from Fri 23 Jun 2023 04:59:24 AM UTC. - repo: using cache for: ol9_appstream - ol9_appstream: using metadata from Fri 23 Jun 2023 05:03:17 AM UTC. - repo: using cache for: percona-release-x86_64 - percona-release-x86_64: using metadata from Mon 26 Jun 2023 01:02:27 PM UTC. - repo: using cache for: percona-release-noarch - percona-release-noarch: using metadata from Wed 06 Jul 2022 08:25:44 PM UTC. - repo: using cache for: percona-testing-x86_64 - percona-testing-x86_64: using metadata from Wed 28 Jun 2023 05:27:06 PM UTC. - repo: using cache for: percona-testing-noarch - percona-testing-noarch: using metadata from Wed 06 Jul 2022 08:20:55 PM UTC. - repo: using cache for: percona-ppg-11 - percona-ppg-11: using metadata from Mon 22 May 2023 08:40:15 AM UTC. - repo: using cache for: percona-ppg-14 - percona-ppg-14: using metadata from Wed 28 Jun 2023 02:57:51 PM UTC. - repo: using cache for: prel-release-noarch - prel-release-noarch: using metadata from Thu 16 Sep 2021 06:35:55 AM UTC. - repo: using cache for: pmm-server - pmm-server: using metadata from Wed 28 Jun 2023 02:46:09 PM UTC. - Last metadata expiration check: 0:59:54 ago on Wed 28 Jun 2023 07:06:43 PM UTC. - `, "\n") - actual, err := parseInfo(stdout, "Name") - require.NoError(t, err) - assert.Empty(t, actual) - }) - - t.Run("RepoInfo EL9", func(t *testing.T) { - // yum repoinfo pmm-server. - stdout := strings.Split(` - Last metadata expiration check: 9:26:06 ago on Wed 28 Jun 2023 09:26:18 AM UTC. - Repo-id : pmm-server - Repo-name : PMM Server YUM repository - x86_64 - Repo-status : enabled - Repo-revision : 1687873070 - Repo-updated : Tue 27 Jun 2023 01:25:23 PM UTC - Repo-pkgs : 478 - Repo-available-pkgs: 478 - Repo-size : 3.7 G - Repo-baseurl : https://repo.percona.com/pmm3-components/yum/experimental/9/RPMS/x86_64/ - Repo-expire : 172,800 second(s) (last: Wed 28 Jun 2023 09:26:18 AM UTC) - Repo-filename : /etc/yum.repos.d/pmm-server.repo - Total packages: 478 - `, "\n") - expected := map[string]string{ - "Repo-id": "pmm-server", - "Repo-name": "PMM Server YUM repository - x86_64", - "Repo-status": "enabled", - "Repo-revision": "1687873070", - "Repo-updated": "Tue 27 Jun 2023 01:25:23 PM UTC", - "Repo-pkgs": "478", - "Repo-available-pkgs": "478", - "Repo-size": "3.7 G", - "Repo-baseurl": "https://repo.percona.com/pmm3-components/yum/experimental/9/RPMS/x86_64/", - "Repo-expire": "172,800 second(s) (last: Wed 28 Jun 2023 09:26:18 AM UTC)", - "Repo-filename": "/etc/yum.repos.d/pmm-server.repo", - "Total packages": "478", - } - actual, err := parseInfo(stdout, "Repo-id") - require.NoError(t, err) - assert.Equal(t, expected, actual) - releasetime, err := parseInfoTime(actual["Repo-updated"]) - require.NoError(t, err) - assert.Equal(t, time.Date(2023, time.June, 27, 13, 25, 23, 0, time.UTC), releasetime) - }) -} - -func TestGetRHELVersion(t *testing.T) { - t.Run("getRHELVersion EL9", func(t *testing.T) { - actual, err := getRHELVersion() - if actual == "7" { - t.Skip("Skip running EL9 tests on EL7") - } - expected := "9" - require.NoError(t, err) - assert.Equal(t, expected, actual) - }) - - t.Run("getRHELVersion EL7", func(t *testing.T) { - actual, err := getRHELVersion() - if actual == "9" { - t.Skip("Skip running EL7 test on EL9") - } - expected := "7" - require.NoError(t, err) - assert.Equal(t, expected, actual) - }) -} diff --git a/update/pkg/yum/yum.go b/update/pkg/yum/yum.go deleted file mode 100644 index 008ad208b1..0000000000 --- a/update/pkg/yum/yum.go +++ /dev/null @@ -1,154 +0,0 @@ -// 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 yum - -import ( - "context" - "strings" - "time" - - "github.com/pkg/errors" - - "github.com/percona/pmm/update/pkg/run" - "github.com/percona/pmm/version" -) - -const ( - yumInfoCancelTimeout = 30 * time.Second // must be _much_ less than stopwaitsecs in supervisord config - yumUpdateCancelTimeout = 120 * time.Second // must be less than stopwaitsecs in supervisord config - changeLogURLPath = "https://per.co.na/pmm/" -) - -// http://man7.org/linux/man-pages/man8/yum.8.html#LIST_OPTIONS - -// Installed returns current version information for a package with given name. -// It runs quickly. -func Installed(ctx context.Context, name string) (*version.UpdateInstalledResult, error) { - cmdLine := "yum --verbose info installed " + name - stdout, _, err := run.Run(ctx, yumInfoCancelTimeout, cmdLine, nil) - if err != nil { - return nil, errors.Wrapf(err, "%#q failed", cmdLine) - } - - info, err := parseInfo(stdout, "Name") - if err != nil { - return nil, err - } - res := version.PackageInfo{ - Version: niceVersion(info), - FullVersion: fullVersion(info), - Repo: info["From repo"], - } - buildTime, err := parseInfoTime(info["Buildtime"]) - if err == nil { - res.BuildTime = &buildTime - } - return &version.UpdateInstalledResult{ - Installed: res, - }, nil -} - -// getReleaseTime returns date and time when repo was updated (packages published or repo got rebuilt). -func getReleaseTime(ctx context.Context, repo string) (string, error) { - cmdLine := "yum repoinfo " + repo - stdout, _, err := run.Run(ctx, yumInfoCancelTimeout, cmdLine, nil) - if err != nil { - return "", errors.Wrapf(err, "%#q failed", cmdLine) - } - - info, err := parseInfo(stdout, "Repo-id") - if err != nil { - return "", err - } - - if time, ok := info["Repo-updated"]; ok { - return time, nil - } - - return "", errors.New("Repo-updated field is not found in repoinfo") -} - -// Check returns up-to-date versions information for a package with given name. -// It runs slowly. -func Check(ctx context.Context, name string) (*version.UpdateCheckResult, error) { - repoPropName := "Repository" // default value for RHEL9 - - installed, err := Installed(ctx, name) - if err != nil { - return nil, err - } - res := &version.UpdateCheckResult{ - Installed: installed.Installed, - } - - cmdLine := "yum --verbose info updates " + name - stdout, stderr, err := run.Run(ctx, yumInfoCancelTimeout, cmdLine, nil) - if err != nil { - if strings.Contains(strings.Join(stderr, "\n"), "Error: No matching Packages to list") { - // no update available, return the same values - res.Latest = res.Installed - return res, nil - } - - return nil, errors.Wrapf(err, "%#q failed", cmdLine) - } - - info, err := parseInfo(stdout, "Name") - if err != nil { - return nil, err - } - - v, err := getRHELVersion() - if err == nil && v == "7" { - // change the prop name for EL7 - repoPropName = "Repo" - } - repo, ok := info[repoPropName] - if !ok { - return nil, errors.New("Repository field is not found in yum info") - } - - res.Latest = version.PackageInfo{ - Version: niceVersion(info), - FullVersion: fullVersion(info), - Repo: repo, - } - - // replace Buildtime with repo release time to show time of release. - repoUpdated, err := getReleaseTime(ctx, repo) - if err != nil { - return nil, err - } - releaseTime, err := parseInfoTime(repoUpdated) - if err == nil { - res.Latest.BuildTime = &releaseTime - } - - res.LatestNewsURL = changeLogURLPath + res.Latest.Version - - res.UpdateAvailable = true - return res, nil -} - -// Update updates package with given name. -func Update(ctx context.Context, name string) error { - cmdLine := "yum update --assumeyes " + name - _, _, err := run.Run(ctx, yumUpdateCancelTimeout, cmdLine, nil) - if err != nil { - return errors.Wrapf(err, "%#q failed", cmdLine) - } - return nil -} diff --git a/update/pkg/yum/yum_test.go b/update/pkg/yum/yum_test.go deleted file mode 100644 index a508b47749..0000000000 --- a/update/pkg/yum/yum_test.go +++ /dev/null @@ -1,100 +0,0 @@ -// 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 yum - -import ( - "context" - "os" - "strings" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -var gaReleaseDate = time.Date(2019, 9, 18, 0, 0, 0, 0, time.UTC) - -const ( - pmmManagedPackageName = "pmm-managed" -) - -func TestInstalled(t *testing.T) { - res, err := Installed(context.Background(), pmmManagedPackageName) - require.NoError(t, err) - - assert.True(t, strings.HasPrefix(res.Installed.Version, "3."), "installed version should start with `3.`. Actual value is: %s", res.Installed.Version) - assert.True(t, strings.HasPrefix(res.Installed.FullVersion, "3."), "installed full version should start with `3.`. Actual value is: %s", res.Installed.FullVersion) - require.NotEmpty(t, res.Installed.BuildTime) - assert.True(t, res.Installed.BuildTime.After(gaReleaseDate), "Installed.BuildTime = %s", res.Installed.BuildTime) - assert.Equal(t, "local", res.Installed.Repo) -} - -func TestCheck(t *testing.T) { - res, err := Check(context.Background(), pmmManagedPackageName) - - require.NoError(t, err) - - assert.True(t, strings.HasPrefix(res.Installed.Version, "3."), "installed version should start with `3.`. Actual value is: %s", res.Installed.Version) - assert.True(t, strings.HasPrefix(res.Installed.FullVersion, "3."), "installed full version should start with `3.`. Actual value is: %s", res.Installed.FullVersion) - require.NotEmpty(t, res.Installed.BuildTime) - assert.True(t, res.Installed.BuildTime.After(gaReleaseDate), "Installed.BuildTime = %s", res.Installed.BuildTime) - assert.Equal(t, "local", res.Installed.Repo) - - assert.True(t, strings.HasPrefix(res.Latest.Version, "3."), "The latest available version should start with `3.`. Actual value is: %s", res.Latest.Version) - assert.True(t, strings.HasPrefix(res.Latest.FullVersion, "3."), "The latest available versions full value should start with `3.`. Actual value is: %s", res.Latest.FullVersion) - require.NotEmpty(t, res.Latest.BuildTime) - assert.True(t, res.Latest.BuildTime.After(gaReleaseDate), "Latest.BuildTime = %s", res.Latest.BuildTime) - assert.NotEmpty(t, res.Latest.Repo) - - // We assume that the latest perconalab/pmm-server:3-dev-latest image - // always contains the latest pmm-update package versions. - // If this test fails, re-pull them and recreate devcontainer. - var updateAvailable bool - image := os.Getenv("PMM_SERVER_IMAGE") - require.NotEmpty(t, image) - if image != "perconalab/pmm-server:3-dev-latest" { - updateAvailable = true - } - - if updateAvailable { - t.Log("Assuming pmm-update update is available.") - assert.True(t, res.UpdateAvailable, "update should be available") - - // latest_news_url may not be present yet for this version if VERSION file was bumped already, - // but pmm-update.spec's changelog wasn't updated yet - if res.LatestNewsURL != "" { - assert.True(t, strings.HasPrefix(res.LatestNewsURL, "https://per.co.na/pmm/3."), "latest_news_url = %q", res.LatestNewsURL) - } - - assert.NotEqual(t, res.Installed.Version, res.Latest.Version, "versions should not be the same") - assert.NotEqual(t, res.Installed.FullVersion, res.Latest.FullVersion, "versions should not be the same") - assert.NotEqual(t, *res.Installed.BuildTime, *res.Latest.BuildTime, "build times should not be the same (%s)", *res.Installed.BuildTime) - assert.Equal(t, "pmm-server", res.Latest.Repo) - } else { - t.Log("Assuming the latest pmm-update version.") - assert.False(t, res.UpdateAvailable, "update should not be available") - assert.Empty(t, res.LatestNewsURL, "latest_news_url should be empty") - assert.Equal(t, res.Installed, res.Latest, "version should be the same (latest)") - assert.Equal(t, *res.Installed.BuildTime, *res.Latest.BuildTime, "build times should be the same") - assert.Equal(t, "local", res.Latest.Repo) - } -} - -func TestUpdate(t *testing.T) { - err := Update(context.Background(), "make") - require.NoError(t, err) -} diff --git a/version/parsed.go b/version/parsed.go index dce3d6887d..a256beeef6 100644 --- a/version/parsed.go +++ b/version/parsed.go @@ -19,6 +19,7 @@ import ( "fmt" "regexp" "strconv" + "strings" "github.com/pkg/errors" ) @@ -100,3 +101,15 @@ func (p *Parsed) Less(right *Parsed) bool { return p.Rest < right.Rest } + +// UnmarshalJSON implements json.Unmarshaler interface and allows to unmarshal version information from JSON. +func (p *Parsed) UnmarshalJSON(b []byte) error { + s := string(b) + s = strings.Trim(s, `"`) + parsed, err := Parse(s) + if err != nil { + return err + } + *p = *parsed + return nil +} diff --git a/version/update.go b/version/update.go index 5441043343..235004df4d 100644 --- a/version/update.go +++ b/version/update.go @@ -32,8 +32,15 @@ type UpdateInstalledResult struct { // UpdateCheckResult represents `pmm-update -check` result. type UpdateCheckResult struct { - Installed PackageInfo `json:"installed"` - Latest PackageInfo `json:"latest,omitempty"` - UpdateAvailable bool `json:"update_available"` - LatestNewsURL string `json:"latest_news_url"` + Installed PackageInfo `json:"installed"` + Latest DockerVersionInfo `json:"latest,omitempty"` + UpdateAvailable bool `json:"update_available"` + LatestNewsURL string `json:"latest_news_url"` +} + +// DockerVersionInfo describes the version of the Docker image. +type DockerVersionInfo struct { + Version Parsed `json:"version"` + DockerImage string `json:"docker_image"` + BuildTime time.Time `json:"build_time"` }