Skip to content

Commit

Permalink
feat: move plan files to ~/.pug, and auto-delete them
Browse files Browse the repository at this point in the history
  • Loading branch information
leg100 committed May 2, 2024
1 parent 81c27c1 commit 05c039d
Show file tree
Hide file tree
Showing 16 changed files with 54 additions and 79 deletions.
1 change: 0 additions & 1 deletion demos/getting_started/reset.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,3 @@

find ./demos/getting_started -name .terraform -exec rm -rf {} \; > /dev/null 2>&1 || true
find ./demos/getting_started -name terraform.tfstate* -exec rm -rf {} \; > /dev/null 2>&1 || true
find ./demos/getting_started -name .pug -exec rm -rf {} \; > /dev/null 2>&1 || true
1 change: 0 additions & 1 deletion demos/modules/reset.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,3 @@

find ./demos/modules -name .terraform -exec rm -rf {} \; > /dev/null 2>&1 || true
find ./demos/modules -name terraform.tfstate* -exec rm -rf {} \; > /dev/null 2>&1 || true
find ./demos/modules -name .pug -exec rm -rf {} \; > /dev/null 2>&1 || true
1 change: 0 additions & 1 deletion demos/run/reset.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,3 @@

find ./demos/run -name .terraform -exec rm -rf {} \; > /dev/null 2>&1 || true
find ./demos/run -name terraform.tfstate* -exec rm -rf {} \; > /dev/null 2>&1 || true
find ./demos/run -name .pug -exec rm -rf {} \; > /dev/null 2>&1 || true
1 change: 0 additions & 1 deletion demos/runs/reset.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,3 @@

find ./demos/runs -name .terraform -exec rm -rf {} \; > /dev/null 2>&1 || true
find ./demos/runs -name terraform.tfstate* -exec rm -rf {} \; > /dev/null 2>&1 || true
find ./demos/runs -name .pug -exec rm -rf {} \; > /dev/null 2>&1 || true
1 change: 0 additions & 1 deletion demos/state/reset.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,3 @@

find ./demos/state -name .terraform -exec rm -rf {} \; > /dev/null 2>&1 || true
find ./demos/state -name terraform.tfstate* -exec rm -rf {} \; > /dev/null 2>&1 || true
find ./demos/state -name .pug -exec rm -rf {} \; > /dev/null 2>&1 || true
1 change: 0 additions & 1 deletion demos/tasks/reset.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#!/usr/bin/env bash

find ./demos/tasks -name .terraform -exec rm -rf {} \; > /dev/null 2>&1 || true
find ./demos/tasks -name .pug -exec rm -rf {} \; > /dev/null 2>&1 || true
find ./demos/tasks -name terraform.tfstate -exec rm -rf {} \; > /dev/null 2>&1 || true
1 change: 0 additions & 1 deletion demos/workspaces/reset.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#!/usr/bin/env bash

find ./demos/workspaces -name .terraform -exec rm -rf {} \; > /dev/null 2>&1 || true
find ./demos/workspaces -name .pug -exec rm -rf {} \; > /dev/null 2>&1 || true
find ./demos/workspaces -name terraform.tfstate -exec rm -rf {} \; > /dev/null 2>&1 || true
10 changes: 8 additions & 2 deletions internal/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"context"
"fmt"
"io"
"os"

tea "github.com/charmbracelet/bubbletea"
"github.com/leg100/pug/internal"
Expand Down Expand Up @@ -61,7 +62,7 @@ func Start(stdout, stderr io.Writer, args []string) error {
"max_tasks", cfg.MaxTasks,
"plugin_cache", cfg.PluginCache,
"program", cfg.Program,
"work_dir", cfg.Workdir,
"work_dir", cfg.WorkDir,
)

p := tea.NewProgram(
Expand Down Expand Up @@ -95,7 +96,7 @@ func newApp(ctx context.Context, cfg config) (*app, tea.Model, error) {

// Perform any conversions from the flag parsed primitive types to pug
// defined types.
workdir, err := internal.NewWorkdir(cfg.Workdir)
workdir, err := internal.NewWorkdir(cfg.WorkDir)
if err != nil {
return nil, nil, err
}
Expand Down Expand Up @@ -130,6 +131,7 @@ func newApp(ctx context.Context, cfg config) (*app, tea.Model, error) {
WorkspaceService: workspaces,
StateService: states,
DisableReloadAfterApply: cfg.DisableReloadAfterApply,
DataDir: cfg.DataDir,
Logger: logger,
})

Expand Down Expand Up @@ -233,6 +235,10 @@ func (a *app) start(ctx context.Context, stdout io.Writer, s sender) func() erro
for _, task := range running {
a.tasks.Cancel(task.ID)
}
// Remove all run artefacts (plan files etc,...)
for _, run := range a.runs.List(run.ListOptions{}) {
_ = os.RemoveAll(run.ArtefactsPath())
}
// Wait for canceled tasks to terminate.
return waitfn()
}
Expand Down
16 changes: 13 additions & 3 deletions internal/app/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package app
import (
"fmt"
"io"
"os"
"path/filepath"
"runtime"

"github.com/hashicorp/terraform/command/cliconfig"
Expand All @@ -19,7 +21,8 @@ type config struct {
FirstPage string
Debug bool
DisableReloadAfterApply bool
Workdir string
WorkDir string
DataDir string

loggingOptions logging.Options
version bool
Expand All @@ -30,10 +33,17 @@ type config struct {
func parse(stderr io.Writer, args []string) (config, error) {
var cfg config

home, err := os.UserHomeDir()
if err != nil {
return config{}, fmt.Errorf("retrieving user's home directory: %w", err)
}
defaultDataDir := filepath.Join(home, ".pug")

fs := ff.NewFlagSet("pug")
fs.StringVar(&cfg.Program, 'p', "program", "terraform", "The default program to use with pug.")
fs.StringVar(&cfg.Workdir, 'w', "workdir", ".", "The working directory containing modules.")
fs.StringVar(&cfg.WorkDir, 'w', "workdir", ".", "The working directory containing modules.")
fs.IntVar(&cfg.MaxTasks, 't', "max-tasks", 2*runtime.NumCPU(), "The maximum number of parallel tasks.")
fs.StringVar(&cfg.DataDir, 0, "data-dir", defaultDataDir, "Directory in which to store plan files.")
fs.StringEnumVar(&cfg.FirstPage, 'f', "first-page", "The first page to open on startup.", "modules", "workspaces", "runs", "tasks", "logs")
fs.BoolVar(&cfg.Debug, 'd', "debug", "Log bubbletea messages to messages.log")
fs.BoolVar(&cfg.version, 'v', "version", "Print version.")
Expand All @@ -46,7 +56,7 @@ func parse(stderr io.Writer, args []string) (config, error) {
tfcfg, _ := cliconfig.LoadConfig()
cfg.PluginCache = (tfcfg.PluginCacheDir != "")

err := ff.Parse(fs, args,
err = ff.Parse(fs, args,
ff.WithEnvVarPrefix("PUG"),
ff.WithConfigFileFlag("config"),
ff.WithConfigFileParser(ffyaml.Parse),
Expand Down
12 changes: 4 additions & 8 deletions internal/app/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package app
import (
"io"
"os"
"path"
"path/filepath"
"runtime"
"strings"
"testing"
Expand All @@ -20,12 +20,7 @@ func TestConfig(t *testing.T) {
t.Setenv("PUG_FIRST_PAGE", "")
t.Setenv("PUG_LOG_LEVEL", "")
t.Setenv("PUG_MAX_TASKS", "")
// Specify terraform config file, to override any such file present at the
// default location on the host computer
cliConfigFilePath := path.Join(t.TempDir(), "terraform.tfrc")
err := os.WriteFile(cliConfigFilePath, []byte(""), 0o644)
require.NoError(t, err)
t.Setenv("TF_CLI_CONFIG_FILE", cliConfigFilePath)
t.Setenv("HOME", t.TempDir())

tests := []struct {
name string
Expand All @@ -44,7 +39,8 @@ func TestConfig(t *testing.T) {
Program: "terraform",
MaxTasks: 2 * runtime.NumCPU(),
FirstPage: "modules",
Workdir: ".",
WorkDir: ".",
DataDir: filepath.Join(os.Getenv("HOME"), ".pug"),
loggingOptions: logging.Options{
Level: "info",
},
Expand Down
12 changes: 5 additions & 7 deletions internal/app/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,9 @@ func setup(t *testing.T, workdir string, sopts ...setupOption) *teatest.TestMode
config{
FirstPage: "modules",
Program: "terraform",
Workdir: workdir,
WorkDir: workdir,
MaxTasks: 3,
DataDir: t.TempDir(),
loggingOptions: logging.Options{
Level: "debug",
AdditionalWriters: []io.Writer{
Expand Down Expand Up @@ -91,12 +92,9 @@ func cleanupArtefacts(workdir string, opts setupOptions) {
if walkerr != nil {
return walkerr
}
if d.IsDir() {
switch d.Name() {
case ".terraform", internal.PugDirectory:
os.RemoveAll(path)
return fs.SkipDir
}
if d.IsDir() && d.Name() == ".terraform" {
os.RemoveAll(path)
return fs.SkipDir
}
// TODO: consider leaving lock file; it prevents a warning message cropping
// up.
Expand Down
5 changes: 0 additions & 5 deletions internal/pug.go

This file was deleted.

24 changes: 16 additions & 8 deletions internal/run/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,8 @@ type Run struct {
// Error is non-nil when the run status is Errored
Error error

// Run's dedicated directory for artefacts created during its lifetime. The
// path is relative to its module directory.
artefactsPath string
// Path to pug's data directory
dataDir string

// Call this function after every status update
afterUpdate func(run *Run)
Expand All @@ -69,6 +68,7 @@ type CreateOptions struct {
Destroy bool

afterUpdate func(run *Run)
dataDir string
}

func newRun(mod *module.Module, ws *workspace.Workspace, opts CreateOptions) (*Run, error) {
Expand All @@ -81,12 +81,12 @@ func newRun(mod *module.Module, ws *workspace.Workspace, opts CreateOptions) (*R
Created: time.Now(),
Updated: time.Now(),
afterUpdate: opts.afterUpdate,
dataDir: opts.dataDir,
}

// Create directory for artefacts including plan file etc.
run.artefactsPath = filepath.Join(ws.PugDirectory(), run.String())
if err := os.MkdirAll(filepath.Join(mod.FullPath(), run.artefactsPath), 0o755); err != nil {
return nil, err
// Create directory for run artefacts including plan file etc.
if err := os.MkdirAll(run.ArtefactsPath(), 0o755); err != nil {
return nil, fmt.Errorf("creating run artefacts directory: %w", err)
}

// Check if a tfvars file exists for the workspace. If so then use it for
Expand All @@ -107,7 +107,7 @@ func (r *Run) WorkspaceID() resource.ID {
// PlanPath is the path to the run's plan file, relative to the module's
// directory.
func (r *Run) PlanPath() string {
return filepath.Join(r.artefactsPath, "plan.out")
return filepath.Join(r.ArtefactsPath(), "plan")
}

// PlanArgs produces the arguments for terraform plan
Expand Down Expand Up @@ -141,6 +141,11 @@ func (r *Run) LogValue() slog.Value {
)
}

// Run's dedicated directory for artefacts created during its lifetime.
func (r *Run) ArtefactsPath() string {
return filepath.Join(r.dataDir, r.String())
}

func (r *Run) setErrored(err error) {
r.Error = err
r.updateStatus(Errored)
Expand All @@ -154,5 +159,8 @@ func (r *Run) updateStatus(status Status) {
}
if r.IsFinished() {
slog.Info("completed run", "status", r.Status, "run", r)

// Once a run is finished remove its artefacts
_ = os.RemoveAll(r.ArtefactsPath())
}
}
35 changes: 4 additions & 31 deletions internal/run/run_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package run

import (
"fmt"
"os"
"path/filepath"
"testing"
Expand Down Expand Up @@ -35,41 +34,15 @@ func TestRun_VarsFile(t *testing.T) {
assert.Contains(t, run.PlanArgs(), "-var-file=dev.tfvars")
}

func TestRun_MakePugDirectory(t *testing.T) {
workdir := internal.NewTestWorkdir(t)
testutils.ChTempDir(t, workdir.String())

mod := module.New(workdir, "a/b/c")
ws, err := workspace.New(mod, "dev")
require.NoError(t, err)

run, err := newRun(mod, ws, CreateOptions{})
require.NoError(t, err)

want := fmt.Sprintf("a/b/c/.pug/dev/%s", run.ID)
assert.DirExists(t, want)
}
func TestRun_MakeArtefactsPath(t *testing.T) {
dataDir := t.TempDir()

func TestRun_PugDirectory(t *testing.T) {
mod := module.New(internal.NewTestWorkdir(t), "a/b/c")
ws, err := workspace.New(mod, "dev")
require.NoError(t, err)

run, err := newRun(mod, ws, CreateOptions{})
require.NoError(t, err)

want := fmt.Sprintf(".pug/dev/%s", run.ID)
assert.Equal(t, want, run.artefactsPath)
}

func TestRun_PlanPath(t *testing.T) {
mod := module.New(internal.NewTestWorkdir(t), "a/b/c")
ws, err := workspace.New(mod, "dev")
require.NoError(t, err)

run, err := newRun(mod, ws, CreateOptions{})
run, err := newRun(mod, ws, CreateOptions{dataDir: dataDir})
require.NoError(t, err)

want := fmt.Sprintf(".pug/dev/%s/plan.out", run.ID)
assert.Equal(t, want, run.PlanPath())
assert.DirExists(t, run.ArtefactsPath())
}
4 changes: 4 additions & 0 deletions internal/run/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type Service struct {
states *state.Service

disableReloadAfterApply bool
dataDir string

*pubsub.Broker[*Run]
}
Expand All @@ -34,6 +35,7 @@ type ServiceOptions struct {
WorkspaceService *workspace.Service
StateService *state.Service
DisableReloadAfterApply bool
DataDir string
Logger logging.Interface
}

Expand All @@ -47,6 +49,7 @@ func NewService(opts ServiceOptions) *Service {
workspaces: opts.WorkspaceService,
states: opts.StateService,
disableReloadAfterApply: opts.DisableReloadAfterApply,
dataDir: opts.DataDir,
logger: opts.Logger,
}
}
Expand Down Expand Up @@ -75,6 +78,7 @@ func (s *Service) create(workspaceID resource.ID, opts CreateOptions) (*Run, err
opts.afterUpdate = func(run *Run) {
s.Publish(resource.UpdatedEvent, run)
}
opts.dataDir = s.dataDir
run, err := newRun(mod, ws, opts)
if err != nil {
return nil, fmt.Errorf("constructing run: %w", err)
Expand Down
8 changes: 0 additions & 8 deletions internal/workspace/workspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ import (
"fmt"
"log/slog"
"net/url"
"path/filepath"

"github.com/leg100/pug/internal"
"github.com/leg100/pug/internal/module"
"github.com/leg100/pug/internal/resource"
)
Expand Down Expand Up @@ -40,12 +38,6 @@ func (ws *Workspace) TerraformEnv() string {
return fmt.Sprintf("TF_WORKSPACE=%s", ws.Name)
}

// PugDirectory returns the workspace's pug directory, relative to its module
// directory.
func (ws *Workspace) PugDirectory() string {
return filepath.Join(internal.PugDirectory, ws.Name)
}

func (ws *Workspace) LogValue() slog.Value {
return slog.GroupValue(
slog.String("name", ws.Name),
Expand Down

0 comments on commit 05c039d

Please sign in to comment.