cli/internal/cmd/vm/cmd_templates.go

314 lines
8.6 KiB
Go
Raw Normal View History

package vm
import (
"context"
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"text/tabwriter"
"github.com/host-uk/core/pkg/container"
"github.com/host-uk/core/pkg/i18n"
Migrate pkg/container to io.Medium abstraction (#292) * chore(io): migrate pkg/container to Medium abstraction Migrated State, Templates, and LinuxKitManager in pkg/container to use the io.Medium abstraction for storage operations. - Introduced TemplateManager struct to handle template logic with injected medium. - Updated State struct to use injected medium for persistence. - Updated LinuxKitManager to hold and use an io.Medium instance. - Updated all internal callers in internal/cmd/vm and pkg/devops to use new APIs. - Adapted and maintained comprehensive test coverage in linuxkit_test.go. - Fixed naming collision with standard io package by aliasing it as goio. * chore(io): migrate pkg/container to Medium abstraction (v2) - Migrated State, Templates, and LinuxKitManager in pkg/container to use io.Medium. - Introduced TemplateManager struct for dependency injection. - Updated all call sites in internal/cmd/vm and pkg/devops. - Restored and adapted comprehensive test suite in linuxkit_test.go. - Fixed naming collisions and followed project test naming conventions. * chore(io): address PR feedback for container Medium migration - Added Open method to io.Medium interface to support log streaming. - Implemented Open in local.Medium and MockMedium. - Fixed extension inconsistency in GetTemplate (.yml vs .yaml). - Refactored TemplateManager to use configurable WorkingDir and HomeDir. - Reused TemplateManager instance in cmd_templates.go. - Updated LinuxKitManager to use medium.Open for log access. - Maintained and updated all tests to verify these improvements.
2026-02-04 15:33:22 +00:00
"github.com/host-uk/core/pkg/io"
"github.com/spf13/cobra"
)
Migrate pkg/container to io.Medium abstraction (#292) * chore(io): migrate pkg/container to Medium abstraction Migrated State, Templates, and LinuxKitManager in pkg/container to use the io.Medium abstraction for storage operations. - Introduced TemplateManager struct to handle template logic with injected medium. - Updated State struct to use injected medium for persistence. - Updated LinuxKitManager to hold and use an io.Medium instance. - Updated all internal callers in internal/cmd/vm and pkg/devops to use new APIs. - Adapted and maintained comprehensive test coverage in linuxkit_test.go. - Fixed naming collision with standard io package by aliasing it as goio. * chore(io): migrate pkg/container to Medium abstraction (v2) - Migrated State, Templates, and LinuxKitManager in pkg/container to use io.Medium. - Introduced TemplateManager struct for dependency injection. - Updated all call sites in internal/cmd/vm and pkg/devops. - Restored and adapted comprehensive test suite in linuxkit_test.go. - Fixed naming collisions and followed project test naming conventions. * chore(io): address PR feedback for container Medium migration - Added Open method to io.Medium interface to support log streaming. - Implemented Open in local.Medium and MockMedium. - Fixed extension inconsistency in GetTemplate (.yml vs .yaml). - Refactored TemplateManager to use configurable WorkingDir and HomeDir. - Reused TemplateManager instance in cmd_templates.go. - Updated LinuxKitManager to use medium.Open for log access. - Maintained and updated all tests to verify these improvements.
2026-02-04 15:33:22 +00:00
var templateManager = container.NewTemplateManager(io.Local)
// addVMTemplatesCommand adds the 'templates' command under vm.
func addVMTemplatesCommand(parent *cobra.Command) {
templatesCmd := &cobra.Command{
Use: "templates",
Short: i18n.T("cmd.vm.templates.short"),
Long: i18n.T("cmd.vm.templates.long"),
RunE: func(cmd *cobra.Command, args []string) error {
return listTemplates()
},
}
// Add subcommands
addTemplatesShowCommand(templatesCmd)
addTemplatesVarsCommand(templatesCmd)
parent.AddCommand(templatesCmd)
}
// addTemplatesShowCommand adds the 'templates show' subcommand.
func addTemplatesShowCommand(parent *cobra.Command) {
showCmd := &cobra.Command{
Use: "show <template-name>",
Short: i18n.T("cmd.vm.templates.show.short"),
Long: i18n.T("cmd.vm.templates.show.long"),
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return errors.New(i18n.T("cmd.vm.error.template_required"))
}
return showTemplate(args[0])
},
}
parent.AddCommand(showCmd)
}
// addTemplatesVarsCommand adds the 'templates vars' subcommand.
func addTemplatesVarsCommand(parent *cobra.Command) {
varsCmd := &cobra.Command{
Use: "vars <template-name>",
Short: i18n.T("cmd.vm.templates.vars.short"),
Long: i18n.T("cmd.vm.templates.vars.long"),
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return errors.New(i18n.T("cmd.vm.error.template_required"))
}
return showTemplateVars(args[0])
},
}
parent.AddCommand(varsCmd)
}
func listTemplates() error {
Migrate pkg/container to io.Medium abstraction (#292) * chore(io): migrate pkg/container to Medium abstraction Migrated State, Templates, and LinuxKitManager in pkg/container to use the io.Medium abstraction for storage operations. - Introduced TemplateManager struct to handle template logic with injected medium. - Updated State struct to use injected medium for persistence. - Updated LinuxKitManager to hold and use an io.Medium instance. - Updated all internal callers in internal/cmd/vm and pkg/devops to use new APIs. - Adapted and maintained comprehensive test coverage in linuxkit_test.go. - Fixed naming collision with standard io package by aliasing it as goio. * chore(io): migrate pkg/container to Medium abstraction (v2) - Migrated State, Templates, and LinuxKitManager in pkg/container to use io.Medium. - Introduced TemplateManager struct for dependency injection. - Updated all call sites in internal/cmd/vm and pkg/devops. - Restored and adapted comprehensive test suite in linuxkit_test.go. - Fixed naming collisions and followed project test naming conventions. * chore(io): address PR feedback for container Medium migration - Added Open method to io.Medium interface to support log streaming. - Implemented Open in local.Medium and MockMedium. - Fixed extension inconsistency in GetTemplate (.yml vs .yaml). - Refactored TemplateManager to use configurable WorkingDir and HomeDir. - Reused TemplateManager instance in cmd_templates.go. - Updated LinuxKitManager to use medium.Open for log access. - Maintained and updated all tests to verify these improvements.
2026-02-04 15:33:22 +00:00
templates := templateManager.ListTemplates()
if len(templates) == 0 {
fmt.Println(i18n.T("cmd.vm.templates.no_templates"))
return nil
}
fmt.Printf("%s\n\n", repoNameStyle.Render(i18n.T("cmd.vm.templates.title")))
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
feat: infrastructure packages and lint cleanup (#281) * ci: consolidate duplicate workflows and merge CodeQL configs Remove 17 duplicate workflow files that were split copies of the combined originals. Each family (CI, CodeQL, Coverage, PR Build, Alpha Release) had the same job duplicated across separate push/pull_request/schedule/manual trigger files. Merge codeql.yml and codescan.yml into a single codeql.yml with a language matrix covering go, javascript-typescript, python, and actions — matching the previous default setup coverage. Remaining workflows (one per family): - ci.yml (push + PR + manual) - codeql.yml (push + PR + schedule, all languages) - coverage.yml (push + PR + manual) - alpha-release.yml (push + manual) - pr-build.yml (PR + manual) - release.yml (tag push) - agent-verify.yml, auto-label.yml, auto-project.yml Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add collect, config, crypt, plugin packages and fix all lint issues Add four new infrastructure packages with CLI commands: - pkg/config: layered configuration (defaults → file → env → flags) - pkg/crypt: crypto primitives (Argon2id, AES-GCM, ChaCha20, HMAC, checksums) - pkg/plugin: plugin system with GitHub-based install/update/remove - pkg/collect: collection subsystem (GitHub, BitcoinTalk, market, papers, excavate) Fix all golangci-lint issues across the entire codebase (~100 errcheck, staticcheck SA1012/SA1019/ST1005, unused, ineffassign fixes) so that `core go qa` passes with 0 issues. Closes #167, #168, #170, #250, #251, #252, #253, #254, #255, #256 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 11:34:43 +00:00
_, _ = fmt.Fprintln(w, i18n.T("cmd.vm.templates.header"))
_, _ = fmt.Fprintln(w, "----\t-----------")
for _, tmpl := range templates {
desc := tmpl.Description
if len(desc) > 60 {
desc = desc[:57] + "..."
}
feat: infrastructure packages and lint cleanup (#281) * ci: consolidate duplicate workflows and merge CodeQL configs Remove 17 duplicate workflow files that were split copies of the combined originals. Each family (CI, CodeQL, Coverage, PR Build, Alpha Release) had the same job duplicated across separate push/pull_request/schedule/manual trigger files. Merge codeql.yml and codescan.yml into a single codeql.yml with a language matrix covering go, javascript-typescript, python, and actions — matching the previous default setup coverage. Remaining workflows (one per family): - ci.yml (push + PR + manual) - codeql.yml (push + PR + schedule, all languages) - coverage.yml (push + PR + manual) - alpha-release.yml (push + manual) - pr-build.yml (PR + manual) - release.yml (tag push) - agent-verify.yml, auto-label.yml, auto-project.yml Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add collect, config, crypt, plugin packages and fix all lint issues Add four new infrastructure packages with CLI commands: - pkg/config: layered configuration (defaults → file → env → flags) - pkg/crypt: crypto primitives (Argon2id, AES-GCM, ChaCha20, HMAC, checksums) - pkg/plugin: plugin system with GitHub-based install/update/remove - pkg/collect: collection subsystem (GitHub, BitcoinTalk, market, papers, excavate) Fix all golangci-lint issues across the entire codebase (~100 errcheck, staticcheck SA1012/SA1019/ST1005, unused, ineffassign fixes) so that `core go qa` passes with 0 issues. Closes #167, #168, #170, #250, #251, #252, #253, #254, #255, #256 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 11:34:43 +00:00
_, _ = fmt.Fprintf(w, "%s\t%s\n", repoNameStyle.Render(tmpl.Name), desc)
}
feat: infrastructure packages and lint cleanup (#281) * ci: consolidate duplicate workflows and merge CodeQL configs Remove 17 duplicate workflow files that were split copies of the combined originals. Each family (CI, CodeQL, Coverage, PR Build, Alpha Release) had the same job duplicated across separate push/pull_request/schedule/manual trigger files. Merge codeql.yml and codescan.yml into a single codeql.yml with a language matrix covering go, javascript-typescript, python, and actions — matching the previous default setup coverage. Remaining workflows (one per family): - ci.yml (push + PR + manual) - codeql.yml (push + PR + schedule, all languages) - coverage.yml (push + PR + manual) - alpha-release.yml (push + manual) - pr-build.yml (PR + manual) - release.yml (tag push) - agent-verify.yml, auto-label.yml, auto-project.yml Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add collect, config, crypt, plugin packages and fix all lint issues Add four new infrastructure packages with CLI commands: - pkg/config: layered configuration (defaults → file → env → flags) - pkg/crypt: crypto primitives (Argon2id, AES-GCM, ChaCha20, HMAC, checksums) - pkg/plugin: plugin system with GitHub-based install/update/remove - pkg/collect: collection subsystem (GitHub, BitcoinTalk, market, papers, excavate) Fix all golangci-lint issues across the entire codebase (~100 errcheck, staticcheck SA1012/SA1019/ST1005, unused, ineffassign fixes) so that `core go qa` passes with 0 issues. Closes #167, #168, #170, #250, #251, #252, #253, #254, #255, #256 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 11:34:43 +00:00
_ = w.Flush()
fmt.Println()
fmt.Printf("%s %s\n", i18n.T("cmd.vm.templates.hint.show"), dimStyle.Render("core vm templates show <name>"))
fmt.Printf("%s %s\n", i18n.T("cmd.vm.templates.hint.vars"), dimStyle.Render("core vm templates vars <name>"))
fmt.Printf("%s %s\n", i18n.T("cmd.vm.templates.hint.run"), dimStyle.Render("core vm run --template <name> --var SSH_KEY=\"...\""))
return nil
}
func showTemplate(name string) error {
Migrate pkg/container to io.Medium abstraction (#292) * chore(io): migrate pkg/container to Medium abstraction Migrated State, Templates, and LinuxKitManager in pkg/container to use the io.Medium abstraction for storage operations. - Introduced TemplateManager struct to handle template logic with injected medium. - Updated State struct to use injected medium for persistence. - Updated LinuxKitManager to hold and use an io.Medium instance. - Updated all internal callers in internal/cmd/vm and pkg/devops to use new APIs. - Adapted and maintained comprehensive test coverage in linuxkit_test.go. - Fixed naming collision with standard io package by aliasing it as goio. * chore(io): migrate pkg/container to Medium abstraction (v2) - Migrated State, Templates, and LinuxKitManager in pkg/container to use io.Medium. - Introduced TemplateManager struct for dependency injection. - Updated all call sites in internal/cmd/vm and pkg/devops. - Restored and adapted comprehensive test suite in linuxkit_test.go. - Fixed naming collisions and followed project test naming conventions. * chore(io): address PR feedback for container Medium migration - Added Open method to io.Medium interface to support log streaming. - Implemented Open in local.Medium and MockMedium. - Fixed extension inconsistency in GetTemplate (.yml vs .yaml). - Refactored TemplateManager to use configurable WorkingDir and HomeDir. - Reused TemplateManager instance in cmd_templates.go. - Updated LinuxKitManager to use medium.Open for log access. - Maintained and updated all tests to verify these improvements.
2026-02-04 15:33:22 +00:00
content, err := templateManager.GetTemplate(name)
if err != nil {
return err
}
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("common.label.template")), repoNameStyle.Render(name))
fmt.Println(content)
return nil
}
func showTemplateVars(name string) error {
Migrate pkg/container to io.Medium abstraction (#292) * chore(io): migrate pkg/container to Medium abstraction Migrated State, Templates, and LinuxKitManager in pkg/container to use the io.Medium abstraction for storage operations. - Introduced TemplateManager struct to handle template logic with injected medium. - Updated State struct to use injected medium for persistence. - Updated LinuxKitManager to hold and use an io.Medium instance. - Updated all internal callers in internal/cmd/vm and pkg/devops to use new APIs. - Adapted and maintained comprehensive test coverage in linuxkit_test.go. - Fixed naming collision with standard io package by aliasing it as goio. * chore(io): migrate pkg/container to Medium abstraction (v2) - Migrated State, Templates, and LinuxKitManager in pkg/container to use io.Medium. - Introduced TemplateManager struct for dependency injection. - Updated all call sites in internal/cmd/vm and pkg/devops. - Restored and adapted comprehensive test suite in linuxkit_test.go. - Fixed naming collisions and followed project test naming conventions. * chore(io): address PR feedback for container Medium migration - Added Open method to io.Medium interface to support log streaming. - Implemented Open in local.Medium and MockMedium. - Fixed extension inconsistency in GetTemplate (.yml vs .yaml). - Refactored TemplateManager to use configurable WorkingDir and HomeDir. - Reused TemplateManager instance in cmd_templates.go. - Updated LinuxKitManager to use medium.Open for log access. - Maintained and updated all tests to verify these improvements.
2026-02-04 15:33:22 +00:00
content, err := templateManager.GetTemplate(name)
if err != nil {
return err
}
required, optional := container.ExtractVariables(content)
fmt.Printf("%s %s\n\n", dimStyle.Render(i18n.T("common.label.template")), repoNameStyle.Render(name))
if len(required) > 0 {
fmt.Printf("%s\n", errorStyle.Render(i18n.T("cmd.vm.templates.vars.required")))
for _, v := range required {
fmt.Printf(" %s\n", varStyle.Render("${"+v+"}"))
}
fmt.Println()
}
if len(optional) > 0 {
fmt.Printf("%s\n", successStyle.Render(i18n.T("cmd.vm.templates.vars.optional")))
for v, def := range optional {
fmt.Printf(" %s = %s\n",
varStyle.Render("${"+v+"}"),
defaultStyle.Render(def))
}
fmt.Println()
}
if len(required) == 0 && len(optional) == 0 {
fmt.Println(i18n.T("cmd.vm.templates.vars.none"))
}
return nil
}
// RunFromTemplate builds and runs a LinuxKit image from a template.
func RunFromTemplate(templateName string, vars map[string]string, runOpts container.RunOptions) error {
// Apply template with variables
Migrate pkg/container to io.Medium abstraction (#292) * chore(io): migrate pkg/container to Medium abstraction Migrated State, Templates, and LinuxKitManager in pkg/container to use the io.Medium abstraction for storage operations. - Introduced TemplateManager struct to handle template logic with injected medium. - Updated State struct to use injected medium for persistence. - Updated LinuxKitManager to hold and use an io.Medium instance. - Updated all internal callers in internal/cmd/vm and pkg/devops to use new APIs. - Adapted and maintained comprehensive test coverage in linuxkit_test.go. - Fixed naming collision with standard io package by aliasing it as goio. * chore(io): migrate pkg/container to Medium abstraction (v2) - Migrated State, Templates, and LinuxKitManager in pkg/container to use io.Medium. - Introduced TemplateManager struct for dependency injection. - Updated all call sites in internal/cmd/vm and pkg/devops. - Restored and adapted comprehensive test suite in linuxkit_test.go. - Fixed naming collisions and followed project test naming conventions. * chore(io): address PR feedback for container Medium migration - Added Open method to io.Medium interface to support log streaming. - Implemented Open in local.Medium and MockMedium. - Fixed extension inconsistency in GetTemplate (.yml vs .yaml). - Refactored TemplateManager to use configurable WorkingDir and HomeDir. - Reused TemplateManager instance in cmd_templates.go. - Updated LinuxKitManager to use medium.Open for log access. - Maintained and updated all tests to verify these improvements.
2026-02-04 15:33:22 +00:00
content, err := templateManager.ApplyTemplate(templateName, vars)
if err != nil {
return fmt.Errorf(i18n.T("common.error.failed", map[string]any{"Action": "apply template"})+": %w", err)
}
// Create a temporary directory for the build
tmpDir, err := os.MkdirTemp("", "core-linuxkit-*")
if err != nil {
return fmt.Errorf(i18n.T("common.error.failed", map[string]any{"Action": "create temp directory"})+": %w", err)
}
feat: infrastructure packages and lint cleanup (#281) * ci: consolidate duplicate workflows and merge CodeQL configs Remove 17 duplicate workflow files that were split copies of the combined originals. Each family (CI, CodeQL, Coverage, PR Build, Alpha Release) had the same job duplicated across separate push/pull_request/schedule/manual trigger files. Merge codeql.yml and codescan.yml into a single codeql.yml with a language matrix covering go, javascript-typescript, python, and actions — matching the previous default setup coverage. Remaining workflows (one per family): - ci.yml (push + PR + manual) - codeql.yml (push + PR + schedule, all languages) - coverage.yml (push + PR + manual) - alpha-release.yml (push + manual) - pr-build.yml (PR + manual) - release.yml (tag push) - agent-verify.yml, auto-label.yml, auto-project.yml Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: add collect, config, crypt, plugin packages and fix all lint issues Add four new infrastructure packages with CLI commands: - pkg/config: layered configuration (defaults → file → env → flags) - pkg/crypt: crypto primitives (Argon2id, AES-GCM, ChaCha20, HMAC, checksums) - pkg/plugin: plugin system with GitHub-based install/update/remove - pkg/collect: collection subsystem (GitHub, BitcoinTalk, market, papers, excavate) Fix all golangci-lint issues across the entire codebase (~100 errcheck, staticcheck SA1012/SA1019/ST1005, unused, ineffassign fixes) so that `core go qa` passes with 0 issues. Closes #167, #168, #170, #250, #251, #252, #253, #254, #255, #256 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 11:34:43 +00:00
defer func() { _ = os.RemoveAll(tmpDir) }()
// Write the YAML file
yamlPath := filepath.Join(tmpDir, templateName+".yml")
if err := os.WriteFile(yamlPath, []byte(content), 0644); err != nil {
return fmt.Errorf(i18n.T("common.error.failed", map[string]any{"Action": "write template"})+": %w", err)
}
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("common.label.template")), repoNameStyle.Render(templateName))
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.vm.label.building")), yamlPath)
// Build the image using linuxkit
outputPath := filepath.Join(tmpDir, templateName)
if err := buildLinuxKitImage(yamlPath, outputPath); err != nil {
return fmt.Errorf(i18n.T("common.error.failed", map[string]any{"Action": "build image"})+": %w", err)
}
// Find the built image (linuxkit creates .iso or other format)
imagePath := findBuiltImage(outputPath)
if imagePath == "" {
return errors.New(i18n.T("cmd.vm.error.no_image_found"))
}
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("common.label.image")), imagePath)
fmt.Println()
// Run the image
Migrate pkg/container to io.Medium abstraction (#292) * chore(io): migrate pkg/container to Medium abstraction Migrated State, Templates, and LinuxKitManager in pkg/container to use the io.Medium abstraction for storage operations. - Introduced TemplateManager struct to handle template logic with injected medium. - Updated State struct to use injected medium for persistence. - Updated LinuxKitManager to hold and use an io.Medium instance. - Updated all internal callers in internal/cmd/vm and pkg/devops to use new APIs. - Adapted and maintained comprehensive test coverage in linuxkit_test.go. - Fixed naming collision with standard io package by aliasing it as goio. * chore(io): migrate pkg/container to Medium abstraction (v2) - Migrated State, Templates, and LinuxKitManager in pkg/container to use io.Medium. - Introduced TemplateManager struct for dependency injection. - Updated all call sites in internal/cmd/vm and pkg/devops. - Restored and adapted comprehensive test suite in linuxkit_test.go. - Fixed naming collisions and followed project test naming conventions. * chore(io): address PR feedback for container Medium migration - Added Open method to io.Medium interface to support log streaming. - Implemented Open in local.Medium and MockMedium. - Fixed extension inconsistency in GetTemplate (.yml vs .yaml). - Refactored TemplateManager to use configurable WorkingDir and HomeDir. - Reused TemplateManager instance in cmd_templates.go. - Updated LinuxKitManager to use medium.Open for log access. - Maintained and updated all tests to verify these improvements.
2026-02-04 15:33:22 +00:00
manager, err := container.NewLinuxKitManager(io.Local)
if err != nil {
return fmt.Errorf(i18n.T("common.error.failed", map[string]any{"Action": "initialize container manager"})+": %w", err)
}
fmt.Printf("%s %s\n", dimStyle.Render(i18n.T("cmd.vm.label.hypervisor")), manager.Hypervisor().Name())
fmt.Println()
ctx := context.Background()
c, err := manager.Run(ctx, imagePath, runOpts)
if err != nil {
Migrate pkg/container to io.Medium abstraction (#292) * chore(io): migrate pkg/container to Medium abstraction Migrated State, Templates, and LinuxKitManager in pkg/container to use the io.Medium abstraction for storage operations. - Introduced TemplateManager struct to handle template logic with injected medium. - Updated State struct to use injected medium for persistence. - Updated LinuxKitManager to hold and use an io.Medium instance. - Updated all internal callers in internal/cmd/vm and pkg/devops to use new APIs. - Adapted and maintained comprehensive test coverage in linuxkit_test.go. - Fixed naming collision with standard io package by aliasing it as goio. * chore(io): migrate pkg/container to Medium abstraction (v2) - Migrated State, Templates, and LinuxKitManager in pkg/container to use io.Medium. - Introduced TemplateManager struct for dependency injection. - Updated all call sites in internal/cmd/vm and pkg/devops. - Restored and adapted comprehensive test suite in linuxkit_test.go. - Fixed naming collisions and followed project test naming conventions. * chore(io): address PR feedback for container Medium migration - Added Open method to io.Medium interface to support log streaming. - Implemented Open in local.Medium and MockMedium. - Fixed extension inconsistency in GetTemplate (.yml vs .yaml). - Refactored TemplateManager to use configurable WorkingDir and HomeDir. - Reused TemplateManager instance in cmd_templates.go. - Updated LinuxKitManager to use medium.Open for log access. - Maintained and updated all tests to verify these improvements.
2026-02-04 15:33:22 +00:00
return fmt.Errorf(i18n.T("i18n.fail.run", "container")+": %w", err)
}
if runOpts.Detach {
fmt.Printf("%s %s\n", successStyle.Render(i18n.T("common.label.started")), c.ID)
fmt.Printf("%s %d\n", dimStyle.Render(i18n.T("cmd.vm.label.pid")), c.PID)
fmt.Println()
fmt.Println(i18n.T("cmd.vm.hint.view_logs", map[string]interface{}{"ID": c.ID[:8]}))
fmt.Println(i18n.T("cmd.vm.hint.stop", map[string]interface{}{"ID": c.ID[:8]}))
} else {
fmt.Printf("\n%s %s\n", dimStyle.Render(i18n.T("cmd.vm.label.container_stopped")), c.ID)
}
return nil
}
// buildLinuxKitImage builds a LinuxKit image from a YAML file.
func buildLinuxKitImage(yamlPath, outputPath string) error {
// Check if linuxkit is available
lkPath, err := lookupLinuxKit()
if err != nil {
return err
}
// Build the image
// linuxkit build --format iso-bios --name <output> <yaml>
cmd := exec.Command(lkPath, "build",
"--format", "iso-bios",
"--name", outputPath,
yamlPath)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
// findBuiltImage finds the built image file.
func findBuiltImage(basePath string) string {
// LinuxKit can create different formats
extensions := []string{".iso", "-bios.iso", ".qcow2", ".raw", ".vmdk"}
for _, ext := range extensions {
path := basePath + ext
if _, err := os.Stat(path); err == nil {
return path
}
}
// Check directory for any image file
dir := filepath.Dir(basePath)
base := filepath.Base(basePath)
entries, err := os.ReadDir(dir)
if err != nil {
return ""
}
for _, entry := range entries {
name := entry.Name()
if strings.HasPrefix(name, base) {
for _, ext := range []string{".iso", ".qcow2", ".raw", ".vmdk"} {
if strings.HasSuffix(name, ext) {
return filepath.Join(dir, name)
}
}
}
}
return ""
}
// lookupLinuxKit finds the linuxkit binary.
func lookupLinuxKit() (string, error) {
// Check PATH first
if path, err := exec.LookPath("linuxkit"); err == nil {
return path, nil
}
// Check common locations
paths := []string{
"/usr/local/bin/linuxkit",
"/opt/homebrew/bin/linuxkit",
}
for _, p := range paths {
if _, err := os.Stat(p); err == nil {
return p, nil
}
}
return "", errors.New(i18n.T("cmd.vm.error.linuxkit_not_found"))
}
// ParseVarFlags parses --var flags into a map.
// Format: --var KEY=VALUE or --var KEY="VALUE"
func ParseVarFlags(varFlags []string) map[string]string {
vars := make(map[string]string)
for _, v := range varFlags {
parts := strings.SplitN(v, "=", 2)
if len(parts) == 2 {
key := strings.TrimSpace(parts[0])
value := strings.TrimSpace(parts[1])
// Remove surrounding quotes if present
value = strings.Trim(value, "\"'")
vars[key] = value
}
}
return vars
}