diff --git a/client/cmd/state.go b/client/cmd/state.go new file mode 100644 index 00000000000..21a5508f4bd --- /dev/null +++ b/client/cmd/state.go @@ -0,0 +1,181 @@ +package cmd + +import ( + "fmt" + + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "google.golang.org/grpc/status" + + "github.com/netbirdio/netbird/client/proto" +) + +var ( + allFlag bool +) + +var stateCmd = &cobra.Command{ + Use: "state", + Short: "Manage daemon state", + Long: "Provides commands for managing and inspecting the Netbird daemon state.", +} + +var stateListCmd = &cobra.Command{ + Use: "list", + Aliases: []string{"ls"}, + Short: "List all stored states", + Long: "Lists all registered states with their status and basic information.", + Example: " netbird state list", + RunE: stateList, +} + +var stateCleanCmd = &cobra.Command{ + Use: "clean [state-name]", + Short: "Clean stored states", + Long: `Clean specific state or all states. The daemon must not be running. +This will perform cleanup operations and remove the state.`, + Example: ` netbird state clean dns_state + netbird state clean --all`, + RunE: stateClean, + PreRunE: func(cmd *cobra.Command, args []string) error { + // Check mutual exclusivity between --all flag and state-name argument + if allFlag && len(args) > 0 { + return fmt.Errorf("cannot specify both --all flag and state name") + } + if !allFlag && len(args) != 1 { + return fmt.Errorf("requires a state name argument or --all flag") + } + return nil + }, +} + +var stateDeleteCmd = &cobra.Command{ + Use: "delete [state-name]", + Short: "Delete stored states", + Long: `Delete specific state or all states from storage. The daemon must not be running. +This will remove the state without performing any cleanup operations.`, + Example: ` netbird state delete dns_state + netbird state delete --all`, + RunE: stateDelete, + PreRunE: func(cmd *cobra.Command, args []string) error { + // Check mutual exclusivity between --all flag and state-name argument + if allFlag && len(args) > 0 { + return fmt.Errorf("cannot specify both --all flag and state name") + } + if !allFlag && len(args) != 1 { + return fmt.Errorf("requires a state name argument or --all flag") + } + return nil + }, +} + +func init() { + rootCmd.AddCommand(stateCmd) + stateCmd.AddCommand(stateListCmd, stateCleanCmd, stateDeleteCmd) + + stateCleanCmd.Flags().BoolVarP(&allFlag, "all", "a", false, "Clean all states") + stateDeleteCmd.Flags().BoolVarP(&allFlag, "all", "a", false, "Delete all states") +} + +func stateList(cmd *cobra.Command, _ []string) error { + conn, err := getClient(cmd) + if err != nil { + return err + } + defer func() { + if err := conn.Close(); err != nil { + log.Errorf(errCloseConnection, err) + } + }() + + client := proto.NewDaemonServiceClient(conn) + resp, err := client.ListStates(cmd.Context(), &proto.ListStatesRequest{}) + if err != nil { + return fmt.Errorf("failed to list states: %v", status.Convert(err).Message()) + } + + cmd.Printf("\nStored states:\n\n") + for _, state := range resp.States { + cmd.Printf("- %s\n", state.Name) + } + + return nil +} + +func stateClean(cmd *cobra.Command, args []string) error { + var stateName string + if !allFlag { + stateName = args[0] + } + + conn, err := getClient(cmd) + if err != nil { + return err + } + defer func() { + if err := conn.Close(); err != nil { + log.Errorf(errCloseConnection, err) + } + }() + + client := proto.NewDaemonServiceClient(conn) + resp, err := client.CleanState(cmd.Context(), &proto.CleanStateRequest{ + StateName: stateName, + All: allFlag, + }) + if err != nil { + return fmt.Errorf("failed to clean state: %v", status.Convert(err).Message()) + } + + if resp.CleanedStates == 0 { + cmd.Println("No states were cleaned") + return nil + } + + if allFlag { + cmd.Printf("Successfully cleaned %d states\n", resp.CleanedStates) + } else { + cmd.Printf("Successfully cleaned state %q\n", stateName) + } + + return nil +} + +func stateDelete(cmd *cobra.Command, args []string) error { + var stateName string + if !allFlag { + stateName = args[0] + } + + conn, err := getClient(cmd) + if err != nil { + return err + } + defer func() { + if err := conn.Close(); err != nil { + log.Errorf(errCloseConnection, err) + } + }() + + client := proto.NewDaemonServiceClient(conn) + resp, err := client.DeleteState(cmd.Context(), &proto.DeleteStateRequest{ + StateName: stateName, + All: allFlag, + }) + if err != nil { + return fmt.Errorf("failed to delete state: %v", status.Convert(err).Message()) + } + + if resp.DeletedStates == 0 { + cmd.Println("No states were deleted") + return nil + } + + if allFlag { + cmd.Printf("Successfully deleted %d states\n", resp.DeletedStates) + } else { + cmd.Printf("Successfully deleted state %q\n", stateName) + } + + return nil +} diff --git a/client/internal/connect.go b/client/internal/connect.go index bcc9d17a3f6..914b120f623 100644 --- a/client/internal/connect.go +++ b/client/internal/connect.go @@ -335,6 +335,19 @@ func (c *ConnectClient) Engine() *Engine { return e } +// Status returns the current client status +func (c *ConnectClient) Status() StatusType { + if c == nil { + return StatusIdle + } + status, err := CtxGetState(c.ctx).Status() + if err != nil { + return StatusIdle + } + + return status +} + func (c *ConnectClient) Stop() error { if c == nil { return nil diff --git a/client/internal/statemanager/manager.go b/client/internal/statemanager/manager.go index 448ff899204..036f2c5fd8b 100644 --- a/client/internal/statemanager/manager.go +++ b/client/internal/statemanager/manager.go @@ -18,6 +18,11 @@ import ( nberrors "github.com/netbirdio/netbird/client/errors" ) +const ( + errStateNotRegistered = "state %s not registered" + errLoadStateFile = "load state file: %w" +) + // State interface defines the methods that all state types must implement type State interface { Name() string @@ -158,7 +163,7 @@ func (m *Manager) setState(name string, state State) error { defer m.mu.Unlock() if _, exists := m.states[name]; !exists { - return fmt.Errorf("state %s not registered", name) + return fmt.Errorf(errStateNotRegistered, name) } m.states[name] = state @@ -167,6 +172,63 @@ func (m *Manager) setState(name string, state State) error { return nil } +// DeleteStateByName handles deletion of states without cleanup. +// It doesn't require the state to be registered. +func (m *Manager) DeleteStateByName(stateName string) error { + if m == nil { + return nil + } + + m.mu.Lock() + defer m.mu.Unlock() + + rawStates, err := m.loadStateFile(false) + if err != nil { + return fmt.Errorf(errLoadStateFile, err) + } + if rawStates == nil { + return nil + } + + if _, exists := rawStates[stateName]; !exists { + return fmt.Errorf("state %s not found", stateName) + } + + // Mark state as deleted by setting it to nil and marking it dirty + m.states[stateName] = nil + m.dirty[stateName] = struct{}{} + + return nil +} + +// DeleteAllStates removes all states. +func (m *Manager) DeleteAllStates() (int, error) { + if m == nil { + return 0, nil + } + + m.mu.Lock() + defer m.mu.Unlock() + + rawStates, err := m.loadStateFile(false) + if err != nil { + return 0, fmt.Errorf(errLoadStateFile, err) + } + if rawStates == nil { + return 0, nil + } + + count := len(rawStates) + + // Mark all states as deleted and dirty + for name := range rawStates { + m.states[name] = nil + m.dirty[name] = struct{}{} + } + + return count, nil +} + func (m *Manager) periodicStateSave(ctx context.Context) { ticker := time.NewTicker(10 * time.Second) defer ticker.Stop() @@ -227,7 +289,7 @@ func (m *Manager) PersistState(ctx context.Context) error { } } - log.Debugf("persisted shutdown states: %v", maps.Keys(m.dirty)) + log.Debugf("persisted states: %v", maps.Keys(m.dirty)) clear(m.dirty) @@ -235,7 +297,7 @@ func (m *Manager) PersistState(ctx context.Context) error { } // loadStateFile reads and unmarshals the state file into a map of raw JSON messages -func (m *Manager) loadStateFile() (map[string]json.RawMessage, error) { +func (m *Manager) loadStateFile(deleteCorrupt bool) (map[string]json.RawMessage, error) { data, err := os.ReadFile(m.filePath) if err != nil { if errors.Is(err, fs.ErrNotExist) { @@ -247,11 +309,13 @@ func (m *Manager) loadStateFile() (map[string]json.RawMessage, error) { var rawStates map[string]json.RawMessage if err := json.Unmarshal(data, &rawStates); err != nil { - log.Warn("State file appears to be corrupted, attempting to delete it") - if err := os.Remove(m.filePath); err != nil { - log.Errorf("Failed to delete corrupted state file: %v", err) - } else { - log.Info("State file deleted") + if deleteCorrupt { + log.Warn("State file appears to be corrupted, attempting to delete it", err) + if err := os.Remove(m.filePath); err != nil { + log.Errorf("Failed to delete corrupted state file: %v", err) + } else { + log.Info("State file deleted") + } } return nil, fmt.Errorf("unmarshal states: %w", err) } @@ -263,7 +327,7 @@ func (m *Manager) loadStateFile() (map[string]json.RawMessage, error) { func (m *Manager) loadSingleRawState(name string, rawState json.RawMessage) (State, error) { stateType, ok := m.stateTypes[name] if !ok { - return nil, fmt.Errorf("state %s not registered", name) + return nil, fmt.Errorf(errStateNotRegistered, name) } if string(rawState) == "null" { @@ -287,7 +351,7 @@ func (m *Manager) LoadState(state State) error { m.mu.Lock() defer m.mu.Unlock() - rawStates, err := m.loadStateFile() + rawStates, err := m.loadStateFile(false) if err != nil { return err } @@ -314,6 +378,84 @@ func (m *Manager) LoadState(state State) error { return nil } +// cleanupSingleState handles the cleanup of a specific state and returns any error. +// The caller must hold the mutex. +func (m *Manager) cleanupSingleState(name string, rawState json.RawMessage) error { + // For unregistered states, preserve the raw JSON + if _, registered := m.stateTypes[name]; !registered { + m.states[name] = &RawState{data: rawState} + return nil + } + + // Load the state + loadedState, err := m.loadSingleRawState(name, rawState) + if err != nil { + return err + } + + if loadedState == nil { + return nil + } + + // Check if state supports cleanup + cleanableState, isCleanable := loadedState.(CleanableState) + if !isCleanable { + // If it doesn't support cleanup, keep it as-is + m.states[name] = loadedState + return nil + } + + // Perform cleanup + log.Infof("cleaning up state %s", name) + if err := cleanableState.Cleanup(); err != nil { + // On cleanup error, preserve the state + m.states[name] = loadedState + return fmt.Errorf("cleanup state: %w", err) + } + + // Successfully cleaned up - mark for deletion + m.states[name] = nil + m.dirty[name] = struct{}{} + return nil +} + +// CleanupStateByName loads and cleans up a specific state by name if it implements CleanableState. +// Returns an error if the state doesn't exist, isn't registered, or cleanup fails. +func (m *Manager) CleanupStateByName(name string) error { + if m == nil { + return nil + } + + m.mu.Lock() + defer m.mu.Unlock() + + // Check if state is registered + if _, registered := m.stateTypes[name]; !registered { + return fmt.Errorf(errStateNotRegistered, name) + } + + // Load raw states from file + rawStates, err := m.loadStateFile(false) + if err != nil { + return err + } + if rawStates == nil { + return nil + } + + // Check if state exists in file + rawState, exists := rawStates[name] + if !exists { + return nil + } + + if err := m.cleanupSingleState(name, rawState); err != nil { + return fmt.Errorf("%s: %w", name, err) + } + + return nil +} + // PerformCleanup retrieves all states from the state file and calls Cleanup on registered states that support it. // Unregistered states are preserved in their original state. func (m *Manager) PerformCleanup() error { @@ -325,10 +467,9 @@ func (m *Manager) PerformCleanup() error { defer m.mu.Unlock() // Load raw states from file - rawStates, err := m.loadStateFile() + rawStates, err := m.loadStateFile(true) if err != nil { - log.Warnf("Failed to load state during cleanup: %v", err) - return err + return fmt.Errorf(errLoadStateFile, err) } if rawStates == nil { return nil @@ -338,43 +479,34 @@ func (m *Manager) PerformCleanup() error { // Process each state in the file for name, rawState := range rawStates { - // For unregistered states, preserve the raw JSON - if _, registered := m.stateTypes[name]; !registered { - m.states[name] = &RawState{data: rawState} - continue + if err := m.cleanupSingleState(name, rawState); err != nil { + merr = multierror.Append(merr, fmt.Errorf("%s: %w", name, err)) } + } - // Load the registered state - loadedState, err := m.loadSingleRawState(name, rawState) - if err != nil { - merr = multierror.Append(merr, err) - continue - } + return nberrors.FormatErrorOrNil(merr) +} - if loadedState == nil { - continue - } +// GetSavedStateNames returns all state names that are currently saved in the state file. +func (m *Manager) GetSavedStateNames() ([]string, error) { + if m == nil { + return nil, nil + } - // Check if state supports cleanup - cleanableState, isCleanable := loadedState.(CleanableState) - if !isCleanable { - // If it doesn't support cleanup, keep it as-is - m.states[name] = loadedState - continue - } + rawStates, err := m.loadStateFile(false) + if err != nil { + return nil, fmt.Errorf(errLoadStateFile, err) + } + if rawStates == nil { + return nil, nil + } - // Perform cleanup for cleanable states - log.Infof("client was not shut down properly, cleaning up %s", name) - if err := cleanableState.Cleanup(); err != nil { - merr = multierror.Append(merr, fmt.Errorf("cleanup state for %s: %w", name, err)) - // On cleanup error, preserve the state - m.states[name] = loadedState - } else { - // Successfully cleaned up - mark for deletion - m.states[name] = nil - m.dirty[name] = struct{}{} + var states []string + for name, state := range rawStates { + if len(state) != 0 && string(state) != "null" { + states = append(states, name) } } - return nberrors.FormatErrorOrNil(merr) + return states, nil } diff --git a/client/proto/daemon.pb.go b/client/proto/daemon.pb.go index b942d8b6e0a..d67fb0bcc4a 100644 --- a/client/proto/daemon.pb.go +++ b/client/proto/daemon.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.26.0 -// protoc v3.21.12 +// protoc v4.23.4 // source: daemon.proto package proto @@ -2103,6 +2103,349 @@ func (*SetLogLevelResponse) Descriptor() ([]byte, []int) { return file_daemon_proto_rawDescGZIP(), []int{30} } +// State represents a daemon state entry +type State struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *State) Reset() { + *x = State{} + if protoimpl.UnsafeEnabled { + mi := &file_daemon_proto_msgTypes[31] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *State) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*State) ProtoMessage() {} + +func (x *State) ProtoReflect() protoreflect.Message { + mi := &file_daemon_proto_msgTypes[31] + 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 State.ProtoReflect.Descriptor instead. +func (*State) Descriptor() ([]byte, []int) { + return file_daemon_proto_rawDescGZIP(), []int{31} +} + +func (x *State) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +// ListStatesRequest is empty as it requires no parameters +type ListStatesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ListStatesRequest) Reset() { + *x = ListStatesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_daemon_proto_msgTypes[32] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListStatesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListStatesRequest) ProtoMessage() {} + +func (x *ListStatesRequest) ProtoReflect() protoreflect.Message { + mi := &file_daemon_proto_msgTypes[32] + 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 ListStatesRequest.ProtoReflect.Descriptor instead. +func (*ListStatesRequest) Descriptor() ([]byte, []int) { + return file_daemon_proto_rawDescGZIP(), []int{32} +} + +// ListStatesResponse contains a list of states +type ListStatesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + States []*State `protobuf:"bytes,1,rep,name=states,proto3" json:"states,omitempty"` +} + +func (x *ListStatesResponse) Reset() { + *x = ListStatesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_daemon_proto_msgTypes[33] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListStatesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListStatesResponse) ProtoMessage() {} + +func (x *ListStatesResponse) ProtoReflect() protoreflect.Message { + mi := &file_daemon_proto_msgTypes[33] + 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 ListStatesResponse.ProtoReflect.Descriptor instead. +func (*ListStatesResponse) Descriptor() ([]byte, []int) { + return file_daemon_proto_rawDescGZIP(), []int{33} +} + +func (x *ListStatesResponse) GetStates() []*State { + if x != nil { + return x.States + } + return nil +} + +// CleanStateRequest for cleaning states +type CleanStateRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + StateName string `protobuf:"bytes,1,opt,name=state_name,json=stateName,proto3" json:"state_name,omitempty"` + All bool `protobuf:"varint,2,opt,name=all,proto3" json:"all,omitempty"` +} + +func (x *CleanStateRequest) Reset() { + *x = CleanStateRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_daemon_proto_msgTypes[34] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CleanStateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CleanStateRequest) ProtoMessage() {} + +func (x *CleanStateRequest) ProtoReflect() protoreflect.Message { + mi := &file_daemon_proto_msgTypes[34] + 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 CleanStateRequest.ProtoReflect.Descriptor instead. +func (*CleanStateRequest) Descriptor() ([]byte, []int) { + return file_daemon_proto_rawDescGZIP(), []int{34} +} + +func (x *CleanStateRequest) GetStateName() string { + if x != nil { + return x.StateName + } + return "" +} + +func (x *CleanStateRequest) GetAll() bool { + if x != nil { + return x.All + } + return false +} + +// CleanStateResponse contains the result of the clean operation +type CleanStateResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + CleanedStates int32 `protobuf:"varint,1,opt,name=cleaned_states,json=cleanedStates,proto3" json:"cleaned_states,omitempty"` +} + +func (x *CleanStateResponse) Reset() { + *x = CleanStateResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_daemon_proto_msgTypes[35] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CleanStateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CleanStateResponse) ProtoMessage() {} + +func (x *CleanStateResponse) ProtoReflect() protoreflect.Message { + mi := &file_daemon_proto_msgTypes[35] + 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 CleanStateResponse.ProtoReflect.Descriptor instead. +func (*CleanStateResponse) Descriptor() ([]byte, []int) { + return file_daemon_proto_rawDescGZIP(), []int{35} +} + +func (x *CleanStateResponse) GetCleanedStates() int32 { + if x != nil { + return x.CleanedStates + } + return 0 +} + +// DeleteStateRequest for deleting states +type DeleteStateRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + StateName string `protobuf:"bytes,1,opt,name=state_name,json=stateName,proto3" json:"state_name,omitempty"` + All bool `protobuf:"varint,2,opt,name=all,proto3" json:"all,omitempty"` +} + +func (x *DeleteStateRequest) Reset() { + *x = DeleteStateRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_daemon_proto_msgTypes[36] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteStateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteStateRequest) ProtoMessage() {} + +func (x *DeleteStateRequest) ProtoReflect() protoreflect.Message { + mi := &file_daemon_proto_msgTypes[36] + 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 DeleteStateRequest.ProtoReflect.Descriptor instead. +func (*DeleteStateRequest) Descriptor() ([]byte, []int) { + return file_daemon_proto_rawDescGZIP(), []int{36} +} + +func (x *DeleteStateRequest) GetStateName() string { + if x != nil { + return x.StateName + } + return "" +} + +func (x *DeleteStateRequest) GetAll() bool { + if x != nil { + return x.All + } + return false +} + +// DeleteStateResponse contains the result of the delete operation +type DeleteStateResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + DeletedStates int32 `protobuf:"varint,1,opt,name=deleted_states,json=deletedStates,proto3" json:"deleted_states,omitempty"` +} + +func (x *DeleteStateResponse) Reset() { + *x = DeleteStateResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_daemon_proto_msgTypes[37] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteStateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteStateResponse) ProtoMessage() {} + +func (x *DeleteStateResponse) ProtoReflect() protoreflect.Message { + mi := &file_daemon_proto_msgTypes[37] + 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 DeleteStateResponse.ProtoReflect.Descriptor instead. +func (*DeleteStateResponse) Descriptor() ([]byte, []int) { + return file_daemon_proto_rawDescGZIP(), []int{37} +} + +func (x *DeleteStateResponse) GetDeletedStates() int32 { + if x != nil { + return x.DeletedStates + } + return 0 +} + var File_daemon_proto protoreflect.FileDescriptor var file_daemon_proto_rawDesc = []byte{ @@ -2399,66 +2742,102 @@ var file_daemon_proto_rawDesc = []byte{ 0x32, 0x10, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x15, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x2a, 0x62, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x0b, 0x0a, 0x07, - 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x41, 0x4e, - 0x49, 0x43, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x46, 0x41, 0x54, 0x41, 0x4c, 0x10, 0x02, 0x12, - 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x41, - 0x52, 0x4e, 0x10, 0x04, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x05, 0x12, 0x09, - 0x0a, 0x05, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x06, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x52, 0x41, - 0x43, 0x45, 0x10, 0x07, 0x32, 0xb8, 0x06, 0x0a, 0x0d, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x36, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, - 0x14, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, - 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, - 0x0a, 0x0c, 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1b, - 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, - 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x64, 0x61, - 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, - 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x2d, 0x0a, 0x02, 0x55, - 0x70, 0x12, 0x11, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x55, 0x70, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x39, 0x0a, 0x06, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x12, 0x15, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x64, 0x61, - 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x04, 0x44, 0x6f, 0x77, 0x6e, 0x12, 0x13, 0x2e, - 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x6f, 0x77, 0x6e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x09, 0x47, 0x65, - 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, - 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x19, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, - 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x64, - 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, - 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x52, - 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, - 0x65, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6c, 0x65, - 0x63, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x4d, 0x0a, 0x0e, 0x44, 0x65, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f, - 0x75, 0x74, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, - 0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1c, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, - 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x48, 0x0a, 0x0b, 0x44, 0x65, 0x62, 0x75, 0x67, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, - 0x12, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x42, - 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x64, - 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x42, 0x75, 0x6e, 0x64, 0x6c, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0b, 0x47, - 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x1a, 0x2e, 0x64, 0x61, 0x65, - 0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, - 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0b, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, - 0x65, 0x76, 0x65, 0x6c, 0x12, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, - 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, - 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, - 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x22, 0x1b, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x13, 0x0a, + 0x11, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x22, 0x3b, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, + 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x65, 0x73, 0x22, + 0x44, 0x0a, 0x11, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x22, 0x3b, 0x0a, 0x12, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x63, + 0x6c, 0x65, 0x61, 0x6e, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x0d, 0x63, 0x6c, 0x65, 0x61, 0x6e, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x73, 0x22, 0x45, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x22, 0x3c, 0x0a, 0x13, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x2a, 0x62, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c, 0x65, + 0x76, 0x65, 0x6c, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, + 0x12, 0x09, 0x0a, 0x05, 0x50, 0x41, 0x4e, 0x49, 0x43, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x46, + 0x41, 0x54, 0x41, 0x4c, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, + 0x03, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x41, 0x52, 0x4e, 0x10, 0x04, 0x12, 0x08, 0x0a, 0x04, 0x49, + 0x4e, 0x46, 0x4f, 0x10, 0x05, 0x12, 0x09, 0x0a, 0x05, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x06, + 0x12, 0x09, 0x0a, 0x05, 0x54, 0x52, 0x41, 0x43, 0x45, 0x10, 0x07, 0x32, 0x90, 0x08, 0x0a, 0x0d, + 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x36, 0x0a, + 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x14, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, + 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x64, + 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0c, 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, + 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x57, + 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x57, 0x61, 0x69, 0x74, + 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x2d, 0x0a, 0x02, 0x55, 0x70, 0x12, 0x11, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, + 0x6e, 0x2e, 0x55, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x64, 0x61, + 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x39, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x15, 0x2e, 0x64, 0x61, + 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x04, + 0x44, 0x6f, 0x77, 0x6e, 0x12, 0x13, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x6f, + 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x64, 0x61, 0x65, 0x6d, + 0x6f, 0x6e, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x42, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, + 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, + 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x75, + 0x74, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x73, + 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, + 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x75, 0x74, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0c, + 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x64, + 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f, 0x75, 0x74, + 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x64, 0x61, 0x65, 0x6d, + 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4d, 0x0a, 0x0e, 0x44, 0x65, 0x73, + 0x65, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x64, 0x61, + 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, + 0x6e, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0b, 0x44, 0x65, 0x62, 0x75, + 0x67, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, + 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x62, + 0x75, 0x67, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, + 0x6c, 0x12, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, + 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, + 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, + 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0b, + 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x1a, 0x2e, 0x64, 0x61, + 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, + 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, + 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, 0x0a, + 0x0a, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x64, 0x61, + 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, + 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x12, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x08, + 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2474,7 +2853,7 @@ func file_daemon_proto_rawDescGZIP() []byte { } var file_daemon_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_daemon_proto_msgTypes = make([]protoimpl.MessageInfo, 32) +var file_daemon_proto_msgTypes = make([]protoimpl.MessageInfo, 39) var file_daemon_proto_goTypes = []interface{}{ (LogLevel)(0), // 0: daemon.LogLevel (*LoginRequest)(nil), // 1: daemon.LoginRequest @@ -2508,16 +2887,23 @@ var file_daemon_proto_goTypes = []interface{}{ (*GetLogLevelResponse)(nil), // 29: daemon.GetLogLevelResponse (*SetLogLevelRequest)(nil), // 30: daemon.SetLogLevelRequest (*SetLogLevelResponse)(nil), // 31: daemon.SetLogLevelResponse - nil, // 32: daemon.Route.ResolvedIPsEntry - (*durationpb.Duration)(nil), // 33: google.protobuf.Duration - (*timestamppb.Timestamp)(nil), // 34: google.protobuf.Timestamp + (*State)(nil), // 32: daemon.State + (*ListStatesRequest)(nil), // 33: daemon.ListStatesRequest + (*ListStatesResponse)(nil), // 34: daemon.ListStatesResponse + (*CleanStateRequest)(nil), // 35: daemon.CleanStateRequest + (*CleanStateResponse)(nil), // 36: daemon.CleanStateResponse + (*DeleteStateRequest)(nil), // 37: daemon.DeleteStateRequest + (*DeleteStateResponse)(nil), // 38: daemon.DeleteStateResponse + nil, // 39: daemon.Route.ResolvedIPsEntry + (*durationpb.Duration)(nil), // 40: google.protobuf.Duration + (*timestamppb.Timestamp)(nil), // 41: google.protobuf.Timestamp } var file_daemon_proto_depIdxs = []int32{ - 33, // 0: daemon.LoginRequest.dnsRouteInterval:type_name -> google.protobuf.Duration + 40, // 0: daemon.LoginRequest.dnsRouteInterval:type_name -> google.protobuf.Duration 19, // 1: daemon.StatusResponse.fullStatus:type_name -> daemon.FullStatus - 34, // 2: daemon.PeerState.connStatusUpdate:type_name -> google.protobuf.Timestamp - 34, // 3: daemon.PeerState.lastWireguardHandshake:type_name -> google.protobuf.Timestamp - 33, // 4: daemon.PeerState.latency:type_name -> google.protobuf.Duration + 41, // 2: daemon.PeerState.connStatusUpdate:type_name -> google.protobuf.Timestamp + 41, // 3: daemon.PeerState.lastWireguardHandshake:type_name -> google.protobuf.Timestamp + 40, // 4: daemon.PeerState.latency:type_name -> google.protobuf.Duration 16, // 5: daemon.FullStatus.managementState:type_name -> daemon.ManagementState 15, // 6: daemon.FullStatus.signalState:type_name -> daemon.SignalState 14, // 7: daemon.FullStatus.localPeerState:type_name -> daemon.LocalPeerState @@ -2525,39 +2911,46 @@ var file_daemon_proto_depIdxs = []int32{ 17, // 9: daemon.FullStatus.relays:type_name -> daemon.RelayState 18, // 10: daemon.FullStatus.dns_servers:type_name -> daemon.NSGroupState 25, // 11: daemon.ListRoutesResponse.routes:type_name -> daemon.Route - 32, // 12: daemon.Route.resolvedIPs:type_name -> daemon.Route.ResolvedIPsEntry + 39, // 12: daemon.Route.resolvedIPs:type_name -> daemon.Route.ResolvedIPsEntry 0, // 13: daemon.GetLogLevelResponse.level:type_name -> daemon.LogLevel 0, // 14: daemon.SetLogLevelRequest.level:type_name -> daemon.LogLevel - 24, // 15: daemon.Route.ResolvedIPsEntry.value:type_name -> daemon.IPList - 1, // 16: daemon.DaemonService.Login:input_type -> daemon.LoginRequest - 3, // 17: daemon.DaemonService.WaitSSOLogin:input_type -> daemon.WaitSSOLoginRequest - 5, // 18: daemon.DaemonService.Up:input_type -> daemon.UpRequest - 7, // 19: daemon.DaemonService.Status:input_type -> daemon.StatusRequest - 9, // 20: daemon.DaemonService.Down:input_type -> daemon.DownRequest - 11, // 21: daemon.DaemonService.GetConfig:input_type -> daemon.GetConfigRequest - 20, // 22: daemon.DaemonService.ListRoutes:input_type -> daemon.ListRoutesRequest - 22, // 23: daemon.DaemonService.SelectRoutes:input_type -> daemon.SelectRoutesRequest - 22, // 24: daemon.DaemonService.DeselectRoutes:input_type -> daemon.SelectRoutesRequest - 26, // 25: daemon.DaemonService.DebugBundle:input_type -> daemon.DebugBundleRequest - 28, // 26: daemon.DaemonService.GetLogLevel:input_type -> daemon.GetLogLevelRequest - 30, // 27: daemon.DaemonService.SetLogLevel:input_type -> daemon.SetLogLevelRequest - 2, // 28: daemon.DaemonService.Login:output_type -> daemon.LoginResponse - 4, // 29: daemon.DaemonService.WaitSSOLogin:output_type -> daemon.WaitSSOLoginResponse - 6, // 30: daemon.DaemonService.Up:output_type -> daemon.UpResponse - 8, // 31: daemon.DaemonService.Status:output_type -> daemon.StatusResponse - 10, // 32: daemon.DaemonService.Down:output_type -> daemon.DownResponse - 12, // 33: daemon.DaemonService.GetConfig:output_type -> daemon.GetConfigResponse - 21, // 34: daemon.DaemonService.ListRoutes:output_type -> daemon.ListRoutesResponse - 23, // 35: daemon.DaemonService.SelectRoutes:output_type -> daemon.SelectRoutesResponse - 23, // 36: daemon.DaemonService.DeselectRoutes:output_type -> daemon.SelectRoutesResponse - 27, // 37: daemon.DaemonService.DebugBundle:output_type -> daemon.DebugBundleResponse - 29, // 38: daemon.DaemonService.GetLogLevel:output_type -> daemon.GetLogLevelResponse - 31, // 39: daemon.DaemonService.SetLogLevel:output_type -> daemon.SetLogLevelResponse - 28, // [28:40] is the sub-list for method output_type - 16, // [16:28] is the sub-list for method input_type - 16, // [16:16] is the sub-list for extension type_name - 16, // [16:16] is the sub-list for extension extendee - 0, // [0:16] is the sub-list for field type_name + 32, // 15: daemon.ListStatesResponse.states:type_name -> daemon.State + 24, // 16: daemon.Route.ResolvedIPsEntry.value:type_name -> daemon.IPList + 1, // 17: daemon.DaemonService.Login:input_type -> daemon.LoginRequest + 3, // 18: daemon.DaemonService.WaitSSOLogin:input_type -> daemon.WaitSSOLoginRequest + 5, // 19: daemon.DaemonService.Up:input_type -> daemon.UpRequest + 7, // 20: daemon.DaemonService.Status:input_type -> daemon.StatusRequest + 9, // 21: daemon.DaemonService.Down:input_type -> daemon.DownRequest + 11, // 22: daemon.DaemonService.GetConfig:input_type -> daemon.GetConfigRequest + 20, // 23: daemon.DaemonService.ListRoutes:input_type -> daemon.ListRoutesRequest + 22, // 24: daemon.DaemonService.SelectRoutes:input_type -> daemon.SelectRoutesRequest + 22, // 25: daemon.DaemonService.DeselectRoutes:input_type -> daemon.SelectRoutesRequest + 26, // 26: daemon.DaemonService.DebugBundle:input_type -> daemon.DebugBundleRequest + 28, // 27: daemon.DaemonService.GetLogLevel:input_type -> daemon.GetLogLevelRequest + 30, // 28: daemon.DaemonService.SetLogLevel:input_type -> daemon.SetLogLevelRequest + 33, // 29: daemon.DaemonService.ListStates:input_type -> daemon.ListStatesRequest + 35, // 30: daemon.DaemonService.CleanState:input_type -> daemon.CleanStateRequest + 37, // 31: daemon.DaemonService.DeleteState:input_type -> daemon.DeleteStateRequest + 2, // 32: daemon.DaemonService.Login:output_type -> daemon.LoginResponse + 4, // 33: daemon.DaemonService.WaitSSOLogin:output_type -> daemon.WaitSSOLoginResponse + 6, // 34: daemon.DaemonService.Up:output_type -> daemon.UpResponse + 8, // 35: daemon.DaemonService.Status:output_type -> daemon.StatusResponse + 10, // 36: daemon.DaemonService.Down:output_type -> daemon.DownResponse + 12, // 37: daemon.DaemonService.GetConfig:output_type -> daemon.GetConfigResponse + 21, // 38: daemon.DaemonService.ListRoutes:output_type -> daemon.ListRoutesResponse + 23, // 39: daemon.DaemonService.SelectRoutes:output_type -> daemon.SelectRoutesResponse + 23, // 40: daemon.DaemonService.DeselectRoutes:output_type -> daemon.SelectRoutesResponse + 27, // 41: daemon.DaemonService.DebugBundle:output_type -> daemon.DebugBundleResponse + 29, // 42: daemon.DaemonService.GetLogLevel:output_type -> daemon.GetLogLevelResponse + 31, // 43: daemon.DaemonService.SetLogLevel:output_type -> daemon.SetLogLevelResponse + 34, // 44: daemon.DaemonService.ListStates:output_type -> daemon.ListStatesResponse + 36, // 45: daemon.DaemonService.CleanState:output_type -> daemon.CleanStateResponse + 38, // 46: daemon.DaemonService.DeleteState:output_type -> daemon.DeleteStateResponse + 32, // [32:47] is the sub-list for method output_type + 17, // [17:32] is the sub-list for method input_type + 17, // [17:17] is the sub-list for extension type_name + 17, // [17:17] is the sub-list for extension extendee + 0, // [0:17] is the sub-list for field type_name } func init() { file_daemon_proto_init() } @@ -2938,6 +3331,90 @@ func file_daemon_proto_init() { return nil } } + file_daemon_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*State); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_daemon_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListStatesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_daemon_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListStatesResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_daemon_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CleanStateRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_daemon_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CleanStateResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_daemon_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteStateRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_daemon_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteStateResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_daemon_proto_msgTypes[0].OneofWrappers = []interface{}{} type x struct{} @@ -2946,7 +3423,7 @@ func file_daemon_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_daemon_proto_rawDesc, NumEnums: 1, - NumMessages: 32, + NumMessages: 39, NumExtensions: 0, NumServices: 1, }, diff --git a/client/proto/daemon.proto b/client/proto/daemon.proto index 384bc0e6206..2e87dcffc81 100644 --- a/client/proto/daemon.proto +++ b/client/proto/daemon.proto @@ -45,6 +45,15 @@ service DaemonService { // SetLogLevel sets the log level of the daemon rpc SetLogLevel(SetLogLevelRequest) returns (SetLogLevelResponse) {} + + // List all states + rpc ListStates(ListStatesRequest) returns (ListStatesResponse) {} + + // Clean specific state or all states + rpc CleanState(CleanStateRequest) returns (CleanStateResponse) {} + + // Delete specific state or all states + rpc DeleteState(DeleteStateRequest) returns (DeleteStateResponse) {} }; message LoginRequest { @@ -293,4 +302,39 @@ message SetLogLevelRequest { } message SetLogLevelResponse { -} \ No newline at end of file +} + +// State represents a daemon state entry +message State { + string name = 1; +} + +// ListStatesRequest is empty as it requires no parameters +message ListStatesRequest {} + +// ListStatesResponse contains a list of states +message ListStatesResponse { + repeated State states = 1; +} + +// CleanStateRequest for cleaning states +message CleanStateRequest { + string state_name = 1; + bool all = 2; +} + +// CleanStateResponse contains the result of the clean operation +message CleanStateResponse { + int32 cleaned_states = 1; +} + +// DeleteStateRequest for deleting states +message DeleteStateRequest { + string state_name = 1; + bool all = 2; +} + +// DeleteStateResponse contains the result of the delete operation +message DeleteStateResponse { + int32 deleted_states = 1; +} diff --git a/client/proto/daemon_grpc.pb.go b/client/proto/daemon_grpc.pb.go index e0bc117e52d..92c2193052e 100644 --- a/client/proto/daemon_grpc.pb.go +++ b/client/proto/daemon_grpc.pb.go @@ -43,6 +43,12 @@ type DaemonServiceClient interface { GetLogLevel(ctx context.Context, in *GetLogLevelRequest, opts ...grpc.CallOption) (*GetLogLevelResponse, error) // SetLogLevel sets the log level of the daemon SetLogLevel(ctx context.Context, in *SetLogLevelRequest, opts ...grpc.CallOption) (*SetLogLevelResponse, error) + // List all states + ListStates(ctx context.Context, in *ListStatesRequest, opts ...grpc.CallOption) (*ListStatesResponse, error) + // Clean specific state or all states + CleanState(ctx context.Context, in *CleanStateRequest, opts ...grpc.CallOption) (*CleanStateResponse, error) + // Delete specific state or all states + DeleteState(ctx context.Context, in *DeleteStateRequest, opts ...grpc.CallOption) (*DeleteStateResponse, error) } type daemonServiceClient struct { @@ -161,6 +167,33 @@ func (c *daemonServiceClient) SetLogLevel(ctx context.Context, in *SetLogLevelRe return out, nil } +func (c *daemonServiceClient) ListStates(ctx context.Context, in *ListStatesRequest, opts ...grpc.CallOption) (*ListStatesResponse, error) { + out := new(ListStatesResponse) + err := c.cc.Invoke(ctx, "/daemon.DaemonService/ListStates", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *daemonServiceClient) CleanState(ctx context.Context, in *CleanStateRequest, opts ...grpc.CallOption) (*CleanStateResponse, error) { + out := new(CleanStateResponse) + err := c.cc.Invoke(ctx, "/daemon.DaemonService/CleanState", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *daemonServiceClient) DeleteState(ctx context.Context, in *DeleteStateRequest, opts ...grpc.CallOption) (*DeleteStateResponse, error) { + out := new(DeleteStateResponse) + err := c.cc.Invoke(ctx, "/daemon.DaemonService/DeleteState", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // DaemonServiceServer is the server API for DaemonService service. // All implementations must embed UnimplementedDaemonServiceServer // for forward compatibility @@ -190,6 +223,12 @@ type DaemonServiceServer interface { GetLogLevel(context.Context, *GetLogLevelRequest) (*GetLogLevelResponse, error) // SetLogLevel sets the log level of the daemon SetLogLevel(context.Context, *SetLogLevelRequest) (*SetLogLevelResponse, error) + // List all states + ListStates(context.Context, *ListStatesRequest) (*ListStatesResponse, error) + // Clean specific state or all states + CleanState(context.Context, *CleanStateRequest) (*CleanStateResponse, error) + // Delete specific state or all states + DeleteState(context.Context, *DeleteStateRequest) (*DeleteStateResponse, error) mustEmbedUnimplementedDaemonServiceServer() } @@ -233,6 +272,15 @@ func (UnimplementedDaemonServiceServer) GetLogLevel(context.Context, *GetLogLeve func (UnimplementedDaemonServiceServer) SetLogLevel(context.Context, *SetLogLevelRequest) (*SetLogLevelResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SetLogLevel not implemented") } +func (UnimplementedDaemonServiceServer) ListStates(context.Context, *ListStatesRequest) (*ListStatesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListStates not implemented") +} +func (UnimplementedDaemonServiceServer) CleanState(context.Context, *CleanStateRequest) (*CleanStateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CleanState not implemented") +} +func (UnimplementedDaemonServiceServer) DeleteState(context.Context, *DeleteStateRequest) (*DeleteStateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteState not implemented") +} func (UnimplementedDaemonServiceServer) mustEmbedUnimplementedDaemonServiceServer() {} // UnsafeDaemonServiceServer may be embedded to opt out of forward compatibility for this service. @@ -462,6 +510,60 @@ func _DaemonService_SetLogLevel_Handler(srv interface{}, ctx context.Context, de return interceptor(ctx, in, info, handler) } +func _DaemonService_ListStates_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListStatesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DaemonServiceServer).ListStates(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/daemon.DaemonService/ListStates", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DaemonServiceServer).ListStates(ctx, req.(*ListStatesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _DaemonService_CleanState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CleanStateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DaemonServiceServer).CleanState(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/daemon.DaemonService/CleanState", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DaemonServiceServer).CleanState(ctx, req.(*CleanStateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _DaemonService_DeleteState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteStateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DaemonServiceServer).DeleteState(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/daemon.DaemonService/DeleteState", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DaemonServiceServer).DeleteState(ctx, req.(*DeleteStateRequest)) + } + return interceptor(ctx, in, info, handler) +} + // DaemonService_ServiceDesc is the grpc.ServiceDesc for DaemonService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -517,6 +619,18 @@ var DaemonService_ServiceDesc = grpc.ServiceDesc{ MethodName: "SetLogLevel", Handler: _DaemonService_SetLogLevel_Handler, }, + { + MethodName: "ListStates", + Handler: _DaemonService_ListStates_Handler, + }, + { + MethodName: "CleanState", + Handler: _DaemonService_CleanState_Handler, + }, + { + MethodName: "DeleteState", + Handler: _DaemonService_DeleteState_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "daemon.proto", diff --git a/client/server/state.go b/client/server/state.go index 509782e86c7..222c7c7bd00 100644 --- a/client/server/state.go +++ b/client/server/state.go @@ -5,12 +5,112 @@ import ( "fmt" "github.com/hashicorp/go-multierror" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" nberrors "github.com/netbirdio/netbird/client/errors" + "github.com/netbirdio/netbird/client/internal" "github.com/netbirdio/netbird/client/internal/statemanager" + "github.com/netbirdio/netbird/client/proto" ) -// restoreResidualConfig checks if the client was not shut down in a clean way and restores residual state if required. +// ListStates returns a list of all saved states +func (s *Server) ListStates(_ context.Context, _ *proto.ListStatesRequest) (*proto.ListStatesResponse, error) { + mgr := statemanager.New(statemanager.GetDefaultStatePath()) + + stateNames, err := mgr.GetSavedStateNames() + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to get saved state names: %v", err) + } + + states := make([]*proto.State, 0, len(stateNames)) + for _, name := range stateNames { + states = append(states, &proto.State{ + Name: name, + }) + } + + return &proto.ListStatesResponse{ + States: states, + }, nil +} + +// CleanState handles cleaning of states (performing cleanup operations) +func (s *Server) CleanState(ctx context.Context, req *proto.CleanStateRequest) (*proto.CleanStateResponse, error) { + if s.connectClient.Status() == internal.StatusConnected || s.connectClient.Status() == internal.StatusConnecting { + return nil, status.Errorf(codes.FailedPrecondition, "cannot clean state while connecting or connected, run 'netbird down' first.") + } + + if req.All { + // Reuse existing cleanup logic for all states + if err := restoreResidualState(ctx); err != nil { + return nil, status.Errorf(codes.Internal, "failed to clean all states: %v", err) + } + + // Get count of cleaned states + mgr := statemanager.New(statemanager.GetDefaultStatePath()) + stateNames, err := mgr.GetSavedStateNames() + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to get state count: %v", err) + } + + return &proto.CleanStateResponse{ + CleanedStates: int32(len(stateNames)), + }, nil + } + + // Handle single state cleanup + mgr := statemanager.New(statemanager.GetDefaultStatePath()) + registerStates(mgr) + + if err := mgr.CleanupStateByName(req.StateName); err != nil { + return nil, status.Errorf(codes.Internal, "failed to clean state %s: %v", req.StateName, err) + } + + if err := mgr.PersistState(ctx); err != nil { + return nil, status.Errorf(codes.Internal, "failed to persist state changes: %v", err) + } + + return &proto.CleanStateResponse{ + CleanedStates: 1, + }, nil +} + +// DeleteState handles deletion of states without cleanup +func (s *Server) DeleteState(ctx context.Context, req *proto.DeleteStateRequest) (*proto.DeleteStateResponse, error) { + if s.connectClient.Status() == internal.StatusConnected || s.connectClient.Status() == internal.StatusConnecting { + return nil, status.Errorf(codes.FailedPrecondition, "cannot clean state while connecting or connected, run 'netbird down' first.") + } + + mgr := statemanager.New(statemanager.GetDefaultStatePath()) + + var count int + var err error + + if req.All { + count, err = mgr.DeleteAllStates() + } else { + err = mgr.DeleteStateByName(req.StateName) + if err == nil { + count = 1 + } + } + + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to delete state: %v", err) + } + + // Persist the changes + if err := mgr.PersistState(ctx); err != nil { + return nil, status.Errorf(codes.Internal, "failed to persist state changes: %v", err) + } + + return &proto.DeleteStateResponse{ + DeletedStates: int32(count), + }, nil +} + +// restoreResidualState checks if the client was not shut down in a clean way and restores residual if required. // Otherwise, we might not be able to connect to the management server to retrieve new config. func restoreResidualState(ctx context.Context) error { path := statemanager.GetDefaultStatePath() @@ -24,6 +124,7 @@ func restoreResidualState(ctx context.Context) error { registerStates(mgr) var merr *multierror.Error + if err := mgr.PerformCleanup(); err != nil { merr = multierror.Append(merr, fmt.Errorf("perform cleanup: %w", err)) }