Merge branch 'fix/io-migration-container' into new

# Conflicts:
#	pkg/container/state.go
#	pkg/container/templates.go
This commit is contained in:
Snider 2026-02-08 21:28:36 +00:00
commit 15d5aa0fbd
2 changed files with 45 additions and 83 deletions

View file

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

View file

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