diff --git a/cmd/appstreamfile/main.go b/cmd/appstreamfile/main.go index d520f43..7845109 100644 --- a/cmd/appstreamfile/main.go +++ b/cmd/appstreamfile/main.go @@ -83,7 +83,7 @@ func run(ctx context.Context, opts *RunOptions) error { return fmt.Errorf("config file validation failed: %w", err) } - err = service.ImplementConfig(config) + err = service.ImplementConfig(ctx, config) if err != nil { return fmt.Errorf("error setting up config: %w", err) diff --git a/internal/execx/execx.go b/internal/execx/execx.go index b75e85d..4e9299d 100644 --- a/internal/execx/execx.go +++ b/internal/execx/execx.go @@ -1,8 +1,11 @@ package execx +import "context" + type Commander interface { LookPath(file string) (string, error) Command(name string, arg ...string) Cmd + CommandContext(ctx context.Context, name string, arg ...string) Cmd } type Cmd interface { diff --git a/internal/execx/osexec_commander.go b/internal/execx/osexec_commander.go index d5c73e4..d755fb7 100644 --- a/internal/execx/osexec_commander.go +++ b/internal/execx/osexec_commander.go @@ -1,6 +1,9 @@ package execx -import "os/exec" +import ( + "context" + "os/exec" +) type ExecCmd struct { *exec.Cmd @@ -10,7 +13,7 @@ func (c *ExecCmd) CombinedOutput() ([]byte, error) { return c.Cmd.CombinedOutput() } -func (c *ExecCmd) String() string{ +func (c *ExecCmd) String() string { return c.Cmd.String() } @@ -23,3 +26,7 @@ func (ex *ExecCommander) LookPath(file string) (string, error) { func (ex *ExecCommander) Command(name string, arg ...string) Cmd { return &ExecCmd{exec.Command(name, arg...)} } + +func (ex *ExecCommander) CommandContext(context context.Context, name string, arg ...string) Cmd { + return &ExecCmd{exec.CommandContext(context, name, arg...)} +} diff --git a/internal/service/catalog_service.go b/internal/service/catalog_service.go index 481eb0c..437ab56 100644 --- a/internal/service/catalog_service.go +++ b/internal/service/catalog_service.go @@ -1,6 +1,7 @@ package service import ( + "context" "fmt" "github.com/aslamcodes/appstreamfile/internal/config" @@ -11,7 +12,7 @@ type UpdateStackCatalogSvc struct { Exec execx.Commander } -func (svc *UpdateStackCatalogSvc) UpdateStackCatalog(c config.CatalogConfig) error { +func (svc *UpdateStackCatalogSvc) UpdateStackCatalog(ctx context.Context, c config.CatalogConfig) error { fmt.Println("\nConfiguring stack catalog") fmt.Println(c) @@ -23,7 +24,7 @@ func (svc *UpdateStackCatalogSvc) UpdateStackCatalog(c config.CatalogConfig) err args := append([]string{"add-application"}, c.Args()...) - cmd := svc.Exec.Command(IMAGE_ASSISTANT, args...) + cmd := svc.Exec.CommandContext(ctx, IMAGE_ASSISTANT, args...) output, err := cmd.CombinedOutput() diff --git a/internal/service/catalog_service_test.go b/internal/service/catalog_service_test.go index ad75666..4044ef1 100644 --- a/internal/service/catalog_service_test.go +++ b/internal/service/catalog_service_test.go @@ -1,6 +1,7 @@ package service_test import ( + "context" "errors" "slices" "testing" @@ -10,6 +11,8 @@ import ( ) func TestCatalogConfigSuccess(t *testing.T) { + ctx := context.TODO() + fc := &FakeCommander{ LookPathErr: nil, } @@ -27,7 +30,7 @@ func TestCatalogConfigSuccess(t *testing.T) { Exec: fc, } - updateCatalogSvc.UpdateStackCatalog(catalog) + updateCatalogSvc.UpdateStackCatalog(ctx, catalog) if !slices.Equal(catalog.Args(), fc.LastArgs[1:]) { t.Errorf("expected %v\n, got %v", catalog.Args(), fc.LastArgs[1:]) @@ -36,6 +39,8 @@ func TestCatalogConfigSuccess(t *testing.T) { } func TestCatalogConfigFail(t *testing.T) { + ctx := context.TODO() + fc := &FakeCommander{ LookPathErr: errors.New("file not found"), } @@ -53,7 +58,7 @@ func TestCatalogConfigFail(t *testing.T) { Exec: fc, } - err := updateCatalogSvc.UpdateStackCatalog(catalog) + err := updateCatalogSvc.UpdateStackCatalog(ctx, catalog) if !errors.Is(fc.LookPathErr, err) { t.Errorf("expected %v\n, got %v", fc.LookPathErr, err) diff --git a/internal/service/config_service.go b/internal/service/config_service.go index 26be9a5..a9cd231 100644 --- a/internal/service/config_service.go +++ b/internal/service/config_service.go @@ -1,6 +1,7 @@ package service import ( + "context" "fmt" "github.com/aslamcodes/appstreamfile/internal/config" @@ -9,24 +10,20 @@ import ( const IMAGE_ASSISTANT = "image-assistant" -func ImplementConfig(c *config.Config) error { +func ImplementConfig(ctx context.Context, c *config.Config) error { + execCmd := &execx.ExecCommander{} + services := &services{ FileDeploySvc: &FileDeploySvc{}, SessionScriptService: &SessionScriptSvc{}, - CatalogSvc: &UpdateStackCatalogSvc{ - Exec: &execx.ExecCommander{}, - }, - ImageBuildService: &ImageBuildSvc{ - Exec: &execx.ExecCommander{}, - }, - InstallerService: &InstallerSvc{ - Exec: &execx.ExecCommander{}, - }, + CatalogSvc: &UpdateStackCatalogSvc{Exec: execCmd}, + ImageBuildService: &ImageBuildSvc{Exec: execCmd}, + InstallerService: &InstallerSvc{Exec: execCmd}, } for _, i := range c.Installers { fmt.Println("Executing installer with", i.Executable) - err := services.InstallerService.InstallScript(&i) + err := services.InstallerService.InstallScript(ctx, &i) if err != nil { return fmt.Errorf("error executing %s script\n%s: %w", i.Executable, i.InstallScript, err) @@ -42,17 +39,17 @@ func ImplementConfig(c *config.Config) error { } } - if err := services.SessionScriptService.UpdateSessionScriptConfig(SessionScriptLocation(), c.SessionScripts); err != nil { + if err := services.SessionScriptService.UpdateSessionScriptConfig(ctx, SessionScriptLocation(), c.SessionScripts); err != nil { return fmt.Errorf("error configuring session scripts: %w", err) } for _, catalog := range c.Catalogs { - if err := services.CatalogSvc.UpdateStackCatalog(catalog); err != nil { + if err := services.CatalogSvc.UpdateStackCatalog(ctx, catalog); err != nil { return fmt.Errorf("error updating stack catalog: %w", err) } } - if err := services.ImageBuildService.BuildImage(c.Image); err != nil { + if err := services.ImageBuildService.BuildImage(ctx, c.Image); err != nil { return fmt.Errorf("error building out image: %w", err) } diff --git a/internal/service/image_build_service.go b/internal/service/image_build_service.go index 1ae47fc..f5698d0 100644 --- a/internal/service/image_build_service.go +++ b/internal/service/image_build_service.go @@ -1,6 +1,7 @@ package service import ( + "context" "fmt" "github.com/aslamcodes/appstreamfile/internal/config" @@ -11,7 +12,7 @@ type ImageBuildSvc struct { Exec execx.Commander } -func (i *ImageBuildSvc) BuildImage(image config.Image) error { +func (i *ImageBuildSvc) BuildImage(ctx context.Context, image config.Image) error { fmt.Println("\nBuilding out image") _, err := i.Exec.LookPath(IMAGE_ASSISTANT) @@ -22,7 +23,7 @@ func (i *ImageBuildSvc) BuildImage(image config.Image) error { args := append([]string{"create-image"}, image.Args()...) - cmd := i.Exec.Command(IMAGE_ASSISTANT, args...) + cmd := i.Exec.CommandContext(ctx, IMAGE_ASSISTANT, args...) fmt.Println(cmd.String()) diff --git a/internal/service/image_build_service_test.go b/internal/service/image_build_service_test.go index d1ea25e..b7f7fdf 100644 --- a/internal/service/image_build_service_test.go +++ b/internal/service/image_build_service_test.go @@ -1,6 +1,7 @@ package service_test import ( + "context" "fmt" "strings" "testing" @@ -10,6 +11,8 @@ import ( ) func TestImageBuild(t *testing.T) { + ctx := context.TODO() + fc := FakeCommander{ LastCommand: "", LastArgs: []string{}, @@ -30,7 +33,7 @@ func TestImageBuild(t *testing.T) { DryRun: true, } - i.BuildImage(image) + i.BuildImage(ctx, image) expectedCommand := strings.TrimSpace(`image-assistant create-image --name test --display-name test2 --description test3 --use-latest-agent-version --enable-dynamic-app-catalog --dry-run --tags k1 v1 k2 built with appstreamfile`) actual := strings.TrimSpace(fmt.Sprintf("%s %s", fc.LastCommand, strings.Join(fc.LastArgs, " "))) diff --git a/internal/service/installer_service.go b/internal/service/installer_service.go index f8207c5..04e7cdf 100644 --- a/internal/service/installer_service.go +++ b/internal/service/installer_service.go @@ -1,6 +1,7 @@ package service import ( + "context" "fmt" "os" @@ -13,7 +14,7 @@ type InstallerSvc struct { KeepTmpForTest bool } -func (s *InstallerSvc) InstallScript(inst *config.Installer) error { +func (s *InstallerSvc) InstallScript(ctx context.Context, inst *config.Installer) error { var ( exe string ext string @@ -52,11 +53,11 @@ func (s *InstallerSvc) InstallScript(inst *config.Installer) error { return fmt.Errorf("unable to close the file: %w", err) } - return s.RunScript(exe, args, f.Name()) + return s.RunScript(ctx, exe, args, f.Name()) } -func (s *InstallerSvc) RunScript(exe string, args []string, filePath string) error { - cmd := s.Exec.Command(exe, append(args, filePath)...) +func (s *InstallerSvc) RunScript(ctx context.Context, exe string, args []string, filePath string) error { + cmd := s.Exec.CommandContext(ctx, exe, append(args, filePath)...) out, err := cmd.CombinedOutput() diff --git a/internal/service/installer_service_test.go b/internal/service/installer_service_test.go index cab0269..a551169 100644 --- a/internal/service/installer_service_test.go +++ b/internal/service/installer_service_test.go @@ -1,6 +1,7 @@ package service_test import ( + "context" "os" "testing" @@ -9,6 +10,7 @@ import ( ) func TestInstallCreatesScriptFile(t *testing.T) { + ctx := context.TODO() fake := &FakeCommander{} svc := &service.InstallerSvc{Exec: fake, KeepTmpForTest: true} @@ -17,7 +19,7 @@ func TestInstallCreatesScriptFile(t *testing.T) { InstallScript: "Write-Host hi", } - _ = svc.InstallScript(inst) + _ = svc.InstallScript(ctx, inst) scriptPath := fake.LastArgs[len(fake.LastArgs)-1] @@ -30,10 +32,12 @@ func TestInstallCreatesScriptFile(t *testing.T) { } func TestRunScript(t *testing.T) { + ctx := context.TODO() + fake := &FakeCommander{} svc := &service.InstallerSvc{Exec: fake} - err := svc.RunScript("powershell.exe", + err := svc.RunScript(ctx, "powershell.exe", []string{"-NoProfile", "-File"}, "/tmp/test.ps1", ) diff --git a/internal/service/mock_commander_test.go b/internal/service/mock_commander_test.go index 0efaad0..76035bf 100644 --- a/internal/service/mock_commander_test.go +++ b/internal/service/mock_commander_test.go @@ -1,6 +1,8 @@ package service_test import ( + "context" + "github.com/aslamcodes/appstreamfile/internal/execx" ) @@ -36,3 +38,12 @@ func (fc *FakeCommander) Command(name string, args ...string) execx.Cmd { Args: args, } } + +func (fc *FakeCommander) CommandContext(context context.Context, name string, args ...string) execx.Cmd { + fc.LastCommand = name + fc.LastArgs = args + return &FakeCmd{ + Command: name, + Args: args, + } +} diff --git a/internal/service/session_script_service.go b/internal/service/session_script_service.go index 87170fb..b861623 100644 --- a/internal/service/session_script_service.go +++ b/internal/service/session_script_service.go @@ -2,6 +2,7 @@ package service import ( "bufio" + "context" "encoding/json" "fmt" "os" @@ -20,7 +21,11 @@ func SessionScriptLocation() string { return "/opt/appstream/SessionScripts/config.json" } -func (svc *SessionScriptSvc) UpdateSessionScriptConfig(location string, ss config.SessionScripts) error { +func (svc *SessionScriptSvc) UpdateSessionScriptConfig(ctx context.Context, location string, ss config.SessionScripts) error { + if err := ctx.Err(); err != nil { + return err + } + fmt.Println("Configuring session scripts") if err := os.MkdirAll(filepath.Dir(location), 0770); err != nil { diff --git a/internal/service/session_script_service_test.go b/internal/service/session_script_service_test.go index 63ebec9..4c63b94 100644 --- a/internal/service/session_script_service_test.go +++ b/internal/service/session_script_service_test.go @@ -1,6 +1,7 @@ package service_test import ( + "context" "encoding/json" "os" "path" @@ -12,6 +13,7 @@ import ( ) func Test(t *testing.T) { + ctx := context.TODO() dir := os.TempDir() config_file_path := path.Join(dir, "config.json") @@ -54,7 +56,7 @@ func Test(t *testing.T) { svc := service.SessionScriptSvc{} - svc.UpdateSessionScriptConfig(config_file_path, *ss) + svc.UpdateSessionScriptConfig(ctx, config_file_path, *ss) content, err := os.ReadFile(config_file_path)