From 5490bba12ff528434c20ed35c01c5257a5129c72 Mon Sep 17 00:00:00 2001 From: Marc Cataford Date: Fri, 30 Aug 2024 22:20:56 -0400 Subject: [PATCH] feat: add cache manager --- internal/cache/cache_manager.go | 43 ++++++++++++++++++++++++++ internal/cache/cache_manager_test.go | 44 +++++++++++++++++++++++++++ internal/commands/execute_workflow.go | 1 + internal/runner/runner.go | 7 ++++- internal/runner/runner_flow_test.go | 2 +- internal/runner/runner_job_test.go | 12 ++++---- 6 files changed, 101 insertions(+), 8 deletions(-) create mode 100644 internal/cache/cache_manager.go create mode 100644 internal/cache/cache_manager_test.go diff --git a/internal/cache/cache_manager.go b/internal/cache/cache_manager.go new file mode 100644 index 0000000..d767231 --- /dev/null +++ b/internal/cache/cache_manager.go @@ -0,0 +1,43 @@ +package cache_manager + +import ( + "errors" + "os" + "path/filepath" +) + +type CacheManager struct { + Root string +} + +// Creates a new cache manager rooted at . +// +// If does not exist, returns an error. +func NewCacheManager(cacheRoot string) (*CacheManager, error) { + if _, err := os.Stat(cacheRoot); errors.Is(err, os.ErrNotExist) { + return nil, errors.New("Cache root must exist.") + } + + return &CacheManager{ + Root: cacheRoot, + }, nil +} + +// Returns a path prefixed with the cache root. +func (c CacheManager) Path(elems ...string) string { + return filepath.Join(c.Root, filepath.Join(elems...)) +} + +// Checks whether a specific top-level items exists in the cache. +func (c CacheManager) Exists(key string) bool { + if _, err := os.Stat(c.Path(key)); errors.Is(err, os.ErrNotExist) { + return false + } + + return true +} + +// Deletes an element from the cache, if it exists. +func (c CacheManager) Evict(key string) { + os.RemoveAll(c.Path(key)) +} diff --git a/internal/cache/cache_manager_test.go b/internal/cache/cache_manager_test.go new file mode 100644 index 0000000..4e3f3a6 --- /dev/null +++ b/internal/cache/cache_manager_test.go @@ -0,0 +1,44 @@ +package cache_manager + +import ( + "path/filepath" + "testing" +) + +func TestNewCacheManagerFailsIfNonExistentRoot(t *testing.T) { + nonPath := "/not/a/path" + + mgr, err := NewCacheManager(nonPath) + + if err == nil { + t.Error("Expected error, got nil.") + } + + if mgr != nil { + t.Error("Expected nil manager, got non-nil.") + } +} + +func TestNewCacheManagerReturnsCacheManagerPointer(t *testing.T) { + mgr, err := NewCacheManager(t.TempDir()) + + if err != nil { + t.Errorf("Expected nil error, got %s.", err) + } + + if mgr == nil { + t.Error("Expected non-nil manager, got nil.") + } +} + +func TestCacheManagerPathReturnsPathRootedAtCacheRoot(t *testing.T) { + tmpRoot := t.TempDir() + mgr, _ := NewCacheManager(tmpRoot) + + rootedPath := mgr.Path("test") + expectedPath := filepath.Join(tmpRoot, "test") + + if rootedPath != expectedPath { + t.Errorf("Expected %s, got %s.", expectedPath, rootedPath) + } +} diff --git a/internal/commands/execute_workflow.go b/internal/commands/execute_workflow.go index 203e7b6..0929bf1 100644 --- a/internal/commands/execute_workflow.go +++ b/internal/commands/execute_workflow.go @@ -19,6 +19,7 @@ func ExecuteWorkflow(configuration Configuration, workflowFile string) error { runnerInstance := runner.NewRunner( driver, configuration.Runner.Labels, + configuration.Cache.Dir, ) workflow, err := workflow.FromYamlFile(workflowFile) diff --git a/internal/runner/runner.go b/internal/runner/runner.go index 5bf92b5..fb43a8e 100644 --- a/internal/runner/runner.go +++ b/internal/runner/runner.go @@ -2,6 +2,7 @@ package runner import ( "context" + cache "courgette/internal/cache" driver "courgette/internal/driver" logger "courgette/internal/logging" workflow "courgette/internal/workflow" @@ -13,14 +14,18 @@ import ( type Runner struct { Labels map[string]string Driver driver.ContainerDriver + Cache cache.CacheManager Runs int // Deferred tasks, in order their were scheduled. deferred *DeferredTaskManager } -func NewRunner(driver driver.ContainerDriver, labels map[string]string) Runner { +func NewRunner(driver driver.ContainerDriver, labels map[string]string, cacheRoot string) Runner { + cacheManager, _ := cache.NewCacheManager(cacheRoot) + return Runner{ Driver: driver, + Cache: *cacheManager, Labels: labels, deferred: NewDeferredTaskManager(), } diff --git a/internal/runner/runner_flow_test.go b/internal/runner/runner_flow_test.go index 9918fe2..f3f3788 100644 --- a/internal/runner/runner_flow_test.go +++ b/internal/runner/runner_flow_test.go @@ -48,7 +48,7 @@ func TestRunJobInContainerSchedulesStoppingContainers(t *testing.T) { mockDriver := driver.NewMockDriver() mockDriver.WithMockedCall("Exec", driver.CommandResult{ExitCode: 1, Error: nil}, "test-container", "test-command", ".", fmt.Sprintf("%#v", map[string]string{})) - runner := NewRunner(&mockDriver, map[string]string{}) + runner := NewRunner(&mockDriver, map[string]string{}, t.TempDir()) jobCtx := context.WithValue(context.Background(), "currentJob", workflow.Job{}) jobCtx = context.WithValue(jobCtx, "workflow", workflow.Workflow{}) diff --git a/internal/runner/runner_job_test.go b/internal/runner/runner_job_test.go index 8ca6794..b232d24 100644 --- a/internal/runner/runner_job_test.go +++ b/internal/runner/runner_job_test.go @@ -21,7 +21,7 @@ jobs: workflow, _ := workflow.FromYamlBytes([]byte(workflowSample)) mockDriver := driver.NewMockDriver() - runner := NewRunner(&mockDriver, map[string]string{"test": "test"}) + runner := NewRunner(&mockDriver, map[string]string{"test": "test"}, t.TempDir()) jobContext := context.WithValue(context.Background(), "workflow", *workflow) jobContext = context.WithValue(jobContext, "currentJob", workflow.Jobs["jobA"]) @@ -46,7 +46,7 @@ jobs: workflow, _ := workflow.FromYamlBytes([]byte(workflowSample)) mockDriver := driver.NewMockDriver() - runner := NewRunner(&mockDriver, map[string]string{"test": "test"}) + runner := NewRunner(&mockDriver, map[string]string{"test": "test"}, t.TempDir()) jobContext := context.WithValue(context.Background(), "workflow", *workflow) jobContext = context.WithValue(jobContext, "currentJob", workflow.Jobs["jobA"]) @@ -73,7 +73,7 @@ jobs: mockDriver := driver.NewMockDriver() mockDriver.WithMockedCall("Exec", driver.CommandResult{Error: errors.New("exit 1!"), ExitCode: 1}, "testContainer", "exit 1", fmt.Sprintf("%#v", map[string]string{})) - runner := NewRunner(&mockDriver, map[string]string{"test": "test"}) + runner := NewRunner(&mockDriver, map[string]string{"test": "test"}, t.TempDir()) jobContext := context.WithValue(context.Background(), "workflow", *workflow) jobContext = context.WithValue(jobContext, "currentJob", workflow.Jobs["jobA"]) @@ -101,7 +101,7 @@ jobs: mockDriver := driver.NewMockDriver() mockDriver.WithMockedCall("Exec", driver.CommandResult{Error: errors.New("exit 1!"), ExitCode: 1}, "testContainer", "exit 1", fmt.Sprintf("%#v", map[string]string{})) - runner := NewRunner(&mockDriver, map[string]string{"test": "test"}) + runner := NewRunner(&mockDriver, map[string]string{"test": "test"}, t.TempDir()) jobContext := context.WithValue(context.Background(), "workflow", *workflow) jobContext = context.WithValue(jobContext, "currentJob", workflow.Jobs["jobA"]) @@ -133,7 +133,7 @@ jobs: mockDriver := driver.NewMockDriver() mockDriver.WithMockedCall("Exec", driver.CommandResult{Error: errors.New("exit 1!"), ExitCode: 1}, "testContainer", "exit 1", ".", fmt.Sprintf("%#v", map[string]string{})) - runner := NewRunner(&mockDriver, map[string]string{"test": "test"}) + runner := NewRunner(&mockDriver, map[string]string{"test": "test"}, t.TempDir()) jobContext := context.WithValue(context.Background(), "workflow", *workflow) jobContext = context.WithValue(jobContext, "currentJob", workflow.Jobs["jobA"]) @@ -164,7 +164,7 @@ jobs: mockDriver := driver.NewMockDriver() mockDriver.WithMockedCall("Exec", driver.CommandResult{Error: errors.New("exit 1!"), ExitCode: 1}, "testContainer", "exit 1", ".", fmt.Sprintf("%#v", map[string]string{})) - runner := NewRunner(&mockDriver, map[string]string{"test": "test"}) + runner := NewRunner(&mockDriver, map[string]string{"test": "test"}, t.TempDir()) jobContext := context.WithValue(context.Background(), "workflow", *workflow) jobContext = context.WithValue(jobContext, "currentJob", workflow.Jobs["jobA"])