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 0fef5fb206..d801759200 100644 Binary files a/descriptor.bin and b/descriptor.bin differ diff --git a/docker-compose.yml b/docker-compose.yml index b9f29f11f6..8ad62bdc64 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -25,6 +25,7 @@ services: - PMM_DEV_VERSION_SERVICE_URL=${PMM_DEV_VERSION_SERVICE_URL} - PMM_WATCHTOWER_HOST=${PMM_WATCHTOWER_HOST:-http://watchtower:8080} - PMM_WATCHTOWER_TOKEN=${PMM_WATCHTOWER_TOKEN:-INSECURE_TOKEN} + - PMM_RELEASE_VERSION=3.0.0-alpha # - PMM_DEV_PERCONA_PLATFORM_ADDRESS=https://check.localhost # - PMM_DEV_PERCONA_PLATFORM_INSECURE=1 # - PMM_DEV_PERCONA_PLATFORM_PUBLIC_KEY= @@ -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"` }