feat: add diagnostics and safe reset maintenance

This commit is contained in:
proxyctl coordinator
2026-07-17 03:28:29 +08:00
parent 3003450dce
commit d4c76e4b3c
13 changed files with 1041 additions and 23 deletions
+58
View File
@@ -0,0 +1,58 @@
package profile
import (
"encoding/json"
"errors"
"fmt"
"io"
"io/fs"
"os"
"golang.org/x/sys/unix"
)
// ReadOnlyLoad reads and validates Profile configuration without creating,
// chmodding, chowning, locking, or replacing any filesystem object.
func (s *FileStore) ReadOnlyLoad() (State, error) {
file, err := os.OpenFile(s.path, unix.O_RDONLY|unix.O_NOFOLLOW|unix.O_NONBLOCK, 0)
if errors.Is(err, fs.ErrNotExist) {
return State{}, nil
}
if err != nil {
return State{}, fmt.Errorf("open Profile configuration read-only: %w", err)
}
defer file.Close()
info, err := file.Stat()
if err != nil {
return State{}, fmt.Errorf("inspect Profile configuration: %w", err)
}
if !info.Mode().IsRegular() {
return State{}, errors.New("Profile configuration is not a regular file")
}
data, err := io.ReadAll(file)
if err != nil {
return State{}, fmt.Errorf("read Profile configuration: %w", err)
}
var state State
if err := json.Unmarshal(data, &state); err != nil {
return State{}, fmt.Errorf("parse Profile configuration: %w", err)
}
for _, candidate := range state.Profiles {
if _, err := ValidateAndNormalize(candidate); err != nil {
return State{}, fmt.Errorf("Profile %q is invalid: %w", candidate.Name, err)
}
}
if state.ActiveProfile != "" && indexProfile(state.Profiles, state.ActiveProfile) < 0 {
return State{}, fmt.Errorf("Active Profile %q does not exist", state.ActiveProfile)
}
return state, nil
}
func indexProfile(profiles []Profile, name string) int {
for i := range profiles {
if profiles[i].Name == name {
return i
}
}
return -1
}
+35
View File
@@ -0,0 +1,35 @@
package profile_test
import (
"os"
"path/filepath"
"testing"
"proxyctl/internal/profile"
)
func TestReadOnlyLoadValidatesWithoutRepairingOrCreatingStorage(t *testing.T) {
directory := filepath.Join(t.TempDir(), "proxyctl")
if err := os.MkdirAll(directory, 0o755); err != nil {
t.Fatal(err)
}
path := filepath.Join(directory, "profiles.json")
if err := os.WriteFile(path, []byte(`{"profiles":[{"name":"office","mode":"single","singleUrl":"http://proxy.test:8080","httpUrl":"http://proxy.test:8080","httpsUrl":"http://proxy.test:8080","allUrl":"http://proxy.test:8080"}],"activeProfile":"office"}`), 0o644); err != nil {
t.Fatal(err)
}
store := profile.NewFileStore(path)
state, err := store.ReadOnlyLoad()
if err != nil || state.ActiveProfile != "office" {
t.Fatalf("read-only load state=%#v err=%v", state, err)
}
info, err := os.Stat(path)
if err != nil {
t.Fatal(err)
}
if info.Mode().Perm() != 0o644 {
t.Fatalf("read-only load changed mode to %o", info.Mode().Perm())
}
if _, err := os.Stat(path + ".lock"); !os.IsNotExist(err) {
t.Fatalf("read-only load created lock: %v", err)
}
}