diff --git a/pkg/container/state.go b/pkg/container/state.go index 376952c9..5b4e1e7f 100644 --- a/pkg/container/state.go +++ b/pkg/container/state.go @@ -15,7 +15,6 @@ type State struct { Containers map[string]*Container `json:"containers"` mu sync.RWMutex - medium io.Medium filePath string } @@ -47,25 +46,19 @@ func DefaultLogsDir() (string, error) { } // NewState creates a new State instance. -func NewState(m io.Medium, filePath string) *State { +func NewState(filePath string) *State { return &State{ Containers: make(map[string]*Container), - medium: m, filePath: filePath, } } // LoadState loads the state from the given file path. // If the file doesn't exist, returns an empty state. -func LoadState(m io.Medium, filePath string) (*State, error) { - state := NewState(m, filePath) +func LoadState(filePath string) (*State, error) { + state := NewState(filePath) - absPath, err := filepath.Abs(filePath) - if err != nil { - return nil, err - } - - content, err := m.Read(absPath) + dataStr, err := io.Local.Read(filePath) if err != nil { if os.IsNotExist(err) { return state, nil @@ -73,7 +66,7 @@ func LoadState(m io.Medium, filePath string) (*State, error) { return nil, err } - if err := json.Unmarshal([]byte(content), state); err != nil { + if err := json.Unmarshal([]byte(dataStr), state); err != nil { return nil, err } @@ -85,8 +78,9 @@ func (s *State) SaveState() error { s.mu.RLock() defer s.mu.RUnlock() - absPath, err := filepath.Abs(s.filePath) - if err != nil { + // Ensure the directory exists + dir := filepath.Dir(s.filePath) + if err := io.Local.EnsureDir(dir); err != nil { return err } @@ -95,8 +89,7 @@ func (s *State) SaveState() error { return err } - // s.medium.Write creates parent directories automatically - return s.medium.Write(absPath, string(data)) + return io.Local.Write(s.filePath, string(data)) } // Add adds a container to the state and persists it. @@ -170,10 +163,10 @@ func LogPath(id string) (string, error) { } // EnsureLogsDir ensures the logs directory exists. -func EnsureLogsDir(m io.Medium) error { +func EnsureLogsDir() error { logsDir, err := DefaultLogsDir() if err != nil { return err } - return m.EnsureDir(logsDir) + return io.Local.EnsureDir(logsDir) } diff --git a/pkg/container/templates.go b/pkg/container/templates.go index 263337a6..80ec3005 100644 --- a/pkg/container/templates.go +++ b/pkg/container/templates.go @@ -38,52 +38,17 @@ var builtinTemplates = []Template{ }, } -// TemplateManager manages LinuxKit templates using a storage medium. -type TemplateManager struct { - medium io.Medium - workingDir string - homeDir string -} - -// NewTemplateManager creates a new TemplateManager instance. -func NewTemplateManager(m io.Medium) *TemplateManager { - tm := &TemplateManager{medium: m} - - // Default working and home directories from local system - // These can be overridden if needed. - if wd, err := os.Getwd(); err == nil { - tm.workingDir = wd - } - if home, err := os.UserHomeDir(); err == nil { - tm.homeDir = home - } - - return tm -} - -// WithWorkingDir sets the working directory for user template discovery. -func (tm *TemplateManager) WithWorkingDir(wd string) *TemplateManager { - tm.workingDir = wd - return tm -} - -// WithHomeDir sets the home directory for user template discovery. -func (tm *TemplateManager) WithHomeDir(home string) *TemplateManager { - tm.homeDir = home - return tm -} - // ListTemplates returns all available LinuxKit templates. // It combines embedded templates with any templates found in the user's // .core/linuxkit directory. -func (tm *TemplateManager) ListTemplates() []Template { +func ListTemplates() []Template { templates := make([]Template, len(builtinTemplates)) copy(templates, builtinTemplates) // Check for user templates in .core/linuxkit/ - userTemplatesDir := tm.getUserTemplatesDir() + userTemplatesDir := getUserTemplatesDir() if userTemplatesDir != "" { - userTemplates := tm.scanUserTemplates(userTemplatesDir) + userTemplates := scanUserTemplates(userTemplatesDir) templates = append(templates, userTemplates...) } @@ -92,7 +57,7 @@ func (tm *TemplateManager) ListTemplates() []Template { // GetTemplate returns the content of a template by name. // It first checks embedded templates, then user templates. -func (tm *TemplateManager) GetTemplate(name string) (string, error) { +func GetTemplate(name string) (string, error) { // Check embedded templates first for _, t := range builtinTemplates { if t.Name == name { @@ -105,18 +70,15 @@ func (tm *TemplateManager) GetTemplate(name string) (string, error) { } // Check user templates - userTemplatesDir := tm.getUserTemplatesDir() + userTemplatesDir := getUserTemplatesDir() if userTemplatesDir != "" { - // Check both .yml and .yaml extensions - for _, ext := range []string{".yml", ".yaml"} { - templatePath := filepath.Join(userTemplatesDir, name+ext) - if tm.medium.IsFile(templatePath) { - content, err := tm.medium.Read(templatePath) - if err != nil { - return "", fmt.Errorf("failed to read user template %s: %w", name, err) - } - return content, nil + templatePath := filepath.Join(userTemplatesDir, name+".yml") + if io.Local.IsFile(templatePath) { + content, err := io.Local.Read(templatePath) + if err != nil { + return "", fmt.Errorf("failed to read user template %s: %w", name, err) } + return content, nil } } @@ -124,8 +86,11 @@ func (tm *TemplateManager) GetTemplate(name string) (string, error) { } // ApplyTemplate applies variable substitution to a template. -func (tm *TemplateManager) ApplyTemplate(name string, vars map[string]string) (string, error) { - content, err := tm.GetTemplate(name) +// It supports two syntaxes: +// - ${VAR} - required variable, returns error if not provided +// - ${VAR:-default} - variable with default value +func ApplyTemplate(name string, vars map[string]string) (string, error) { + content, err := GetTemplate(name) if err != nil { return "", err } @@ -226,31 +191,35 @@ func ExtractVariables(content string) (required []string, optional map[string]st // getUserTemplatesDir returns the path to user templates directory. // Returns empty string if the directory doesn't exist. -func (tm *TemplateManager) getUserTemplatesDir() string { +func getUserTemplatesDir() string { // Try workspace-relative .core/linuxkit first - if tm.workingDir != "" { - wsDir := filepath.Join(tm.workingDir, ".core", "linuxkit") - if tm.medium.IsDir(wsDir) { + cwd, err := os.Getwd() + if err == nil { + wsDir := filepath.Join(cwd, ".core", "linuxkit") + if io.Local.IsDir(wsDir) { return wsDir } } // Try home directory - if tm.homeDir != "" { - homeDir := filepath.Join(tm.homeDir, ".core", "linuxkit") - if tm.medium.IsDir(homeDir) { - return homeDir - } + home, err := os.UserHomeDir() + if err != nil { + return "" + } + + homeDir := filepath.Join(home, ".core", "linuxkit") + if io.Local.IsDir(homeDir) { + return homeDir } return "" } // scanUserTemplates scans a directory for .yml template files. -func (tm *TemplateManager) scanUserTemplates(dir string) []Template { +func scanUserTemplates(dir string) []Template { var templates []Template - entries, err := tm.medium.List(dir) + entries, err := io.Local.List(dir) if err != nil { return templates } @@ -281,7 +250,7 @@ func (tm *TemplateManager) scanUserTemplates(dir string) []Template { } // Read file to extract description from comments - description := tm.extractTemplateDescription(filepath.Join(dir, name)) + description := extractTemplateDescription(filepath.Join(dir, name)) if description == "" { description = "User-defined template" } @@ -298,8 +267,8 @@ func (tm *TemplateManager) scanUserTemplates(dir string) []Template { // extractTemplateDescription reads the first comment block from a YAML file // to use as a description. -func (tm *TemplateManager) extractTemplateDescription(path string) string { - content, err := tm.medium.Read(path) +func extractTemplateDescription(path string) string { + content, err := io.Local.Read(path) if err != nil { return "" }