256 lines
7.0 KiB
Go
256 lines
7.0 KiB
Go
package instance
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/wild-cloud/wild-central/daemon/internal/config"
|
|
"github.com/wild-cloud/wild-central/daemon/internal/context"
|
|
"github.com/wild-cloud/wild-central/daemon/internal/secrets"
|
|
"github.com/wild-cloud/wild-central/daemon/internal/storage"
|
|
"github.com/wild-cloud/wild-central/daemon/internal/tools"
|
|
)
|
|
|
|
// Manager handles instance lifecycle operations
|
|
type Manager struct {
|
|
dataDir string
|
|
configMgr *config.Manager
|
|
secretsMgr *secrets.Manager
|
|
contextMgr *context.Manager
|
|
}
|
|
|
|
// NewManager creates a new instance manager
|
|
func NewManager(dataDir string) *Manager {
|
|
return &Manager{
|
|
dataDir: dataDir,
|
|
configMgr: config.NewManager(),
|
|
secretsMgr: secrets.NewManager(),
|
|
contextMgr: context.NewManager(dataDir),
|
|
}
|
|
}
|
|
|
|
// Instance represents a Wild Cloud instance
|
|
type Instance struct {
|
|
Name string
|
|
Path string
|
|
ConfigPath string
|
|
SecretsPath string
|
|
}
|
|
|
|
// GetInstancePath returns the path to an instance directory
|
|
// Deprecated: Use tools.GetInstancePath instead
|
|
func (m *Manager) GetInstancePath(name string) string {
|
|
return tools.GetInstancePath(m.dataDir, name)
|
|
}
|
|
|
|
// GetInstanceConfigPath returns the path to an instance's config file
|
|
// Deprecated: Use tools.GetInstanceConfigPath instead
|
|
func (m *Manager) GetInstanceConfigPath(name string) string {
|
|
return tools.GetInstanceConfigPath(m.dataDir, name)
|
|
}
|
|
|
|
// GetInstanceSecretsPath returns the path to an instance's secrets file
|
|
// Deprecated: Use tools.GetInstanceSecretsPath instead
|
|
func (m *Manager) GetInstanceSecretsPath(name string) string {
|
|
return tools.GetInstanceSecretsPath(m.dataDir, name)
|
|
}
|
|
|
|
// InstanceExists checks if an instance exists
|
|
func (m *Manager) InstanceExists(name string) bool {
|
|
return storage.FileExists(m.GetInstancePath(name))
|
|
}
|
|
|
|
// CreateInstance creates a new Wild Cloud instance with initial structure
|
|
func (m *Manager) CreateInstance(name string) error {
|
|
if name == "" {
|
|
return fmt.Errorf("instance name cannot be empty")
|
|
}
|
|
|
|
instancePath := m.GetInstancePath(name)
|
|
|
|
// Check if instance already exists (idempotency - just return success)
|
|
if m.InstanceExists(name) {
|
|
return nil
|
|
}
|
|
|
|
// Acquire lock for instance creation
|
|
lockPath := tools.GetInstancesLockPath(m.dataDir)
|
|
return storage.WithLock(lockPath, func() error {
|
|
// Create instance directory
|
|
if err := storage.EnsureDir(instancePath, 0755); err != nil {
|
|
return fmt.Errorf("creating instance directory: %w", err)
|
|
}
|
|
|
|
// Create config file
|
|
if err := m.configMgr.EnsureInstanceConfig(instancePath); err != nil {
|
|
return fmt.Errorf("creating config file: %w", err)
|
|
}
|
|
|
|
// Create secrets file
|
|
if err := m.secretsMgr.EnsureSecretsFile(instancePath); err != nil {
|
|
return fmt.Errorf("creating secrets file: %w", err)
|
|
}
|
|
|
|
// Create subdirectories
|
|
subdirs := []string{"talos", "k8s", "logs", "backups"}
|
|
for _, subdir := range subdirs {
|
|
subdirPath := filepath.Join(instancePath, subdir)
|
|
if err := storage.EnsureDir(subdirPath, 0755); err != nil {
|
|
return fmt.Errorf("creating subdirectory %s: %w", subdir, err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
// DeleteInstance removes a Wild Cloud instance
|
|
func (m *Manager) DeleteInstance(name string) error {
|
|
if name == "" {
|
|
return fmt.Errorf("instance name cannot be empty")
|
|
}
|
|
|
|
instancePath := m.GetInstancePath(name)
|
|
|
|
// Check if instance exists
|
|
if !m.InstanceExists(name) {
|
|
return fmt.Errorf("instance %s does not exist", name)
|
|
}
|
|
|
|
// Clear context if this is the current instance
|
|
currentContext, err := m.contextMgr.GetCurrentContext()
|
|
if err == nil && currentContext == name {
|
|
if err := m.contextMgr.ClearCurrentContext(); err != nil {
|
|
return fmt.Errorf("clearing current context: %w", err)
|
|
}
|
|
}
|
|
|
|
// Acquire lock for instance deletion
|
|
lockPath := tools.GetInstancesLockPath(m.dataDir)
|
|
return storage.WithLock(lockPath, func() error {
|
|
// Remove instance directory
|
|
if err := os.RemoveAll(instancePath); err != nil {
|
|
return fmt.Errorf("removing instance directory: %w", err)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
// ListInstances returns a list of all instance names
|
|
func (m *Manager) ListInstances() ([]string, error) {
|
|
instancesDir := tools.GetInstancesPath(m.dataDir)
|
|
|
|
// Ensure instances directory exists
|
|
if !storage.FileExists(instancesDir) {
|
|
return []string{}, nil
|
|
}
|
|
|
|
entries, err := os.ReadDir(instancesDir)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("reading instances directory: %w", err)
|
|
}
|
|
|
|
var instances []string
|
|
for _, entry := range entries {
|
|
if entry.IsDir() && entry.Name() != ".lock" {
|
|
instances = append(instances, entry.Name())
|
|
}
|
|
}
|
|
|
|
return instances, nil
|
|
}
|
|
|
|
// GetInstance retrieves instance information
|
|
func (m *Manager) GetInstance(name string) (*Instance, error) {
|
|
if !m.InstanceExists(name) {
|
|
return nil, fmt.Errorf("instance %s does not exist", name)
|
|
}
|
|
|
|
return &Instance{
|
|
Name: name,
|
|
Path: m.GetInstancePath(name),
|
|
ConfigPath: m.GetInstanceConfigPath(name),
|
|
SecretsPath: m.GetInstanceSecretsPath(name),
|
|
}, nil
|
|
}
|
|
|
|
// GetCurrentInstance returns the current context instance
|
|
func (m *Manager) GetCurrentInstance() (*Instance, error) {
|
|
name, err := m.contextMgr.GetCurrentContext()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return m.GetInstance(name)
|
|
}
|
|
|
|
// SetCurrentInstance sets the current instance context
|
|
func (m *Manager) SetCurrentInstance(name string) error {
|
|
if !m.InstanceExists(name) {
|
|
return fmt.Errorf("instance %s does not exist", name)
|
|
}
|
|
|
|
return m.contextMgr.SetCurrentContext(name)
|
|
}
|
|
|
|
// ValidateInstance checks if an instance has valid structure
|
|
func (m *Manager) ValidateInstance(name string) error {
|
|
if !m.InstanceExists(name) {
|
|
return fmt.Errorf("instance %s does not exist", name)
|
|
}
|
|
|
|
instance, err := m.GetInstance(name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Check config file exists and is valid
|
|
if !storage.FileExists(instance.ConfigPath) {
|
|
return fmt.Errorf("config file missing for instance %s", name)
|
|
}
|
|
|
|
if err := m.configMgr.ValidateConfig(instance.ConfigPath); err != nil {
|
|
return fmt.Errorf("invalid config for instance %s: %w", name, err)
|
|
}
|
|
|
|
// Check secrets file exists with proper permissions
|
|
if !storage.FileExists(instance.SecretsPath) {
|
|
return fmt.Errorf("secrets file missing for instance %s", name)
|
|
}
|
|
|
|
// Verify secrets file permissions
|
|
info, err := os.Stat(instance.SecretsPath)
|
|
if err != nil {
|
|
return fmt.Errorf("checking secrets file permissions: %w", err)
|
|
}
|
|
|
|
if info.Mode().Perm() != 0600 {
|
|
return fmt.Errorf("secrets file has incorrect permissions (expected 0600, got %04o)", info.Mode().Perm())
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// InitializeInstance performs initial setup for a newly created instance
|
|
func (m *Manager) InitializeInstance(name string, initialConfig map[string]string) error {
|
|
if !m.InstanceExists(name) {
|
|
return fmt.Errorf("instance %s does not exist", name)
|
|
}
|
|
|
|
instance, err := m.GetInstance(name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Set initial config values
|
|
for key, value := range initialConfig {
|
|
if err := m.configMgr.SetConfigValue(instance.ConfigPath, key, value); err != nil {
|
|
return fmt.Errorf("setting config value %s: %w", key, err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|