Dev env setup.

This commit is contained in:
2025-10-11 21:40:45 +00:00
parent b76d2fb164
commit 92032202f4
13 changed files with 186 additions and 47 deletions

38
.vscode/launch.json vendored Normal file
View 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
View 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
View 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

View File

@@ -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"`

View File

@@ -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

View File

@@ -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

View File

@@ -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
} }

View File

@@ -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)
} }

View File

@@ -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"`

View File

@@ -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
} }

View File

@@ -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)
} }

View File

@@ -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 {

View File

@@ -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"`