mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-06 16:16:26 +00:00
220 lines
6.4 KiB
Go
220 lines
6.4 KiB
Go
package pulsecli
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/config"
|
|
mockruntime "github.com/rcourtman/pulse-go-rewrite/internal/mock"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
func GetMockEnvPath(mock *MockDeps) string {
|
|
dataDir := strings.TrimSpace(os.Getenv("PULSE_DATA_DIR"))
|
|
if dataDir != "" {
|
|
return filepath.Join(dataDir, ".env")
|
|
}
|
|
if info, err := mockStat(mock, "tmp/dev-config"); err == nil && info.IsDir() {
|
|
return filepath.Join("tmp/dev-config", ".env")
|
|
}
|
|
probe := filepath.Join(mockDefaultEnvDir(mock), ".env")
|
|
if _, err := mockStat(mock, probe); err == nil {
|
|
return probe
|
|
}
|
|
return filepath.Join(config.ResolveRuntimeDataDir(""), ".env")
|
|
}
|
|
|
|
func newMockCmd(mock *MockDeps) *cobra.Command {
|
|
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.`,
|
|
}
|
|
|
|
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 runtime .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(mock, true); err != nil {
|
|
fmt.Fprintf(cmd.ErrOrStderr(), "Error: %v\n", err)
|
|
mockExit(mock, 1)
|
|
return
|
|
}
|
|
fmt.Fprintln(cmd.OutOrStdout(), "✓ Mock mode enabled")
|
|
fmt.Fprintln(cmd.OutOrStdout())
|
|
fmt.Fprintln(cmd.OutOrStdout(), "Restart Pulse to apply changes:")
|
|
fmt.Fprintln(cmd.OutOrStdout(), " sudo systemctl restart pulse")
|
|
},
|
|
}
|
|
|
|
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 runtime .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(mock, false); err != nil {
|
|
fmt.Fprintf(cmd.ErrOrStderr(), "Error: %v\n", err)
|
|
mockExit(mock, 1)
|
|
return
|
|
}
|
|
fmt.Fprintln(cmd.OutOrStdout(), "✓ Mock mode disabled")
|
|
fmt.Fprintln(cmd.OutOrStdout())
|
|
fmt.Fprintln(cmd.OutOrStdout(), "Restart Pulse to apply changes:")
|
|
fmt.Fprintln(cmd.OutOrStdout(), " sudo systemctl restart pulse")
|
|
},
|
|
}
|
|
|
|
mockStatusCmd := &cobra.Command{
|
|
Use: "status",
|
|
Short: "Show current mock mode status",
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
enabled, config := getMockStatus(mock)
|
|
if enabled {
|
|
fmt.Fprintln(cmd.OutOrStdout(), "Mock mode: ENABLED")
|
|
fmt.Fprintln(cmd.OutOrStdout())
|
|
fmt.Fprintln(cmd.OutOrStdout(), "Configuration:")
|
|
for _, line := range config {
|
|
fmt.Fprintf(cmd.OutOrStdout(), " %s\n", line)
|
|
}
|
|
return
|
|
}
|
|
fmt.Fprintln(cmd.OutOrStdout(), "Mock mode: DISABLED")
|
|
fmt.Fprintln(cmd.OutOrStdout())
|
|
fmt.Fprintln(cmd.OutOrStdout(), "Run 'pulse mock enable' to enable mock mode")
|
|
},
|
|
}
|
|
|
|
mockCmd.AddCommand(mockEnableCmd)
|
|
mockCmd.AddCommand(mockDisableCmd)
|
|
mockCmd.AddCommand(mockStatusCmd)
|
|
|
|
return mockCmd
|
|
}
|
|
|
|
func setMockMode(mock *MockDeps, enable bool) error {
|
|
envPath := GetMockEnvPath(mock)
|
|
config := getDefaultMockConfig()
|
|
|
|
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
|
|
}
|
|
}
|
|
}
|
|
|
|
if enable {
|
|
config["PULSE_MOCK_MODE"] = "true"
|
|
} else {
|
|
config["PULSE_MOCK_MODE"] = "false"
|
|
}
|
|
|
|
return writeMockEnv(envPath, config)
|
|
}
|
|
|
|
func getMockStatus(mock *MockDeps) (bool, []string) {
|
|
envPath := GetMockEnvPath(mock)
|
|
data, err := os.ReadFile(envPath)
|
|
if err != nil {
|
|
return false, nil
|
|
}
|
|
|
|
config := parseMockEnv(string(data))
|
|
enabled := strings.EqualFold(strings.TrimSpace(config["PULSE_MOCK_MODE"]), "true")
|
|
lines := make([]string, 0, len(config))
|
|
for key, value := range config {
|
|
lines = append(lines, fmt.Sprintf("%s=%s", key, value))
|
|
}
|
|
return enabled, lines
|
|
}
|
|
|
|
func getDefaultMockConfig() map[string]string {
|
|
cfg := mockruntime.DefaultConfig
|
|
return map[string]string{
|
|
"PULSE_MOCK_MODE": "false",
|
|
"PULSE_MOCK_NODES": strconv.Itoa(cfg.NodeCount),
|
|
"PULSE_MOCK_VMS_PER_NODE": strconv.Itoa(cfg.VMsPerNode),
|
|
"PULSE_MOCK_LXCS_PER_NODE": strconv.Itoa(cfg.LXCsPerNode),
|
|
"PULSE_MOCK_DOCKER_HOSTS": strconv.Itoa(cfg.DockerHostCount),
|
|
"PULSE_MOCK_DOCKER_CONTAINERS": strconv.Itoa(cfg.DockerContainersPerHost),
|
|
"PULSE_MOCK_GENERIC_HOSTS": strconv.Itoa(cfg.GenericHostCount),
|
|
"PULSE_MOCK_K8S_CLUSTERS": strconv.Itoa(cfg.K8sClusterCount),
|
|
"PULSE_MOCK_K8S_NODES": strconv.Itoa(cfg.K8sNodesPerCluster),
|
|
"PULSE_MOCK_K8S_PODS": strconv.Itoa(cfg.K8sPodsPerCluster),
|
|
"PULSE_MOCK_K8S_DEPLOYMENTS": strconv.Itoa(cfg.K8sDeploymentsPerCluster),
|
|
"PULSE_MOCK_RANDOM_METRICS": strconv.FormatBool(cfg.RandomMetrics),
|
|
"PULSE_MOCK_STOPPED_PERCENT": strconv.FormatFloat(cfg.StoppedPercent*100, 'f', -1, 64),
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
func writeMockEnv(envPath string, config map[string]string) error {
|
|
var b strings.Builder
|
|
b.WriteString("# Pulse Mock Mode Configuration\n")
|
|
b.WriteString("# Enable with: pulse mock enable\n")
|
|
b.WriteString("# Disable with: pulse mock disable\n\n")
|
|
|
|
for _, key := range []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",
|
|
} {
|
|
if value, ok := config[key]; ok {
|
|
fmt.Fprintf(&b, "%s=%s\n", key, value)
|
|
}
|
|
}
|
|
|
|
for key, value := range config {
|
|
if strings.HasPrefix(key, "PULSE_MOCK_") {
|
|
continue
|
|
}
|
|
fmt.Fprintf(&b, "%s=%s\n", key, value)
|
|
}
|
|
|
|
return os.WriteFile(envPath, []byte(b.String()), 0644)
|
|
}
|