Dev env setup.
This commit is contained in:
38
.vscode/launch.json
vendored
Normal file
38
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Daemon (debug)",
|
||||||
|
"type": "go",
|
||||||
|
"request": "launch",
|
||||||
|
"mode": "debug",
|
||||||
|
"program": "${workspaceFolder}",
|
||||||
|
"console": "integratedTerminal",
|
||||||
|
"env": {
|
||||||
|
"GO_ENV": "development",
|
||||||
|
"WILD_CENTRAL_ENV": "development",
|
||||||
|
"DEBUG": "true"
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"stopOnEntry": false,
|
||||||
|
"showLog": true,
|
||||||
|
"trace": "verbose"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Daemon (no debug)",
|
||||||
|
"type": "go",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceFolder}",
|
||||||
|
"console": "integratedTerminal",
|
||||||
|
"env": {
|
||||||
|
"GO_ENV": "development",
|
||||||
|
"WILD_CENTRAL_ENV": "development"
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"stopOnEntry": false,
|
||||||
|
"showLog": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
23
.vscode/settings.json
vendored
Normal file
23
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"go.gopath": "",
|
||||||
|
"go.goroot": "",
|
||||||
|
"go.useLanguageServer": true,
|
||||||
|
|
||||||
|
"gopls": {
|
||||||
|
"experimentalWorkspaceModule": true,
|
||||||
|
"build.experimentalWorkspaceModule": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"go.lintTool": "golangci-lint",
|
||||||
|
"go.lintOnSave": "workspace",
|
||||||
|
"go.formatTool": "goimports",
|
||||||
|
"go.testOnSave": false,
|
||||||
|
|
||||||
|
"[go]": {
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.organizeImports": "explicit"
|
||||||
|
},
|
||||||
|
"editor.suggest.snippetsPreventQuickSuggestions": false
|
||||||
|
}
|
||||||
|
}
|
||||||
73
Makefile
Normal file
73
Makefile
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
.PHONY: help build dev test clean install lint fmt vet
|
||||||
|
|
||||||
|
# Binary name
|
||||||
|
BINARY_NAME=wildd
|
||||||
|
BUILD_DIR=build
|
||||||
|
|
||||||
|
# Go parameters
|
||||||
|
GOCMD=go
|
||||||
|
GOBUILD=$(GOCMD) build
|
||||||
|
GOCLEAN=$(GOCMD) clean
|
||||||
|
GOTEST=$(GOCMD) test
|
||||||
|
GOGET=$(GOCMD) get
|
||||||
|
GOMOD=$(GOCMD) mod
|
||||||
|
GOFMT=$(GOCMD) fmt
|
||||||
|
GOVET=$(GOCMD) vet
|
||||||
|
|
||||||
|
# Build flags
|
||||||
|
LDFLAGS=-ldflags "-s -w"
|
||||||
|
|
||||||
|
help: ## Show this help message
|
||||||
|
@echo 'Usage: make [target]'
|
||||||
|
@echo ''
|
||||||
|
@echo 'Available targets:'
|
||||||
|
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf " %-15s %s\n", $$1, $$2}' $(MAKEFILE_LIST)
|
||||||
|
|
||||||
|
build: ## Build the daemon binary
|
||||||
|
@echo "Building $(BINARY_NAME)..."
|
||||||
|
@mkdir -p $(BUILD_DIR)
|
||||||
|
$(GOBUILD) $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME) .
|
||||||
|
|
||||||
|
dev: ## Run the daemon in development mode with live reloading
|
||||||
|
@echo "Starting $(BINARY_NAME) in development mode..."
|
||||||
|
$(GOCMD) run .
|
||||||
|
|
||||||
|
test: ## Run tests
|
||||||
|
@echo "Running tests..."
|
||||||
|
$(GOTEST) -v ./...
|
||||||
|
|
||||||
|
test-cover: ## Run tests with coverage
|
||||||
|
@echo "Running tests with coverage..."
|
||||||
|
$(GOTEST) -v -coverprofile=coverage.out ./...
|
||||||
|
$(GOCMD) tool cover -html=coverage.out -o coverage.html
|
||||||
|
@echo "Coverage report generated: coverage.html"
|
||||||
|
|
||||||
|
clean: ## Clean build artifacts
|
||||||
|
@echo "Cleaning..."
|
||||||
|
$(GOCLEAN)
|
||||||
|
@rm -rf $(BUILD_DIR)
|
||||||
|
@rm -f coverage.out coverage.html
|
||||||
|
|
||||||
|
install: build ## Install the binary to $(go env GOPATH)/bin
|
||||||
|
@echo "Installing $(BINARY_NAME)..."
|
||||||
|
@mkdir -p $$($(GOCMD) env GOPATH)/bin
|
||||||
|
@cp $(BUILD_DIR)/$(BINARY_NAME) $$($(GOCMD) env GOPATH)/bin/
|
||||||
|
|
||||||
|
deps: ## Download dependencies
|
||||||
|
@echo "Downloading dependencies..."
|
||||||
|
$(GOMOD) download
|
||||||
|
$(GOMOD) tidy
|
||||||
|
|
||||||
|
fmt: ## Format Go code
|
||||||
|
@echo "Formatting code..."
|
||||||
|
$(GOFMT) ./...
|
||||||
|
|
||||||
|
vet: ## Run go vet
|
||||||
|
@echo "Running go vet..."
|
||||||
|
$(GOVET) ./...
|
||||||
|
|
||||||
|
lint: fmt vet ## Run formatters and linters
|
||||||
|
|
||||||
|
check: lint test ## Run all checks (lint + test)
|
||||||
|
|
||||||
|
all: clean lint test build ## Clean, lint, test, and build
|
||||||
@@ -485,7 +485,7 @@ func (m *Manager) GetStatus(instanceName, appName string) (*DeployedApp, error)
|
|||||||
var podList struct {
|
var podList struct {
|
||||||
Items []struct {
|
Items []struct {
|
||||||
Status struct {
|
Status struct {
|
||||||
Phase string `json:"phase"`
|
Phase string `json:"phase"`
|
||||||
ContainerStatuses []struct {
|
ContainerStatuses []struct {
|
||||||
Ready bool `json:"ready"`
|
Ready bool `json:"ready"`
|
||||||
} `json:"containerStatuses"`
|
} `json:"containerStatuses"`
|
||||||
|
|||||||
@@ -28,10 +28,10 @@ type BackupInfo struct {
|
|||||||
|
|
||||||
// RestoreOptions configures restore behavior
|
// RestoreOptions configures restore behavior
|
||||||
type RestoreOptions struct {
|
type RestoreOptions struct {
|
||||||
DBOnly bool `json:"db_only"`
|
DBOnly bool `json:"db_only"`
|
||||||
PVCOnly bool `json:"pvc_only"`
|
PVCOnly bool `json:"pvc_only"`
|
||||||
SkipGlobals bool `json:"skip_globals"`
|
SkipGlobals bool `json:"skip_globals"`
|
||||||
SnapshotID string `json:"snapshot_id,omitempty"`
|
SnapshotID string `json:"snapshot_id,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manager handles backup and restore operations
|
// Manager handles backup and restore operations
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ import (
|
|||||||
|
|
||||||
// Manager handles node discovery operations
|
// Manager handles node discovery operations
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
dataDir string
|
dataDir string
|
||||||
nodeMgr *node.Manager
|
nodeMgr *node.Manager
|
||||||
talosctl *tools.Talosctl
|
talosctl *tools.Talosctl
|
||||||
discoveryMu sync.Mutex
|
discoveryMu sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,20 +35,20 @@ func NewManager(dataDir string, instanceName string) *Manager {
|
|||||||
|
|
||||||
// DiscoveredNode represents a discovered node on the network
|
// DiscoveredNode represents a discovered node on the network
|
||||||
type DiscoveredNode struct {
|
type DiscoveredNode struct {
|
||||||
IP string `json:"ip"`
|
IP string `json:"ip"`
|
||||||
Hostname string `json:"hostname,omitempty"`
|
Hostname string `json:"hostname,omitempty"`
|
||||||
MaintenanceMode bool `json:"maintenance_mode"`
|
MaintenanceMode bool `json:"maintenance_mode"`
|
||||||
Version string `json:"version,omitempty"`
|
Version string `json:"version,omitempty"`
|
||||||
Interface string `json:"interface,omitempty"`
|
Interface string `json:"interface,omitempty"`
|
||||||
Disks []string `json:"disks,omitempty"`
|
Disks []string `json:"disks,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DiscoveryStatus represents the current state of discovery
|
// DiscoveryStatus represents the current state of discovery
|
||||||
type DiscoveryStatus struct {
|
type DiscoveryStatus struct {
|
||||||
Active bool `json:"active"`
|
Active bool `json:"active"`
|
||||||
StartedAt time.Time `json:"started_at,omitempty"`
|
StartedAt time.Time `json:"started_at,omitempty"`
|
||||||
NodesFound []DiscoveredNode `json:"nodes_found"`
|
NodesFound []DiscoveredNode `json:"nodes_found"`
|
||||||
Error string `json:"error,omitempty"`
|
Error string `json:"error,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDiscoveryDir returns the discovery directory for an instance
|
// GetDiscoveryDir returns the discovery directory for an instance
|
||||||
|
|||||||
@@ -13,27 +13,27 @@ import (
|
|||||||
|
|
||||||
// Manager handles instance lifecycle operations
|
// Manager handles instance lifecycle operations
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
dataDir string
|
dataDir string
|
||||||
configMgr *config.Manager
|
configMgr *config.Manager
|
||||||
secretsMgr *secrets.Manager
|
secretsMgr *secrets.Manager
|
||||||
contextMgr *context.Manager
|
contextMgr *context.Manager
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewManager creates a new instance manager
|
// NewManager creates a new instance manager
|
||||||
func NewManager(dataDir string) *Manager {
|
func NewManager(dataDir string) *Manager {
|
||||||
return &Manager{
|
return &Manager{
|
||||||
dataDir: dataDir,
|
dataDir: dataDir,
|
||||||
configMgr: config.NewManager(),
|
configMgr: config.NewManager(),
|
||||||
secretsMgr: secrets.NewManager(),
|
secretsMgr: secrets.NewManager(),
|
||||||
contextMgr: context.NewManager(dataDir),
|
contextMgr: context.NewManager(dataDir),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Instance represents a Wild Cloud instance
|
// Instance represents a Wild Cloud instance
|
||||||
type Instance struct {
|
type Instance struct {
|
||||||
Name string
|
Name string
|
||||||
Path string
|
Path string
|
||||||
ConfigPath string
|
ConfigPath string
|
||||||
SecretsPath string
|
SecretsPath string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ import (
|
|||||||
|
|
||||||
// Manager handles node configuration and state management
|
// Manager handles node configuration and state management
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
dataDir string
|
dataDir string
|
||||||
configMgr *config.Manager
|
configMgr *config.Manager
|
||||||
talosctl *tools.Talosctl
|
talosctl *tools.Talosctl
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewManager creates a new node manager
|
// NewManager creates a new node manager
|
||||||
@@ -407,8 +407,8 @@ func (m *Manager) Apply(instanceName, nodeIdentifier string, opts ApplyOptions)
|
|||||||
|
|
||||||
// Post-application updates: move to production IP, exit maintenance mode
|
// Post-application updates: move to production IP, exit maintenance mode
|
||||||
node.Applied = true
|
node.Applied = true
|
||||||
node.CurrentIP = node.TargetIP // Node now on production IP
|
node.CurrentIP = node.TargetIP // Node now on production IP
|
||||||
node.Maintenance = false // Exit maintenance mode
|
node.Maintenance = false // Exit maintenance mode
|
||||||
if err := m.updateNodeStatus(instanceName, node); err != nil {
|
if err := m.updateNodeStatus(instanceName, node); err != nil {
|
||||||
return fmt.Errorf("failed to update node status: %w", err)
|
return fmt.Errorf("failed to update node status: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ type Operation struct {
|
|||||||
Instance string `json:"instance"`
|
Instance string `json:"instance"`
|
||||||
Status string `json:"status"` // pending, running, completed, failed, cancelled
|
Status string `json:"status"` // pending, running, completed, failed, cancelled
|
||||||
Message string `json:"message,omitempty"`
|
Message string `json:"message,omitempty"`
|
||||||
Progress int `json:"progress"` // 0-100
|
Progress int `json:"progress"` // 0-100
|
||||||
LogFile string `json:"logFile,omitempty"` // Path to output log file
|
LogFile string `json:"logFile,omitempty"` // Path to output log file
|
||||||
StartedAt time.Time `json:"started_at"`
|
StartedAt time.Time `json:"started_at"`
|
||||||
EndedAt time.Time `json:"ended_at,omitempty"`
|
EndedAt time.Time `json:"ended_at,omitempty"`
|
||||||
|
|||||||
@@ -95,6 +95,11 @@ func (m *Manager) GetSecret(secretsPath, key string) (string, error) {
|
|||||||
return "", fmt.Errorf("getting secret %s: %w", key, err)
|
return "", fmt.Errorf("getting secret %s: %w", key, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// yq returns "null" for non-existent keys
|
||||||
|
if value == "" || value == "null" {
|
||||||
|
return "", fmt.Errorf("secret not found: %s", key)
|
||||||
|
}
|
||||||
|
|
||||||
return value, nil
|
return value, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,9 +24,9 @@ type ServiceManifest struct {
|
|||||||
|
|
||||||
// ConfigDefinition defines config that should be prompted during service setup
|
// ConfigDefinition defines config that should be prompted during service setup
|
||||||
type ConfigDefinition struct {
|
type ConfigDefinition struct {
|
||||||
Path string `yaml:"path" json:"path"` // Config path to set
|
Path string `yaml:"path" json:"path"` // Config path to set
|
||||||
Prompt string `yaml:"prompt" json:"prompt"` // User prompt text
|
Prompt string `yaml:"prompt" json:"prompt"` // User prompt text
|
||||||
Default string `yaml:"default" json:"default"` // Default value (supports templates)
|
Default string `yaml:"default" json:"default"` // Default value (supports templates)
|
||||||
Type string `yaml:"type,omitempty" json:"type,omitempty"` // Value type: string|int|bool (default: string)
|
Type string `yaml:"type,omitempty" json:"type,omitempty"` // Value type: string|int|bool (default: string)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,11 +42,11 @@ func NewManager(dataDir, servicesDir string) *Manager {
|
|||||||
|
|
||||||
// Service represents a base service
|
// Service represents a base service
|
||||||
type Service struct {
|
type Service struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
Namespace string `json:"namespace"`
|
Namespace string `json:"namespace"`
|
||||||
Dependencies []string `json:"dependencies,omitempty"`
|
Dependencies []string `json:"dependencies,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -557,8 +557,8 @@ func (m *Manager) Deploy(instanceName, serviceName, opID string, broadcaster *op
|
|||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
fmt.Printf("[DEBUG] Command completed for opID=%s, err=%v\n", opID, err)
|
fmt.Printf("[DEBUG] Command completed for opID=%s, err=%v\n", opID, err)
|
||||||
if broadcaster != nil {
|
if broadcaster != nil {
|
||||||
outputWriter.Flush() // Flush any remaining buffered data
|
outputWriter.Flush() // Flush any remaining buffered data
|
||||||
broadcaster.Close(opID) // Close all SSE clients
|
broadcaster.Close(opID) // Close all SSE clients
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ import (
|
|||||||
|
|
||||||
// HealthStatus represents cluster health information
|
// HealthStatus represents cluster health information
|
||||||
type HealthStatus struct {
|
type HealthStatus struct {
|
||||||
Overall string `json:"overall"` // healthy, degraded, unhealthy
|
Overall string `json:"overall"` // healthy, degraded, unhealthy
|
||||||
Components map[string]string `json:"components"` // component -> status
|
Components map[string]string `json:"components"` // component -> status
|
||||||
Issues []string `json:"issues"`
|
Issues []string `json:"issues"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DashboardToken represents a Kubernetes dashboard token
|
// DashboardToken represents a Kubernetes dashboard token
|
||||||
@@ -96,7 +96,7 @@ func checkComponent(kubeconfigPath, name, namespace, selector string) error {
|
|||||||
var result struct {
|
var result struct {
|
||||||
Items []struct {
|
Items []struct {
|
||||||
Status struct {
|
Status struct {
|
||||||
Phase string `json:"phase"`
|
Phase string `json:"phase"`
|
||||||
ContainerStatuses []struct {
|
ContainerStatuses []struct {
|
||||||
Ready bool `json:"ready"`
|
Ready bool `json:"ready"`
|
||||||
} `json:"containerStatuses"`
|
} `json:"containerStatuses"`
|
||||||
|
|||||||
Reference in New Issue
Block a user