diff --git a/README.md b/README.md index d461fb3..82a8dff 100644 --- a/README.md +++ b/README.md @@ -30,18 +30,18 @@ Alongside this project, I am developing a seperate web-based dashboard that can ## Roadmap - [ ] Configuration - - [ ] Custom configuration path (passed to the daemon with a flag `--config-path`) - - [ ] Custom application data path (*for templates, etc, and to be used as a base for the other config directories*) + - [x] Custom configuration path (passed to the daemon with a flag `--config`) + - [ ] Daemon listen port (kind of implemented, could be cleaner) - [x] Declarative Server configuration (Create servers in config) - [ ] Server management - [x] Create servers - - [x] Automatic server binary downloading by version (*Caveat: This works BUT the configuration template downloader needs to be figured out, otherwise you have to manually add a template for each version before it can actually be ran*) + - [x] Automatic server binary downloading by version - [ ] Delete servers - [x] Start/Stop servers - [ ] Server configuration - - [ ] Download correct configuration template for version + - [x] Download correct configuration template for version (see availible templates [here](https://git.bellsworne.tech/chrisbell/vssm_config_templates)) - [x] Version - [x] Server Name - [x] Port diff --git a/config.go b/config.go index 9b04b4d..46af439 100644 --- a/config.go +++ b/config.go @@ -18,7 +18,7 @@ type AppConfig struct { Daemon struct { ListenAddress string `json:"listen_address"` - } + } `json:"daemon"` Instances map[string]VsServerConfigOptions `json:"instances"` } diff --git a/daemon.go b/daemon.go index 292cb27..535a300 100644 --- a/daemon.go +++ b/daemon.go @@ -6,7 +6,6 @@ import ( "net/http" "os" "os/signal" - "path/filepath" "sync" "syscall" "time" @@ -25,12 +24,14 @@ type InstanceStatusResponse struct { type DaemonServer struct { cfg *AppConfig + configPath string procManager *ProcessManager } -func StartDaemon(cfg *AppConfig) error { +func StartDaemon(cfg *AppConfig, configPath string) error { ds := &DaemonServer{ cfg: cfg, + configPath: configPath, procManager: NewProcessManager(), } @@ -173,10 +174,16 @@ func (ds *DaemonServer) handleCreate(w http.ResponseWriter, r *http.Request) { ds.cfg.Instances[name] = options - home, _ := os.UserHomeDir() - configPath := filepath.Join(home, ".config", "vs-manager", "config.json") - data, _ := json.MarshalIndent(ds.cfg, "", " ") - _ = os.WriteFile(configPath, data, 0644) + data, err := json.MarshalIndent(ds.cfg, "", " ") + if err != nil { + http.Error(w, fmt.Sprintf("Failed processing profile adjustments: %v", err), http.StatusInternalServerError) + return + } + + if err := os.WriteFile(ds.configPath, data, 0644); err != nil { + http.Error(w, fmt.Sprintf("Failed saving configuration adjustments to disk: %v", err), http.StatusInternalServerError) + return + } w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, "Successfully created and stored profile for instance %s", name) @@ -200,7 +207,7 @@ func (ds *DaemonServer) handleStart(w http.ResponseWriter, r *http.Request) { return } - instanceConfigPath := filepath.Join(ds.cfg.Storage.InstancesDir, name, "serverconfig.json") + instanceConfigPath := ds.configPath err := SyncInstanceConfig(options.Version, instanceConfigPath, options, ds.cfg) if err != nil { http.Error(w, "Failed to sync config: "+err.Error(), http.StatusInternalServerError) diff --git a/main.go b/main.go index 70cd0e1..2a66c03 100644 --- a/main.go +++ b/main.go @@ -33,7 +33,12 @@ func main() { return } - cfg, err := LoadOrCreateConfig(*configFlag) + absConfigPath, err := filepath.Abs(*configFlag) + if err != nil { + log.Fatalf("Failed to resolve absolute configuration path target: %v", err) + } + + cfg, err := LoadOrCreateConfig(absConfigPath) if err != nil { log.Fatalf("Initialization failed: %v", err) } @@ -43,8 +48,8 @@ func main() { switch subCommand { case "daemon": - fmt.Println("Initializing VS server manager background supervisor...") - if err := StartDaemon(cfg); err != nil { + fmt.Printf("Initializing VS server manager background supervisor [Config: %s]...\n", absConfigPath) + if err := StartDaemon(cfg, absConfigPath); err != nil { log.Fatalf("Daemon runtime fatal error: %v", err) } @@ -71,7 +76,7 @@ func main() { sendIPCRequest(cfg, "POST", fmt.Sprintf("/instances/stop?name=%s", url.QueryEscape(name)), nil) case "cmd": - if len(args) < 4 { + if len(args) < 3 { log.Fatalf("Usage: vssm cmd \"\"") } name := args[1] @@ -84,6 +89,9 @@ func main() { case "list", "status": fetchAndPrintStatus(cfg) + case "show-config": + fmt.Printf("%v", cfg.Storage.AppDataDir) + default: printUsage() } @@ -107,7 +115,8 @@ func sendIPCRequest(cfg *AppConfig, method string, path string, body io.Reader) defer resp.Body.Close() respBody, _ := io.ReadAll(resp.Body) - if resp.StatusCode != http.StatusOK { + + if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated { log.Fatalf("Error from daemon: %s", string(respBody)) } @@ -149,10 +158,13 @@ func fetchAndPrintStatus(cfg *AppConfig) { func printUsage() { fmt.Println("VintageStory Server Manager") - fmt.Println("\nUsage:") + fmt.Println("\nGlobal Options:") + fmt.Println(" --config Explicitly target a non-default config structure file location") + fmt.Println("\nUsage Commands:") fmt.Println(" vssm daemon Starts the background process supervisor") fmt.Println(" vssm create Provisions baseline configuration and stores instance profile") fmt.Println(" vssm start Launches an existing server instance using stored profile") fmt.Println(" vssm stop Gracefully shuts down a server instance") fmt.Println(" vssm cmd \"\" Dispatches a terminal console command down the pipe") + fmt.Println(" vssm list Displays the operational matrix of all instances") }