From 94c3263d6d8aea68aa9e8d6ea2ca398dc0ecb6e7 Mon Sep 17 00:00:00 2001 From: Ferran Borreguero Date: Wed, 2 Apr 2025 09:17:38 +0100 Subject: [PATCH] Override better --- internal/local_runner.go | 52 +++++++++++++++++++++++++++++++++++++++- internal/manifest.go | 13 ++-------- main.go | 19 ++++++++++++++- 3 files changed, 71 insertions(+), 13 deletions(-) diff --git a/internal/local_runner.go b/internal/local_runner.go index ea5d0ff..43dad46 100644 --- a/internal/local_runner.go +++ b/internal/local_runner.go @@ -94,6 +94,29 @@ func NewLocalRunner(out *output, manifest *Manifest, overrides map[string]string overrides[k] = v } + // Now, the override can either be one of two things (we are overloading the override map): + // - docker image: In that case, change the manifest and remove from override map + // - a path to an executable: In that case, we need to run it on the host machine + // and use the override map <- We only check this case, and if it is not a path, we assume + // it is a docker image. If it is not a docker image either, the error will be catched during the execution + for k, v := range overrides { + if _, err := os.Stat(v); err != nil { + // this is a path to an executable, remove it from the overrides since we + // assume it s a docker image and add it to manifest + parts := strings.Split(v, ":") + if len(parts) != 2 { + return nil, fmt.Errorf("invalid override docker image %s, expected image:tag", v) + } + + srv := manifest.MustGetService(k) + srv.image = parts[0] + srv.tag = parts[1] + + delete(overrides, k) + continue + } + } + tasks := map[string]*task{} for _, svc := range manifest.services { tasks[svc.Name] = &task{ @@ -428,6 +451,27 @@ func (d *LocalRunner) applyTemplate(s *service) ([]string, map[string]string, er return argsResult, envs, nil } +func (d *LocalRunner) validateImageExists(image string) error { + // check locally + _, err := d.client.ImageInspect(context.Background(), image) + if err == nil { + return nil + } + if !client.IsErrNotFound(err) { + return err + } + + // check remotely + if _, err = d.client.DistributionInspect(context.Background(), image, ""); err == nil { + return nil + } + if !client.IsErrNotFound(err) { + return err + } + + return fmt.Errorf("image %s not found: %w", image) +} + func (d *LocalRunner) toDockerComposeService(s *service) (map[string]interface{}, error) { // apply the template again on the arguments to figure out the connections // at this point all of them are valid, we just have to resolve them again. We assume for now @@ -444,8 +488,14 @@ func (d *LocalRunner) toDockerComposeService(s *service) (map[string]interface{} return nil, fmt.Errorf("failed to get absolute path for output folder: %w", err) } + // Validate that the image exists + imageName := fmt.Sprintf("%s:%s", s.image, s.tag) + if err := d.validateImageExists(imageName); err != nil { + return nil, fmt.Errorf("failed to validate image %s: %w", imageName, err) + } + service := map[string]interface{}{ - "image": fmt.Sprintf("%s:%s", s.image, s.tag), + "image": imageName, "command": args, // Add volume mount for the output directory "volumes": []string{ diff --git a/internal/manifest.go b/internal/manifest.go index 03835b3..394794b 100644 --- a/internal/manifest.go +++ b/internal/manifest.go @@ -6,7 +6,6 @@ import ( "io" "os" "strings" - "sync" "text/template" "time" @@ -100,7 +99,6 @@ type ServiceWatchdog interface { } func RunWatchdog(manifest *Manifest) error { - var wg sync.WaitGroup watchdogErr := make(chan error, len(manifest.Services())) output, err := manifest.out.LogOutput("watchdog") @@ -110,23 +108,16 @@ func RunWatchdog(manifest *Manifest) error { for _, s := range manifest.Services() { if watchdogFn, ok := s.component.(ServiceWatchdog); ok { - wg.Add(1) - go func() { - defer wg.Done() if err := watchdogFn.Watchdog(output, s, context.Background()); err != nil { watchdogErr <- fmt.Errorf("service %s watchdog failed: %w", s.Name, err) } }() } } - wg.Wait() - close(watchdogErr) - for err := range watchdogErr { - if err != nil { - return err - } + if err := <-watchdogErr; err != nil { + return fmt.Errorf("failed to run watchdog: %w", err) } return nil } diff --git a/main.go b/main.go index c4fd52a..8f8fbad 100644 --- a/main.go +++ b/main.go @@ -166,6 +166,16 @@ func runIt(recipe internal.Recipe) error { log.Printf("Log level: %s\n", logLevel) + // parse the overrides + overrides := map[string]string{} + for _, val := range withOverrides { + parts := strings.SplitN(val, "=", 2) + if len(parts) != 2 { + return fmt.Errorf("invalid override format: %s, expected service=val", val) + } + overrides[parts[0]] = parts[1] + } + builder := recipe.Artifacts() builder.OutputDir(outputFlag) builder.GenesisDelay(genesisDelayFlag) @@ -189,7 +199,14 @@ func runIt(recipe internal.Recipe) error { return nil } - dockerRunner, err := internal.NewLocalRunner(artifacts.Out, svcManager, nil, interactive) + // validate that override is being applied to a service in the manifest + for k := range overrides { + if _, ok := svcManager.GetService(k); !ok { + return fmt.Errorf("service '%s' in override not found in manifest", k) + } + } + + dockerRunner, err := internal.NewLocalRunner(artifacts.Out, svcManager, overrides, interactive) if err != nil { return fmt.Errorf("failed to create docker runner: %w", err) }