From 2d726f8dc179a64be0de49577bf95c82c6d19c49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Tigerstr=C3=B6m?= Date: Tue, 2 Jan 2024 19:18:13 +0100 Subject: [PATCH 01/11] status: add functional opts on SubServerStatus init Refactor Register & Enable functions to enable setting of SubServerOptions, i.e. functional options, when initiating a SubServer. --- status/manager.go | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/status/manager.go b/status/manager.go index d3996416a..13a5fe1ac 100644 --- a/status/manager.go +++ b/status/manager.go @@ -9,6 +9,10 @@ import ( "github.com/lightninglabs/lightning-terminal/litrpc" ) +// SubServerOption defines a functional option that can be used to modify the +// values of a SubServerStatus's fields. +type SubServerOption func(status *SubServerStatus) + // SubServerStatus represents the status of a sub-server. type SubServerStatus struct { // Disabled is true if the sub-server is available in the LiT bundle but @@ -24,9 +28,11 @@ type SubServerStatus struct { } // newSubServerStatus constructs a new SubServerStatus. -func newSubServerStatus() *SubServerStatus { +func newSubServerStatus(disabled bool, + opts ...SubServerOption) *SubServerStatus { + return &SubServerStatus{ - Disabled: true, + Disabled: disabled, } } @@ -73,21 +79,28 @@ func (s *Manager) SubServerStatus(_ context.Context, // RegisterSubServer will create a new sub-server entry for the Manager to // keep track of. -func (s *Manager) RegisterSubServer(name string) { +func (s *Manager) RegisterSubServer(name string, opts ...SubServerOption) { s.mu.RLock() defer s.mu.RUnlock() - s.subServers[name] = newSubServerStatus() + s.registerSubServerUnsafe(name, true, opts...) } // RegisterAndEnableSubServer will create a new sub-server entry for the // Manager to keep track of and will set it as enabled. -func (s *Manager) RegisterAndEnableSubServer(name string) { +func (s *Manager) RegisterAndEnableSubServer(name string, + opts ...SubServerOption) { + s.mu.RLock() defer s.mu.RUnlock() - ss := newSubServerStatus() - ss.Disabled = false + s.registerSubServerUnsafe(name, false, opts...) +} + +func (s *Manager) registerSubServerUnsafe(name string, disabled bool, + opts ...SubServerOption) { + + ss := newSubServerStatus(disabled, opts...) s.subServers[name] = ss } From 3b83d6e1022ce1ee5e99a929d43c88430dec1de6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Tigerstr=C3=B6m?= Date: Fri, 5 Jan 2024 02:04:50 +0100 Subject: [PATCH 02/11] status: format log row in SetErrored correctly --- status/manager.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/status/manager.go b/status/manager.go index 13a5fe1ac..ebda77431 100644 --- a/status/manager.go +++ b/status/manager.go @@ -188,14 +188,15 @@ func (s *Manager) SetErrored(name string, errStr string, s.mu.Lock() defer s.mu.Unlock() - log.Debugf("Setting the %s sub-server as errored: %s", name, errStr) + err := fmt.Sprintf(errStr, params...) + + log.Debugf("Setting the %s sub-server as errored: %s", name, err) ss, ok := s.subServers[name] if !ok { return } - err := fmt.Sprintf(errStr, params...) log.Errorf("could not start the %s sub-server: %s", name, err) ss.Running = false From 3d4c66cadad54689f4b10dcb8d4e2812f46707c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Tigerstr=C3=B6m?= Date: Tue, 12 Dec 2023 01:44:07 +0100 Subject: [PATCH 03/11] status: enable setting custom sub-server statuses --- status/manager.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/status/manager.go b/status/manager.go index ebda77431..926487152 100644 --- a/status/manager.go +++ b/status/manager.go @@ -23,6 +23,13 @@ type SubServerStatus struct { // been started. Running bool + // customStatus is a string that details a custom status of the + // sub-server, if the the sub-server is in a custom state. This status + // can be set to a unique status that only exists for the specific + // sub-server, and will be displayed to the user with the + // litrpc.SubServerStatus. + customStatus string + // Err will be a non-empty string if the sub-server failed to start. Err string } @@ -105,6 +112,20 @@ func (s *Manager) registerSubServerUnsafe(name string, disabled bool, s.subServers[name] = ss } +// SetCustomStatus updates the custom status of the given sub-server to the +// passed status. +func (s *Manager) SetCustomStatus(name, customStatus string) { + s.mu.Lock() + defer s.mu.Unlock() + + ss, ok := s.subServers[name] + if !ok { + return + } + + ss.customStatus = customStatus +} + // GetStatus returns the current status of a given sub-server. This will // silently fail if the referenced sub-server has not yet been registered. func (s *Manager) GetStatus(name string) (*SubServerStatus, error) { @@ -155,6 +176,7 @@ func (s *Manager) SetRunning(name string) { } ss.Running = true + ss.customStatus = "" } // SetStopped can be used to set the status of a sub-server as not Running and @@ -175,6 +197,7 @@ func (s *Manager) SetStopped(name string) { ss.Running = false ss.Err = "" + ss.customStatus = "" } // SetErrored can be used to set the status of a sub-server as not Running @@ -201,4 +224,5 @@ func (s *Manager) SetErrored(name string, errStr string, ss.Running = false ss.Err = err + ss.customStatus = "" } From 4a46e41f973234bf4e952c821332f7e24c6a3ce7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Tigerstr=C3=B6m?= Date: Mon, 11 Dec 2023 19:21:57 +0100 Subject: [PATCH 04/11] protos: add Subserver `custom_status` to protos --- app/src/types/generated/lit-status_pb.d.ts | 4 ++ app/src/types/generated/lit-status_pb.js | 29 ++++++++++++- app/src/util/tests/sampleData.ts | 6 +++ autopilotserverrpc/autopilotserver.pb.go | 2 +- litrpc/firewall.pb.go | 2 +- litrpc/lit-accounts.pb.go | 2 +- litrpc/lit-autopilot.pb.go | 2 +- litrpc/lit-sessions.pb.go | 2 +- litrpc/lit-status.pb.go | 49 ++++++++++++++-------- litrpc/lit-status.proto | 5 +++ litrpc/lit-status.swagger.json | 4 ++ litrpc/proxy.pb.go | 2 +- proto/lit-status.proto | 5 +++ status/manager.go | 7 ++-- 14 files changed, 93 insertions(+), 28 deletions(-) diff --git a/app/src/types/generated/lit-status_pb.d.ts b/app/src/types/generated/lit-status_pb.d.ts index 0cef648ef..ba026b13f 100644 --- a/app/src/types/generated/lit-status_pb.d.ts +++ b/app/src/types/generated/lit-status_pb.d.ts @@ -48,6 +48,9 @@ export class SubServerStatus extends jspb.Message { getError(): string; setError(value: string): void; + getCustomStatus(): string; + setCustomStatus(value: string): void; + serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): SubServerStatus.AsObject; static toObject(includeInstance: boolean, msg: SubServerStatus): SubServerStatus.AsObject; @@ -63,6 +66,7 @@ export namespace SubServerStatus { disabled: boolean, running: boolean, error: string, + customStatus: string, } } diff --git a/app/src/types/generated/lit-status_pb.js b/app/src/types/generated/lit-status_pb.js index c62099739..5ab4e7cd0 100644 --- a/app/src/types/generated/lit-status_pb.js +++ b/app/src/types/generated/lit-status_pb.js @@ -326,7 +326,8 @@ proto.litrpc.SubServerStatus.toObject = function(includeInstance, msg) { var f, obj = { disabled: jspb.Message.getFieldWithDefault(msg, 1, false), running: jspb.Message.getFieldWithDefault(msg, 2, false), - error: jspb.Message.getFieldWithDefault(msg, 3, "") + error: jspb.Message.getFieldWithDefault(msg, 3, ""), + customStatus: jspb.Message.getFieldWithDefault(msg, 4, "") }; if (includeInstance) { @@ -375,6 +376,10 @@ proto.litrpc.SubServerStatus.deserializeBinaryFromReader = function(msg, reader) var value = /** @type {string} */ (reader.readString()); msg.setError(value); break; + case 4: + var value = /** @type {string} */ (reader.readString()); + msg.setCustomStatus(value); + break; default: reader.skipField(); break; @@ -425,6 +430,13 @@ proto.litrpc.SubServerStatus.serializeBinaryToWriter = function(message, writer) f ); } + f = message.getCustomStatus(); + if (f.length > 0) { + writer.writeString( + 4, + f + ); + } }; @@ -477,4 +489,19 @@ proto.litrpc.SubServerStatus.prototype.setError = function(value) { }; +/** + * optional string custom_status = 4; + * @return {string} + */ +proto.litrpc.SubServerStatus.prototype.getCustomStatus = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, "")); +}; + + +/** @param {string} value */ +proto.litrpc.SubServerStatus.prototype.setCustomStatus = function(value) { + jspb.Message.setProto3StringField(this, 4, value); +}; + + goog.object.extend(exports, proto.litrpc); diff --git a/app/src/util/tests/sampleData.ts b/app/src/util/tests/sampleData.ts index 77a80ed0a..6497171fc 100644 --- a/app/src/util/tests/sampleData.ts +++ b/app/src/util/tests/sampleData.ts @@ -1071,6 +1071,7 @@ export const litSubServerStatus: STATUS.SubServerStatusResp.AsObject = { disabled: false, running: true, error: '', + customStatus: '', }, ], [ @@ -1079,6 +1080,7 @@ export const litSubServerStatus: STATUS.SubServerStatusResp.AsObject = { disabled: false, running: true, error: '', + customStatus: '', }, ], [ @@ -1087,6 +1089,7 @@ export const litSubServerStatus: STATUS.SubServerStatusResp.AsObject = { disabled: false, running: true, error: '', + customStatus: '', }, ], [ @@ -1095,6 +1098,7 @@ export const litSubServerStatus: STATUS.SubServerStatusResp.AsObject = { disabled: false, running: true, error: '', + customStatus: '', }, ], [ @@ -1103,6 +1107,7 @@ export const litSubServerStatus: STATUS.SubServerStatusResp.AsObject = { disabled: false, running: true, error: '', + customStatus: '', }, ], [ @@ -1111,6 +1116,7 @@ export const litSubServerStatus: STATUS.SubServerStatusResp.AsObject = { disabled: false, running: true, error: '', + customStatus: '', }, ], ], diff --git a/autopilotserverrpc/autopilotserver.pb.go b/autopilotserverrpc/autopilotserver.pb.go index cff40e070..5109d6d30 100644 --- a/autopilotserverrpc/autopilotserver.pb.go +++ b/autopilotserverrpc/autopilotserver.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v3.6.1 // source: autopilotserver.proto diff --git a/litrpc/firewall.pb.go b/litrpc/firewall.pb.go index 79e42dfb2..48d03112a 100644 --- a/litrpc/firewall.pb.go +++ b/litrpc/firewall.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v3.6.1 // source: firewall.proto diff --git a/litrpc/lit-accounts.pb.go b/litrpc/lit-accounts.pb.go index ff99e25fb..1df886ffe 100644 --- a/litrpc/lit-accounts.pb.go +++ b/litrpc/lit-accounts.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v3.6.1 // source: lit-accounts.proto diff --git a/litrpc/lit-autopilot.pb.go b/litrpc/lit-autopilot.pb.go index 733ca7883..f95717775 100644 --- a/litrpc/lit-autopilot.pb.go +++ b/litrpc/lit-autopilot.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v3.6.1 // source: lit-autopilot.proto diff --git a/litrpc/lit-sessions.pb.go b/litrpc/lit-sessions.pb.go index 68fb3ac3a..4073f70a6 100644 --- a/litrpc/lit-sessions.pb.go +++ b/litrpc/lit-sessions.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v3.6.1 // source: lit-sessions.proto diff --git a/litrpc/lit-status.pb.go b/litrpc/lit-status.pb.go index fee2cd92a..70fcfab4e 100644 --- a/litrpc/lit-status.pb.go +++ b/litrpc/lit-status.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v3.6.1 // source: lit-status.proto @@ -119,6 +119,10 @@ type SubServerStatus struct { // error describes an error that might have resulted in the sub-server not // starting up properly. Error string `protobuf:"bytes,3,opt,name=error,proto3" json:"error,omitempty"` + // custom_status details a custom state that the sub-server has entered, + // which is unique to the sub-server, and which is not the standard + // disabled, running or errored state. + CustomStatus string `protobuf:"bytes,4,opt,name=custom_status,json=customStatus,proto3" json:"custom_status,omitempty"` } func (x *SubServerStatus) Reset() { @@ -174,6 +178,13 @@ func (x *SubServerStatus) GetError() string { return "" } +func (x *SubServerStatus) GetCustomStatus() string { + if x != nil { + return x.CustomStatus + } + return "" +} + var File_lit_status_proto protoreflect.FileDescriptor var file_lit_status_proto_rawDesc = []byte{ @@ -191,23 +202,25 @@ var file_lit_status_proto_rawDesc = []byte{ 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6c, 0x69, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x5d, - 0x0a, 0x0f, 0x53, 0x75, 0x62, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x08, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x18, 0x0a, - 0x07, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, - 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x32, 0x54, 0x0a, - 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x4a, 0x0a, 0x0f, 0x53, 0x75, 0x62, 0x53, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x69, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x1b, 0x2e, 0x6c, 0x69, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x53, 0x75, 0x62, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x42, 0x34, 0x5a, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6c, 0x61, 0x62, 0x73, 0x2f, - 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x2d, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, - 0x61, 0x6c, 0x2f, 0x6c, 0x69, 0x74, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x74, 0x75, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x82, + 0x01, 0x0a, 0x0f, 0x53, 0x75, 0x62, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x18, + 0x0a, 0x07, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x07, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x23, + 0x0a, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x32, 0x54, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x4a, 0x0a, + 0x0f, 0x53, 0x75, 0x62, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x1a, 0x2e, 0x6c, 0x69, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x1b, 0x2e, 0x6c, + 0x69, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x42, 0x34, 0x5a, 0x32, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, + 0x67, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x2d, + 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x2f, 0x6c, 0x69, 0x74, 0x72, 0x70, 0x63, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/litrpc/lit-status.proto b/litrpc/lit-status.proto index c7ec55838..7fb0d9672 100644 --- a/litrpc/lit-status.proto +++ b/litrpc/lit-status.proto @@ -28,4 +28,9 @@ message SubServerStatus { // error describes an error that might have resulted in the sub-server not // starting up properly. string error = 3; + + // custom_status details a custom state that the sub-server has entered, + // which is unique to the sub-server, and which is not the standard + // disabled, running or errored state. + string custom_status = 4; } diff --git a/litrpc/lit-status.swagger.json b/litrpc/lit-status.swagger.json index 02c72af40..532fb220c 100644 --- a/litrpc/lit-status.swagger.json +++ b/litrpc/lit-status.swagger.json @@ -54,6 +54,10 @@ "error": { "type": "string", "description": "error describes an error that might have resulted in the sub-server not\nstarting up properly." + }, + "custom_status": { + "type": "string", + "description": "custom_status details a custom state that the sub-server has entered,\nwhich is unique to the sub-server, and which is not the standard\ndisabled, running or errored state." } } }, diff --git a/litrpc/proxy.pb.go b/litrpc/proxy.pb.go index 26a9cf0a5..b57321938 100644 --- a/litrpc/proxy.pb.go +++ b/litrpc/proxy.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v3.6.1 // source: proxy.proto diff --git a/proto/lit-status.proto b/proto/lit-status.proto index c7ec55838..7fb0d9672 100644 --- a/proto/lit-status.proto +++ b/proto/lit-status.proto @@ -28,4 +28,9 @@ message SubServerStatus { // error describes an error that might have resulted in the sub-server not // starting up properly. string error = 3; + + // custom_status details a custom state that the sub-server has entered, + // which is unique to the sub-server, and which is not the standard + // disabled, running or errored state. + string custom_status = 4; } diff --git a/status/manager.go b/status/manager.go index 926487152..bf1d76271 100644 --- a/status/manager.go +++ b/status/manager.go @@ -73,9 +73,10 @@ func (s *Manager) SubServerStatus(_ context.Context, resp := make(map[string]*litrpc.SubServerStatus, len(s.subServers)) for server, status := range s.subServers { resp[server] = &litrpc.SubServerStatus{ - Disabled: status.Disabled, - Running: status.Running, - Error: status.Err, + Disabled: status.Disabled, + Running: status.Running, + Error: status.Err, + CustomStatus: status.customStatus, } } From 3d4357287fc8cd4e8397acfea8d31e1c2fb234a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Tigerstr=C3=B6m?= Date: Tue, 12 Dec 2023 01:40:18 +0100 Subject: [PATCH 05/11] terminal: add a lnd `Wallet Ready` state --- terminal.go | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/terminal.go b/terminal.go index f0c1e9350..aeecfabac 100644 --- a/terminal.go +++ b/terminal.go @@ -66,6 +66,12 @@ const ( MainnetServer = "autopilot.lightning.finance:12010" TestnetServer = "test.autopilot.lightning.finance:12010" + // lndWalletReadyStatus is a custom status that will be used with the + // LND subserver. If the subserver is in this state then it will allow + // certain wallet calls through while denying other calls that require + // LND to be fully started. + lndWalletReadyStatus = "Wallet Ready" + defaultServerTimeout = 10 * time.Second defaultConnectTimeout = 15 * time.Second defaultStartupTimeout = 5 * time.Second @@ -548,10 +554,9 @@ func (g *LightningTerminal) start() error { err) } - // We can now set the status of LND as running. - // This is done _before_ we wait for the macaroon so that - // LND commands to create and unlock a wallet can be allowed. - g.statusMgr.SetRunning(subservers.LND) + // We now set a custom status for the LND sub-server to indicate that + // the wallet is ready. + g.statusMgr.SetCustomStatus(subservers.LND, lndWalletReadyStatus) // Now that we have started the main UI web server, show some useful // information to the user so they can access the web UI easily. @@ -619,6 +624,10 @@ func (g *LightningTerminal) start() error { return fmt.Errorf("could not start LND") } + // Mark that lnd is now completely running after connecting the + // lnd clients. + g.statusMgr.SetRunning(subservers.LND) + // If we're in integrated and stateless init mode, we won't create // macaroon files in any of the subserver daemons. createDefaultMacaroons := true From 0c6a84babffb681d1f56a90199d7ce039b993dcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Tigerstr=C3=B6m?= Date: Tue, 2 Jan 2024 18:40:13 +0100 Subject: [PATCH 06/11] status: add status manager isReadyOverride func isReadyOverride is a callback that, when set and only if `running` is not yet true, will be used to determine if a system is ready for a call. We will pass the request URI to this method along with the `manualStatus`. The first returned boolean is true if the system should be seen as ready and the second is true if the override does handle the given request. If it does not, then we will fall back to our normal is-ready check. --- status/manager.go | 54 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/status/manager.go b/status/manager.go index bf1d76271..9cf90e76a 100644 --- a/status/manager.go +++ b/status/manager.go @@ -13,6 +13,16 @@ import ( // values of a SubServerStatus's fields. type SubServerOption func(status *SubServerStatus) +// WithIsReadyOverride is a functional option that can be used to set a callback +// function that is used to check if a system is ready _iff_ the system running +// status is not yet true. The call-back will be passed the request URI along +// with any manual status that has been set for the subsystem. +func WithIsReadyOverride(fn func(string, string) (bool, bool)) SubServerOption { + return func(status *SubServerStatus) { + status.isReadyOverride = fn + } +} + // SubServerStatus represents the status of a sub-server. type SubServerStatus struct { // Disabled is true if the sub-server is available in the LiT bundle but @@ -32,6 +42,15 @@ type SubServerStatus struct { // Err will be a non-empty string if the sub-server failed to start. Err string + + // isReadyOverride is a call back that, when set and only if `running` + // is not yet true, will be used to determine if a system is ready for + // a call. We will pass the request URI to this method along with the + // `manualStatus`. The first returned boolean is true if the system + // should be seen as ready and the second is true if the override does + // handle the given request. If it does not, then we will fall back to + // our normal is-ready check. + isReadyOverride func(string, string) (bool, bool) } // newSubServerStatus constructs a new SubServerStatus. @@ -60,6 +79,41 @@ func NewStatusManager() *Manager { } } +// IsSystemReady shows if the given sub-server ready to handle the a request for +// the passed request URI. The first returned boolean is true if the system +// is ready to handle the request. The second returned boolean is true if the +// system has been disabled. +func (s *Manager) IsSystemReady(name, req string) (bool, bool, error) { + s.mu.RLock() + defer s.mu.RUnlock() + + server, ok := s.subServers[name] + if !ok { + return false, false, errors.New("a sub-server with " + + "name %s has not yet been registered") + } + + if server.Disabled { + return false, true, nil + } + + // If there is no override for this server or if the server is already + // running then we just return the 'running' status. + if server.isReadyOverride == nil || server.Running { + return server.Running, false, nil + } + + // Otherwise, we check the override to see if this request is handled + // by the override and if it is, then if the override permits this call. + isReady, handled := server.isReadyOverride(req, server.customStatus) + if handled { + return isReady, false, nil + } + + // Otherwise, we just return the running status. + return server.Running, false, nil +} + // SubServerStatus queries the current status of a given sub-server. // // NOTE: this is part of the litrpc.StatusServer interface. From e4c7275ea7f1e772c102c6446f8aaeca6bf9f951 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Tigerstr=C3=B6m?= Date: Tue, 12 Dec 2023 01:35:00 +0100 Subject: [PATCH 07/11] multi: use request overrides in RPC proxy --- rpc_proxy.go | 9 ++++----- status/manager.go | 15 --------------- 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/rpc_proxy.go b/rpc_proxy.go index f1be75934..3bd20cf19 100644 --- a/rpc_proxy.go +++ b/rpc_proxy.go @@ -638,18 +638,17 @@ func (p *rpcProxy) checkSubSystemStarted(requestURI string) error { // Check with the status manger to see if the sub-server is ready to // handle the request. - serverStatus, err := p.statusMgr.GetStatus(system) + ready, disabled, err := p.statusMgr.IsSystemReady(system, requestURI) if err != nil { return err } - if serverStatus.Disabled { + if disabled { return fmt.Errorf("%s has been disabled", system) } - if !serverStatus.Running { - return fmt.Errorf("%s is not running: %s", system, - serverStatus.Err) + if !ready { + return fmt.Errorf("%s is not ready for: %s", system, requestURI) } return nil diff --git a/status/manager.go b/status/manager.go index 9cf90e76a..c1494f973 100644 --- a/status/manager.go +++ b/status/manager.go @@ -181,21 +181,6 @@ func (s *Manager) SetCustomStatus(name, customStatus string) { ss.customStatus = customStatus } -// GetStatus returns the current status of a given sub-server. This will -// silently fail if the referenced sub-server has not yet been registered. -func (s *Manager) GetStatus(name string) (*SubServerStatus, error) { - s.mu.RLock() - defer s.mu.RUnlock() - - status, ok := s.subServers[name] - if !ok { - return nil, errors.New("a sub-server with name %s has not " + - "yet been registered") - } - - return status, nil -} - // SetEnabled marks the sub-server with the given name as enabled. // // NOTE: This will silently fail if the referenced sub-server has not yet been From b0dbbe93c291bc9d59313bdecf4ddc3ac0becf58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Tigerstr=C3=B6m?= Date: Tue, 12 Dec 2023 01:42:27 +0100 Subject: [PATCH 08/11] terminal: allow lnd `GetState` RPC on Wallet Ready --- terminal.go | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/terminal.go b/terminal.go index aeecfabac..0ffab87eb 100644 --- a/terminal.go +++ b/terminal.go @@ -238,8 +238,24 @@ func (g *LightningTerminal) Run() error { return fmt.Errorf("could not create permissions manager") } + // The litcli status command will call the "/lnrpc.State/GetState" RPC. + // As the status command is available to the user before the macaroons + // have been loaded/created, and before the lnd clients have been + // set up, we need to override the isReady check for this specific + // URI as soon as LND can accept the call, i.e. when the lnd sub-server + // is in the "Wallet Ready" state. + lndOverride := func(uri, manualStatus string) (bool, bool) { + if uri != "/lnrpc.State/GetState" { + return false, false + } + + return manualStatus == lndWalletReadyStatus, true + } + // Register LND, LiT and Accounts with the status manager. - g.statusMgr.RegisterAndEnableSubServer(subservers.LND) + g.statusMgr.RegisterAndEnableSubServer( + subservers.LND, status.WithIsReadyOverride(lndOverride), + ) g.statusMgr.RegisterAndEnableSubServer(subservers.LIT) g.statusMgr.RegisterSubServer(subservers.ACCOUNTS) @@ -556,6 +572,9 @@ func (g *LightningTerminal) start() error { // We now set a custom status for the LND sub-server to indicate that // the wallet is ready. + // This is done _before_ we have set up the lnd clients so that the + // litcli status command won't error before the lnd sub-server has + // been marked as running. g.statusMgr.SetCustomStatus(subservers.LND, lndWalletReadyStatus) // Now that we have started the main UI web server, show some useful From 33646b45984a4dd401f3edde6f5ca0e3f89fa539 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Tigerstr=C3=B6m?= Date: Tue, 2 Jan 2024 19:06:57 +0100 Subject: [PATCH 09/11] status: remove err when marking sub-server as running Ensure that the sub-server error string is set to an empty string when marking a sub-server as running. This will be important for the next commit, which will retry to start the lnd sub-server even if we error when starting the lnd-client(s), and therefore may mark the lnd sub-server as running even after we've errored when starting the lnd-client(s). --- status/manager.go | 1 + 1 file changed, 1 insertion(+) diff --git a/status/manager.go b/status/manager.go index c1494f973..5c8e55565 100644 --- a/status/manager.go +++ b/status/manager.go @@ -216,6 +216,7 @@ func (s *Manager) SetRunning(name string) { } ss.Running = true + ss.Err = "" ss.customStatus = "" } From 33d6f6e4a3510a7d613f759a1ff60795c63c4df0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Tigerstr=C3=B6m?= Date: Wed, 6 Dec 2023 23:11:04 +0100 Subject: [PATCH 10/11] terminal: retry to create lnd client on failure --- terminal.go | 104 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 76 insertions(+), 28 deletions(-) diff --git a/terminal.go b/terminal.go index 0ffab87eb..3d0bc964d 100644 --- a/terminal.go +++ b/terminal.go @@ -48,7 +48,6 @@ import ( "github.com/lightningnetwork/lnd/lnrpc/walletrpc" "github.com/lightningnetwork/lnd/lnrpc/watchtowerrpc" "github.com/lightningnetwork/lnd/lnrpc/wtclientrpc" - "github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lnwallet/btcwallet" "github.com/lightningnetwork/lnd/macaroons" "github.com/lightningnetwork/lnd/rpcperms" @@ -634,7 +633,7 @@ func (g *LightningTerminal) start() error { } // Set up all the LND clients required by LiT. - err = g.setUpLNDClients() + err = g.setUpLNDClients(lndQuit) if err != nil { g.statusMgr.SetErrored( subservers.LND, "could not set up LND clients: %v", err, @@ -699,8 +698,9 @@ func (g *LightningTerminal) start() error { } // setUpLNDClients sets up the various LND clients required by LiT. -func (g *LightningTerminal) setUpLNDClients() error { +func (g *LightningTerminal) setUpLNDClients(lndQuit chan struct{}) error { var ( + err error insecure bool clientOptions []lndclient.BasicClientOption ) @@ -722,25 +722,57 @@ func (g *LightningTerminal) setUpLNDClients() error { clientOptions = append(clientOptions, lndclient.Insecure()) } + // checkRunning checks if we should continue running for the duration of + // the defaultStartupTimeout, or else returns an error indicating why + // a shut-down is needed. + checkRunning := func() error { + select { + case err := <-g.errQueue.ChanOut(): + return fmt.Errorf("error from subsystem: %v", err) + + case <-lndQuit: + return fmt.Errorf("LND has stopped") + + case <-interceptor.ShutdownChannel(): + return fmt.Errorf("received the shutdown signal") + + case <-time.After(defaultStartupTimeout): + return nil + } + } + // The main RPC listener of lnd might need some time to start, it could // be that we run into a connection refused a few times. We use the // basic client connection to find out if the RPC server is started yet // because that doesn't do anything else than just connect. We'll check // if lnd is also ready to be used in the next step. log.Infof("Connecting basic lnd client") - err := wait.NoError(func() error { + + for { // Create an lnd client now that we have the full configuration. // We'll need a basic client and a full client because not all // subservers have the same requirements. - var err error g.basicClient, err = lndclient.NewBasicClient( - host, tlsPath, filepath.Dir(macPath), string(network), - clientOptions..., + host, tlsPath, filepath.Dir(macPath), + string(network), clientOptions..., ) - return err - }, defaultStartupTimeout) - if err != nil { - return fmt.Errorf("could not create basic LND Client: %v", err) + if err == nil { + log.Infof("Basic lnd client connected") + + break + } + + g.statusMgr.SetErrored( + subservers.LIT, + "Error when setting up basic LND Client: %v", err, + ) + + err = checkRunning() + if err != nil { + return err + } + + log.Infof("Retrying to connect basic lnd client") } // Now we know that the connection itself is ready. But we also need to @@ -768,23 +800,39 @@ func (g *LightningTerminal) setUpLNDClients() error { }() log.Infof("Connecting full lnd client") - g.lndClient, err = lndclient.NewLndServices( - &lndclient.LndServicesConfig{ - LndAddress: host, - Network: network, - TLSPath: tlsPath, - Insecure: insecure, - CustomMacaroonPath: macPath, - CustomMacaroonHex: hex.EncodeToString(macData), - BlockUntilChainSynced: true, - BlockUntilUnlocked: true, - CallerCtx: ctxc, - CheckVersion: minimalCompatibleVersion, - }, - ) - if err != nil { - return fmt.Errorf("could not create LND Services client: %v", - err) + for { + g.lndClient, err = lndclient.NewLndServices( + &lndclient.LndServicesConfig{ + LndAddress: host, + Network: network, + TLSPath: tlsPath, + Insecure: insecure, + CustomMacaroonPath: macPath, + CustomMacaroonHex: hex.EncodeToString(macData), + BlockUntilChainSynced: true, + BlockUntilUnlocked: true, + CallerCtx: ctxc, + CheckVersion: minimalCompatibleVersion, + }, + ) + if err == nil { + log.Infof("Full lnd client connected") + + break + } + + g.statusMgr.SetErrored( + subservers.LIT, + "Error when creating LND Services client: %v", + err, + ) + + err = checkRunning() + if err != nil { + return err + } + + log.Infof("Retrying to create LND Services client") } // Pass LND's build tags to the permission manager so that it can From fcd22a74f197957ea3b06d47d7626ee1cc77ffb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Tigerstr=C3=B6m?= Date: Thu, 14 Dec 2023 01:12:42 +0100 Subject: [PATCH 11/11] status: unexport SubServerStatus & rename to subServer --- status/manager.go | 66 +++++++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/status/manager.go b/status/manager.go index 5c8e55565..cb2f2befc 100644 --- a/status/manager.go +++ b/status/manager.go @@ -10,28 +10,28 @@ import ( ) // SubServerOption defines a functional option that can be used to modify the -// values of a SubServerStatus's fields. -type SubServerOption func(status *SubServerStatus) +// values of a subServer's fields. +type SubServerOption func(status *subServer) // WithIsReadyOverride is a functional option that can be used to set a callback // function that is used to check if a system is ready _iff_ the system running // status is not yet true. The call-back will be passed the request URI along // with any manual status that has been set for the subsystem. func WithIsReadyOverride(fn func(string, string) (bool, bool)) SubServerOption { - return func(status *SubServerStatus) { + return func(status *subServer) { status.isReadyOverride = fn } } -// SubServerStatus represents the status of a sub-server. -type SubServerStatus struct { - // Disabled is true if the sub-server is available in the LiT bundle but +// subServer represents the status of a sub-server. +type subServer struct { + // disabled is true if the sub-server is available in the LiT bundle but // has explicitly been disabled by the user. - Disabled bool + disabled bool - // Running is true if the sub-server is enabled and has successfully + // running is true if the sub-server is enabled and has successfully // been started. - Running bool + running bool // customStatus is a string that details a custom status of the // sub-server, if the the sub-server is in a custom state. This status @@ -40,8 +40,8 @@ type SubServerStatus struct { // litrpc.SubServerStatus. customStatus string - // Err will be a non-empty string if the sub-server failed to start. - Err string + // err will be a non-empty string if the sub-server failed to start. + err string // isReadyOverride is a call back that, when set and only if `running` // is not yet true, will be used to determine if a system is ready for @@ -53,12 +53,10 @@ type SubServerStatus struct { isReadyOverride func(string, string) (bool, bool) } -// newSubServerStatus constructs a new SubServerStatus. -func newSubServerStatus(disabled bool, - opts ...SubServerOption) *SubServerStatus { - - return &SubServerStatus{ - Disabled: disabled, +// newSubServer constructs a new subServer. +func newSubServer(disabled bool, opts ...SubServerOption) *subServer { + return &subServer{ + disabled: disabled, } } @@ -68,14 +66,14 @@ func newSubServerStatus(disabled bool, type Manager struct { litrpc.UnimplementedStatusServer - subServers map[string]*SubServerStatus + subServers map[string]*subServer mu sync.RWMutex } // NewStatusManager constructs a new Manager. func NewStatusManager() *Manager { return &Manager{ - subServers: make(map[string]*SubServerStatus), + subServers: make(map[string]*subServer), } } @@ -93,14 +91,14 @@ func (s *Manager) IsSystemReady(name, req string) (bool, bool, error) { "name %s has not yet been registered") } - if server.Disabled { + if server.disabled { return false, true, nil } // If there is no override for this server or if the server is already // running then we just return the 'running' status. - if server.isReadyOverride == nil || server.Running { - return server.Running, false, nil + if server.isReadyOverride == nil || server.running { + return server.running, false, nil } // Otherwise, we check the override to see if this request is handled @@ -111,7 +109,7 @@ func (s *Manager) IsSystemReady(name, req string) (bool, bool, error) { } // Otherwise, we just return the running status. - return server.Running, false, nil + return server.running, false, nil } // SubServerStatus queries the current status of a given sub-server. @@ -127,9 +125,9 @@ func (s *Manager) SubServerStatus(_ context.Context, resp := make(map[string]*litrpc.SubServerStatus, len(s.subServers)) for server, status := range s.subServers { resp[server] = &litrpc.SubServerStatus{ - Disabled: status.Disabled, - Running: status.Running, - Error: status.Err, + Disabled: status.disabled, + Running: status.running, + Error: status.err, CustomStatus: status.customStatus, } } @@ -162,7 +160,7 @@ func (s *Manager) RegisterAndEnableSubServer(name string, func (s *Manager) registerSubServerUnsafe(name string, disabled bool, opts ...SubServerOption) { - ss := newSubServerStatus(disabled, opts...) + ss := newSubServer(disabled, opts...) s.subServers[name] = ss } @@ -196,7 +194,7 @@ func (s *Manager) SetEnabled(name string) { return } - ss.Disabled = false + ss.disabled = false } // SetRunning can be used to set the status of a sub-server as Running @@ -215,8 +213,8 @@ func (s *Manager) SetRunning(name string) { return } - ss.Running = true - ss.Err = "" + ss.running = true + ss.err = "" ss.customStatus = "" } @@ -236,8 +234,8 @@ func (s *Manager) SetStopped(name string) { return } - ss.Running = false - ss.Err = "" + ss.running = false + ss.err = "" ss.customStatus = "" } @@ -263,7 +261,7 @@ func (s *Manager) SetErrored(name string, errStr string, log.Errorf("could not start the %s sub-server: %s", name, err) - ss.Running = false - ss.Err = err + ss.running = false + ss.err = err ss.customStatus = "" }