// Package state manages the persistent deployment state file (~/.wallarm/state.json). package state import ( "encoding/json" "fmt" "os" "path/filepath" ) // StateDir is where wallarm stores its state. const StateDir = ".wallarm" // State represents the persistent deployment state. type State struct { DeploymentType string `json:"deployment_type,omitempty"` // docker or native CloudRegion string `json:"cloud_region,omitempty"` // US or EU APIHost string `json:"api_host,omitempty"` // e.g., api.wallarm.com APIToken string `json:"api_token,omitempty"` // Wallarm API token (sensitive) Nodes []Node `json:"nodes"` } // Node represents a single deployed Wallarm node (docker container or native systemd unit). type Node struct { Name string `json:"name"` Type string `json:"type"` // docker or native Address string `json:"address,omitempty"` // listen address for native Port int `json:"port,omitempty"` // ingress port for docker UpstreamIP string `json:"upstream_ip,omitempty"` // docker UpstreamPort int `json:"upstream_port,omitempty"` // docker Status string `json:"status"` // running, stopped, unknown CreatedAt string `json:"created_at"` } // Path returns the full path to the state file. func Path() string { home, err := os.UserHomeDir() if err != nil { home = "/root" } return filepath.Join(home, StateDir, "state.json") } // Load reads and parses the state file. Returns nil if the file doesn't exist. func Load() (*State, error) { p := Path() data, err := os.ReadFile(p) if os.IsNotExist(err) { return nil, nil } if err != nil { return nil, fmt.Errorf("read state: %w", err) } var s State if err := json.Unmarshal(data, &s); err != nil { return nil, fmt.Errorf("parse state: %w", err) } return &s, nil } // Save writes the state to disk, creating directories as needed. func Save(s *State) error { p := Path() if err := os.MkdirAll(filepath.Dir(p), 0700); err != nil { return fmt.Errorf("create state dir: %w", err) } data, err := json.MarshalIndent(s, "", " ") if err != nil { return fmt.Errorf("marshal state: %w", err) } if err := os.WriteFile(p, data, 0600); err != nil { return fmt.Errorf("write state: %w", err) } return nil } // HasDeployment returns true if a state file exists with at least one node. func HasDeployment() bool { s, err := Load() if err != nil || s == nil { return false } return len(s.Nodes) > 0 }