mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-04-30 12:30:17 +00:00
The DisableDockerUpdateActions setting was being saved to disk but not updated in h.config, causing the UI toggle to appear to revert on page refresh since the API returned the stale runtime value. Related to #1023
244 lines
6.3 KiB
Go
244 lines
6.3 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var mockCmd = &cobra.Command{
|
|
Use: "mock",
|
|
Short: "Manage mock/demo mode for development and demos",
|
|
Long: `Enable or disable mock mode to run Pulse with simulated data instead of real infrastructure.`,
|
|
}
|
|
|
|
var mockEnableCmd = &cobra.Command{
|
|
Use: "enable",
|
|
Short: "Enable mock mode with simulated infrastructure data",
|
|
Long: `Enable mock mode to run Pulse with simulated data.
|
|
|
|
This creates/updates the mock.env file and requires a service restart.
|
|
Mock mode is useful for:
|
|
- Demos without real infrastructure
|
|
- Development and testing
|
|
- Showcasing AI patrol features
|
|
|
|
Example:
|
|
pulse mock enable
|
|
sudo systemctl restart pulse`,
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
if err := setMockMode(true); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
|
osExit(1)
|
|
return
|
|
}
|
|
fmt.Println("✓ Mock mode enabled")
|
|
fmt.Println("")
|
|
fmt.Println("Restart Pulse to apply changes:")
|
|
fmt.Println(" sudo systemctl restart pulse")
|
|
},
|
|
}
|
|
|
|
var mockDisableCmd = &cobra.Command{
|
|
Use: "disable",
|
|
Short: "Disable mock mode and use real infrastructure",
|
|
Long: `Disable mock mode to reconnect to real infrastructure.
|
|
|
|
This updates the mock.env file and requires a service restart.
|
|
|
|
Example:
|
|
pulse mock disable
|
|
sudo systemctl restart pulse`,
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
if err := setMockMode(false); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
|
osExit(1)
|
|
return
|
|
}
|
|
fmt.Println("✓ Mock mode disabled")
|
|
fmt.Println("")
|
|
fmt.Println("Restart Pulse to apply changes:")
|
|
fmt.Println(" sudo systemctl restart pulse")
|
|
},
|
|
}
|
|
|
|
var mockStatusCmd = &cobra.Command{
|
|
Use: "status",
|
|
Short: "Show current mock mode status",
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
enabled, config := getMockStatus()
|
|
if enabled {
|
|
fmt.Println("Mock mode: ENABLED")
|
|
fmt.Println("")
|
|
fmt.Println("Configuration:")
|
|
for _, line := range config {
|
|
fmt.Printf(" %s\n", line)
|
|
}
|
|
} else {
|
|
fmt.Println("Mock mode: DISABLED")
|
|
fmt.Println("")
|
|
fmt.Println("Run 'pulse mock enable' to enable mock mode")
|
|
}
|
|
},
|
|
}
|
|
|
|
func init() {
|
|
mockCmd.AddCommand(mockEnableCmd)
|
|
mockCmd.AddCommand(mockDisableCmd)
|
|
mockCmd.AddCommand(mockStatusCmd)
|
|
rootCmd.AddCommand(mockCmd)
|
|
}
|
|
|
|
// getMockEnvPath returns the path to mock.env
|
|
func getMockEnvPath() string {
|
|
// Check PULSE_DATA_DIR first, then fall back to /opt/pulse
|
|
dataDir := os.Getenv("PULSE_DATA_DIR")
|
|
if dataDir == "" {
|
|
// Check if we're in development (running from /opt/pulse)
|
|
if _, err := os.Stat("/opt/pulse/mock.env"); err == nil {
|
|
return "/opt/pulse/mock.env"
|
|
}
|
|
dataDir = "/opt/pulse"
|
|
}
|
|
return filepath.Join(dataDir, "mock.env")
|
|
}
|
|
|
|
// setMockMode enables or disables mock mode by updating mock.env
|
|
func setMockMode(enable bool) error {
|
|
envPath := getMockEnvPath()
|
|
|
|
// Read existing config or create default
|
|
config := getDefaultMockConfig()
|
|
|
|
// Try to preserve existing config
|
|
if data, err := os.ReadFile(envPath); err == nil {
|
|
existing := parseMockEnv(string(data))
|
|
for k, v := range existing {
|
|
if k != "PULSE_MOCK_MODE" {
|
|
config[k] = v
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set the mode
|
|
if enable {
|
|
config["PULSE_MOCK_MODE"] = "true"
|
|
} else {
|
|
config["PULSE_MOCK_MODE"] = "false"
|
|
}
|
|
|
|
// Write the file
|
|
return writeMockEnv(envPath, config)
|
|
}
|
|
|
|
// getDefaultMockConfig returns the default mock configuration
|
|
func getDefaultMockConfig() map[string]string {
|
|
return map[string]string{
|
|
"PULSE_MOCK_MODE": "false",
|
|
"PULSE_MOCK_NODES": "7",
|
|
"PULSE_MOCK_VMS_PER_NODE": "5",
|
|
"PULSE_MOCK_LXCS_PER_NODE": "8",
|
|
"PULSE_MOCK_DOCKER_HOSTS": "3",
|
|
"PULSE_MOCK_DOCKER_CONTAINERS": "12",
|
|
"PULSE_MOCK_GENERIC_HOSTS": "4",
|
|
"PULSE_MOCK_K8S_CLUSTERS": "2",
|
|
"PULSE_MOCK_K8S_NODES": "4",
|
|
"PULSE_MOCK_K8S_PODS": "30",
|
|
"PULSE_MOCK_K8S_DEPLOYMENTS": "12",
|
|
"PULSE_MOCK_RANDOM_METRICS": "true",
|
|
"PULSE_MOCK_STOPPED_PERCENT": "20",
|
|
}
|
|
}
|
|
|
|
// parseMockEnv parses a mock.env file into a map
|
|
func parseMockEnv(content string) map[string]string {
|
|
result := make(map[string]string)
|
|
for _, line := range strings.Split(content, "\n") {
|
|
line = strings.TrimSpace(line)
|
|
if line == "" || strings.HasPrefix(line, "#") {
|
|
continue
|
|
}
|
|
parts := strings.SplitN(line, "=", 2)
|
|
if len(parts) == 2 {
|
|
result[parts[0]] = parts[1]
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
// writeMockEnv writes the mock.env file
|
|
func writeMockEnv(path string, config map[string]string) error {
|
|
// Order keys for consistent output
|
|
keys := []string{
|
|
"PULSE_MOCK_MODE",
|
|
"PULSE_MOCK_NODES",
|
|
"PULSE_MOCK_VMS_PER_NODE",
|
|
"PULSE_MOCK_LXCS_PER_NODE",
|
|
"PULSE_MOCK_DOCKER_HOSTS",
|
|
"PULSE_MOCK_DOCKER_CONTAINERS",
|
|
"PULSE_MOCK_GENERIC_HOSTS",
|
|
"PULSE_MOCK_K8S_CLUSTERS",
|
|
"PULSE_MOCK_K8S_NODES",
|
|
"PULSE_MOCK_K8S_PODS",
|
|
"PULSE_MOCK_K8S_DEPLOYMENTS",
|
|
"PULSE_MOCK_RANDOM_METRICS",
|
|
"PULSE_MOCK_STOPPED_PERCENT",
|
|
}
|
|
|
|
var lines []string
|
|
lines = append(lines, "# Pulse Mock Mode Configuration")
|
|
lines = append(lines, "# Enable with: pulse mock enable")
|
|
lines = append(lines, "# Disable with: pulse mock disable")
|
|
lines = append(lines, "")
|
|
|
|
for _, key := range keys {
|
|
if val, ok := config[key]; ok {
|
|
lines = append(lines, fmt.Sprintf("%s=%s", key, val))
|
|
}
|
|
}
|
|
|
|
// Add any extra keys not in our ordered list
|
|
for k, v := range config {
|
|
found := false
|
|
for _, key := range keys {
|
|
if k == key {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
lines = append(lines, fmt.Sprintf("%s=%s", k, v))
|
|
}
|
|
}
|
|
|
|
content := strings.Join(lines, "\n") + "\n"
|
|
return os.WriteFile(path, []byte(content), 0644)
|
|
}
|
|
|
|
// getMockStatus returns the current mock mode status and config
|
|
func getMockStatus() (enabled bool, config []string) {
|
|
envPath := getMockEnvPath()
|
|
|
|
data, err := os.ReadFile(envPath)
|
|
if err != nil {
|
|
return false, nil
|
|
}
|
|
|
|
parsed := parseMockEnv(string(data))
|
|
enabled = parsed["PULSE_MOCK_MODE"] == "true"
|
|
|
|
if enabled {
|
|
config = []string{
|
|
fmt.Sprintf("Nodes: %s", parsed["PULSE_MOCK_NODES"]),
|
|
fmt.Sprintf("VMs per node: %s", parsed["PULSE_MOCK_VMS_PER_NODE"]),
|
|
fmt.Sprintf("Containers per node: %s", parsed["PULSE_MOCK_LXCS_PER_NODE"]),
|
|
fmt.Sprintf("Docker hosts: %s", parsed["PULSE_MOCK_DOCKER_HOSTS"]),
|
|
fmt.Sprintf("K8s clusters: %s", parsed["PULSE_MOCK_K8S_CLUSTERS"]),
|
|
}
|
|
}
|
|
|
|
return enabled, config
|
|
}
|