From acdbbd81b3d4ee83f6316016e78523e098f3e153 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacob=20L=C3=A4rfors?= Date: Wed, 29 Jun 2022 08:20:21 +0300 Subject: [PATCH] Add dev mode to Terraplate Significant refactoring needed, in order or dev mode to also be able to perform runs, and therefore made the build step another task in a run. --- .github/workflows/ci.yaml | 2 +- builder/build.go | 46 +-- cmd/build.go | 23 +- cmd/dev.go | 72 +++++ cmd/drift.go | 10 +- cmd/init.go | 26 +- cmd/messages.go | 6 +- cmd/plan.go | 60 +++- docs/assets/images/tp-dev-mode.png | Bin 0 -> 216113 bytes docs/commands/terraplate.md | 1 + docs/commands/terraplate_dev.md | 33 ++ docs/commands/terraplate_init.md | 6 +- docs/commands/terraplate_plan.md | 9 +- docs/index.md | 8 +- docs/tutorials/.pages | 1 + docs/tutorials/dev-mode.md | 38 +++ go.mod | 15 +- go.sum | 37 ++- main_test.go | 4 +- parser/parser.go | 9 +- parser/terraconfig.go | 3 +- runner/cmds.go | 132 +++++--- runner/opts.go | 108 +++++++ runner/result.go | 1 - runner/rootmodule.go | 79 +++++ runner/run.go | 229 ++++++++++---- runner/run_test.go | 50 --- runner/runner.go | 227 ++++++-------- runner/task.go | 30 +- runner/test.sh | 7 + runner/worker.go | 18 ++ tui/entryui/commands.go | 47 +++ tui/entryui/keys.go | 43 +++ tui/entryui/model.go | 197 ++++++++++++ tui/entryui/styles.go | 52 +++ tui/model.go | 73 +++++ tui/modulesui/columns.go | 45 +++ tui/modulesui/commands.go | 108 +++++++ tui/modulesui/item.go | 62 ++++ tui/modulesui/keys.go | 122 ++++++++ tui/modulesui/model.go | 487 +++++++++++++++++++++++++++++ tui/modulesui/state.go | 8 + tui/modulesui/styles.go | 45 +++ tui/state.go | 8 + 44 files changed, 2202 insertions(+), 385 deletions(-) create mode 100644 cmd/dev.go create mode 100644 docs/assets/images/tp-dev-mode.png create mode 100644 docs/commands/terraplate_dev.md create mode 100644 docs/tutorials/dev-mode.md create mode 100644 runner/opts.go delete mode 100644 runner/result.go create mode 100644 runner/rootmodule.go delete mode 100644 runner/run_test.go create mode 100644 runner/test.sh create mode 100644 runner/worker.go create mode 100644 tui/entryui/commands.go create mode 100644 tui/entryui/keys.go create mode 100644 tui/entryui/model.go create mode 100644 tui/entryui/styles.go create mode 100644 tui/model.go create mode 100644 tui/modulesui/columns.go create mode 100644 tui/modulesui/commands.go create mode 100644 tui/modulesui/item.go create mode 100644 tui/modulesui/keys.go create mode 100644 tui/modulesui/model.go create mode 100644 tui/modulesui/state.go create mode 100644 tui/modulesui/styles.go create mode 100644 tui/state.go diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index cee7bf6..adcec46 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -37,7 +37,7 @@ jobs: restore-keys: | ${{ runner.os }}-go- - name: Test - run: go test -failfast ./... + run: go test -v -failfast ./... # Build with GoReleaser (but don't release!). # This verifies that the GoReleaser config is valid and can build - name: Run GoReleaser diff --git a/builder/build.go b/builder/build.go index 6df8e23..503dd65 100644 --- a/builder/build.go +++ b/builder/build.go @@ -2,47 +2,44 @@ package builder import ( "fmt" + "io" "os" "path/filepath" "reflect" "sort" - "github.com/hashicorp/go-multierror" + "github.com/fatih/color" "github.com/hashicorp/hcl/v2/hclwrite" "github.com/verifa/terraplate/parser" "github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty/gocty" ) -// Build takes a TerraConfig as input and builds all the templates and terraform -// files -func Build(config *parser.TerraConfig) error { - var buildErrs error - for _, terrafile := range config.RootModules() { +var errorColor = color.New(color.FgRed, color.Bold) - if err := buildTerraplate(terrafile, config); err != nil { - buildErrs = multierror.Append(buildErrs, fmt.Errorf("building Terraplate Terraform file: %w", err)) - continue - } - - if err := buildTemplates(terrafile); err != nil { - buildErrs = multierror.Append(buildErrs, fmt.Errorf("building templates: %w", err)) - continue - } - - fmt.Printf("%s: Built %d templates\n", terrafile.RelativeDir(), len(terrafile.Templates)) +// BuildTerrafile takes an input Terrafile and builds it, writing any output +// to the provided io.Writer +func BuildTerrafile(tf *parser.Terrafile, out io.Writer) error { + if err := buildTerraplate(tf, out); err != nil { + buildErr := fmt.Errorf("building Terraplate Terraform file: %w", err) + fmt.Fprintf(out, "\n%s: %v", errorColor.Sprint("Error"), buildErr) + return buildErr } - if buildErrs != nil { - return fmt.Errorf("building root modules: %s", buildErrs) + if err := buildTemplates(tf, out); err != nil { + buildErr := fmt.Errorf("building templates: %w", err) + fmt.Fprintf(out, "\n%s: %v", errorColor.Sprint("Error"), buildErr) + return buildErr } - return nil } // buildTemplates builds the templates associated with the given terrafile -func buildTemplates(tf *parser.Terrafile) error { +func buildTemplates(tf *parser.Terrafile, out io.Writer) error { for _, tmpl := range tf.Templates { + target := filepath.Join(tf.Dir, tmpl.Target) + fmt.Fprintf(out, "Building template %s to %s\n", tmpl.Name, target) + data, dataErr := tf.BuildData() if dataErr != nil { return fmt.Errorf("getting build data for %s: %w", tf.Path, dataErr) @@ -56,7 +53,7 @@ func buildTemplates(tf *parser.Terrafile) error { continue } } - target := filepath.Join(tf.Dir, tmpl.Target) + content := defaultTemplateHeader(tf, tmpl) + tmpl.Contents if err := parser.TemplateWrite(data, tmpl.Name, content, target); err != nil { return fmt.Errorf("creating template %s in terrafile %s: %w", tmpl.Name, tf.RelativePath(), err) @@ -67,8 +64,11 @@ func buildTemplates(tf *parser.Terrafile) error { // buildTerraplate builds the terraplate terraform file which contains the // variables (with defaults) and terraform block -func buildTerraplate(terrafile *parser.Terrafile, config *parser.TerraConfig) error { +func buildTerraplate(terrafile *parser.Terrafile, out io.Writer) error { + path := filepath.Join(terrafile.Dir, "terraplate.tf") + fmt.Fprintf(out, "Building terraplate.tf file to %s\n", path) + // Create the Terraform file tfFile := hclwrite.NewEmptyFile() diff --git a/cmd/build.go b/cmd/build.go index 0081c09..60e489c 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -19,7 +19,6 @@ import ( "fmt" "github.com/spf13/cobra" - "github.com/verifa/terraplate/builder" "github.com/verifa/terraplate/parser" "github.com/verifa/terraplate/runner" ) @@ -40,23 +39,19 @@ templates and configurations detected.`, return fmt.Errorf("parsing terraplate: %w", err) } - if err := builder.Build(config); err != nil { - return fmt.Errorf("building terraplate: %w", err) + runOpts := []func(r *runner.TerraRunOpts){ + runner.RunBuild(), } - - fmt.Print(buildSuccessMessage) - if doValidate { - runner := runner.Run(config, runner.RunInit(), runner.RunValidate()) - // Print log - fmt.Println(runner.Log()) - - if runner.HasError() { - return runner.Errors() - } + runOpts = append(runOpts, runner.RunValidate()) } + runOpts = append(runOpts, runner.ExtraArgs(args)) + runner := runner.Run(config, runOpts...) + + fmt.Println(runner.Log()) + fmt.Println(runner.Summary()) - return nil + return runner.Errors() }, } diff --git a/cmd/dev.go b/cmd/dev.go new file mode 100644 index 0000000..c07cc72 --- /dev/null +++ b/cmd/dev.go @@ -0,0 +1,72 @@ +/* +Copyright © 2021 Verifa + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package cmd + +import ( + "fmt" + "io" + "os" + + tea "github.com/charmbracelet/bubbletea" + "github.com/spf13/cobra" + "github.com/verifa/terraplate/parser" + "github.com/verifa/terraplate/runner" + "github.com/verifa/terraplate/tui" +) + +// devCmd represents the plan command +var devCmd = &cobra.Command{ + Use: "dev", + Short: "Enters dev mode which launches a Terminal UI for Terraplate", + Long: `Enters dev mode which launches a Terminal UI for building and running Terraplate root modules.`, + RunE: func(cmd *cobra.Command, args []string) error { + // Parse + config, err := parser.Parse(&config.ParserConfig) + if err != nil { + return fmt.Errorf("parsing terraplate: %w", err) + } + + // Dev mode + fmt.Print(devStartMessage) + runOpts := []func(r *runner.TerraRunOpts){ + runner.RunBuild(), + runner.RunPlan(), + runner.RunShowPlan(), + runner.Jobs(planJobs), + // Discard any output from the runner itself. + // This does not discard the Terraform output. + runner.Output(io.Discard), + } + runOpts = append(runOpts, runner.ExtraArgs(args)) + runner := runner.New(config, runOpts...) + + p := tea.NewProgram( + tui.New(runner), + tea.WithAltScreen(), + tea.WithMouseCellMotion(), + ) + if err := p.Start(); err != nil { + fmt.Printf("Alas, there's been an error: %v", err) + os.Exit(1) + } + return nil + }, +} + +func init() { + RootCmd.AddCommand(devCmd) + devCmd.Flags().IntVarP(&planJobs, "jobs", "j", runner.DefaultJobs, "Number of concurrent terraform jobs to run at one time") +} diff --git a/cmd/drift.go b/cmd/drift.go index f6f12a1..f36a4b8 100644 --- a/cmd/drift.go +++ b/cmd/drift.go @@ -19,7 +19,6 @@ import ( "fmt" "github.com/spf13/cobra" - "github.com/verifa/terraplate/builder" "github.com/verifa/terraplate/notify" "github.com/verifa/terraplate/parser" "github.com/verifa/terraplate/runner" @@ -58,15 +57,10 @@ var driftCmd = &cobra.Command{ if err != nil { return fmt.Errorf("parsing terraplate: %w", err) } - // Build - fmt.Print(buildStartMessage) - if err := builder.Build(config); err != nil { - return err - } - fmt.Print(buildSuccessMessage) // Plan fmt.Print(terraformStartMessage) runOpts := []func(r *runner.TerraRunOpts){ + runner.RunBuild(), runner.RunInit(), runner.RunPlan(), runner.RunShowPlan(), @@ -92,7 +86,7 @@ var driftCmd = &cobra.Command{ } } - fmt.Print(runner.PlanSummary()) + fmt.Print(runner.Summary()) return nil }, } diff --git a/cmd/init.go b/cmd/init.go index b69bd29..a250c0d 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -23,7 +23,11 @@ import ( "github.com/verifa/terraplate/runner" ) -var initJobs int +var ( + initJobs int + initSkipBuild bool + initUpgrade bool +) // initCmd represents the init command var initCmd = &cobra.Command{ @@ -35,9 +39,23 @@ var initCmd = &cobra.Command{ if err != nil { return fmt.Errorf("parsing terraplate: %w", err) } - runner := runner.Run(config, runner.RunInit(), runner.Jobs(initJobs), runner.ExtraArgs(args)) - // Print log + runOpts := []func(r *runner.TerraRunOpts){ + runner.RunInit(), + runner.Jobs(initJobs), + runner.ExtraArgs(args), + } + + if !initSkipBuild { + runOpts = append(runOpts, runner.RunBuild()) + } + if initUpgrade { + runOpts = append(runOpts, runner.RunInitUpgrade()) + } + + runOpts = append(runOpts, runner.ExtraArgs(args)) + runner := runner.Run(config, runOpts...) fmt.Println(runner.Log()) + fmt.Println(runner.Summary()) return runner.Errors() }, @@ -46,5 +64,7 @@ var initCmd = &cobra.Command{ func init() { RootCmd.AddCommand(initCmd) + initCmd.Flags().BoolVar(&initSkipBuild, "skip-build", false, "Skip build process (default: false)") + initCmd.Flags().BoolVarP(&initUpgrade, "upgrade", "u", false, "Perform upgrade when initializing") initCmd.Flags().IntVarP(&initJobs, "jobs", "j", runner.DefaultJobs, "Number of concurrent terraform jobs to run at one time") } diff --git a/cmd/messages.go b/cmd/messages.go index 706ffb9..2bb746b 100644 --- a/cmd/messages.go +++ b/cmd/messages.go @@ -1,16 +1,12 @@ package cmd import ( - "fmt" - "github.com/fatih/color" ) var ( - successColor = color.New(color.FgGreen, color.Bold) errorColor = color.New(color.FgRed, color.Bold) boldText = color.New(color.Bold) - buildStartMessage = boldText.Sprint("\nBuilding root modules...\n\n") - buildSuccessMessage = fmt.Sprintf("\n%s All root modules built\n\n", successColor.Sprint("Success!")) terraformStartMessage = boldText.Sprint("\nTerraforming root modules...\n\n") + devStartMessage = boldText.Sprint("\nStarting dev mode...\n\n") ) diff --git a/cmd/plan.go b/cmd/plan.go index 1b41154..03d9180 100644 --- a/cmd/plan.go +++ b/cmd/plan.go @@ -17,17 +17,21 @@ package cmd import ( "fmt" + "io" + "os" + tea "github.com/charmbracelet/bubbletea" "github.com/spf13/cobra" - "github.com/verifa/terraplate/builder" "github.com/verifa/terraplate/parser" "github.com/verifa/terraplate/runner" + "github.com/verifa/terraplate/tui" ) var ( - runBuild bool - runInit bool - planJobs int + planSkipBuild bool + runInit bool + planJobs int + planDevMode bool ) // planCmd represents the plan command @@ -40,38 +44,62 @@ var planCmd = &cobra.Command{ if err != nil { return fmt.Errorf("parsing terraplate: %w", err) } - if runBuild { - fmt.Print(buildStartMessage) - if err := builder.Build(config); err != nil { - return err - } - fmt.Print(buildSuccessMessage) - } fmt.Print(terraformStartMessage) runOpts := []func(r *runner.TerraRunOpts){ runner.RunPlan(), runner.RunShowPlan(), runner.Jobs(planJobs), } + if !planSkipBuild { + runOpts = append(runOpts, runner.RunBuild()) + } if runInit { runOpts = append(runOpts, runner.RunInit()) } runOpts = append(runOpts, runner.ExtraArgs(args)) - runner := runner.Run(config, runOpts...) + r := runner.Run(config, runOpts...) + + if planDevMode { + // Start dev mode + fmt.Print(devStartMessage) + runOpts := []func(r *runner.TerraRunOpts){ + runner.RunBuild(), + runner.RunPlan(), + runner.RunShowPlan(), + runner.Jobs(planJobs), + // Discard any output from the runner itself. + // This does not discard the Terraform output. + runner.Output(io.Discard), + } + // Override options in runner + r.Opts = runner.NewOpts(runOpts...) + p := tea.NewProgram( + tui.New(r), + tea.WithAltScreen(), + tea.WithMouseCellMotion(), + ) + if err := p.Start(); err != nil { + fmt.Printf("Alas, there's been an error: %v", err) + os.Exit(1) + } + + return nil + } // Print log - fmt.Println(runner.Log()) + fmt.Println(r.Log()) - fmt.Println(runner.PlanSummary()) + fmt.Println(r.Summary()) - return runner.Errors() + return r.Errors() }, } func init() { RootCmd.AddCommand(planCmd) - planCmd.Flags().BoolVar(&runBuild, "build", false, "Run build process also") + planCmd.Flags().BoolVar(&planSkipBuild, "skip-build", false, "Skip build process (default: false)") planCmd.Flags().BoolVar(&runInit, "init", false, "Run terraform init also") + planCmd.Flags().BoolVar(&planDevMode, "dev", false, "Start dev mode after plan finishes") planCmd.Flags().IntVarP(&planJobs, "jobs", "j", runner.DefaultJobs, "Number of concurrent terraform jobs to run at one time") } diff --git a/docs/assets/images/tp-dev-mode.png b/docs/assets/images/tp-dev-mode.png new file mode 100644 index 0000000000000000000000000000000000000000..5fc4b07fe2466a5a56e5842c9eda34284e54be92 GIT binary patch literal 216113 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

y^X0xvV5RX8y|U#d!me<3OLK9dJdf# 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 literal 0 HcmV?d00001 diff --git a/docs/commands/terraplate.md b/docs/commands/terraplate.md index 7945b05..b9fe264 100644 --- a/docs/commands/terraplate.md +++ b/docs/commands/terraplate.md @@ -25,6 +25,7 @@ Terraform configurations like providers and backend. * [terraplate apply](terraplate_apply.md) - Runs terraform apply on all subdirectories * [terraplate build](terraplate_build.md) - Build Terraform files based your Terrafiles +* [terraplate dev](terraplate_dev.md) - Enters dev mode which launches a Terminal UI for Terraplate * [terraplate drift](terraplate_drift.md) - Detect drift in your infrastructure (experimental feature) * [terraplate init](terraplate_init.md) - Runs terraform init on all subdirectories * [terraplate parse](terraplate_parse.md) - Parse the terraplate files and print a summary diff --git a/docs/commands/terraplate_dev.md b/docs/commands/terraplate_dev.md new file mode 100644 index 0000000..76d5cc8 --- /dev/null +++ b/docs/commands/terraplate_dev.md @@ -0,0 +1,33 @@ +--- +# # AUTOMATICALLY GENERATED BY COBRA (DO NOT EDIT) +title: "terraplate dev" +--- +## terraplate dev + +Enters dev mode which launches a Terminal UI for Terraplate + +### Synopsis + +Enters dev mode which launches a Terminal UI for building and running Terraplate root modules. + +``` +terraplate dev [flags] +``` + +### Options + +``` + -h, --help help for dev + -j, --jobs int Number of concurrent terraform jobs to run at one time (default 4) +``` + +### Options inherited from parent commands + +``` + -C, --chdir string Switch to a different working directory before executing the given subcommand. (default ".") +``` + +### SEE ALSO + +* [terraplate](terraplate.md) - DRY Terraform using Go Templates + diff --git a/docs/commands/terraplate_init.md b/docs/commands/terraplate_init.md index 27de9e0..743e244 100644 --- a/docs/commands/terraplate_init.md +++ b/docs/commands/terraplate_init.md @@ -17,8 +17,10 @@ terraplate init [flags] ### Options ``` - -h, --help help for init - -j, --jobs int Number of concurrent terraform jobs to run at one time (default 4) + -h, --help help for init + -j, --jobs int Number of concurrent terraform jobs to run at one time (default 4) + --skip-build Skip build process (default: false) + -u, --upgrade Perform upgrade when initializing ``` ### Options inherited from parent commands diff --git a/docs/commands/terraplate_plan.md b/docs/commands/terraplate_plan.md index b0dbd5d..94b6fc6 100644 --- a/docs/commands/terraplate_plan.md +++ b/docs/commands/terraplate_plan.md @@ -17,10 +17,11 @@ terraplate plan [flags] ### Options ``` - --build Run build process also - -h, --help help for plan - --init Run terraform init also - -j, --jobs int Number of concurrent terraform jobs to run at one time (default 4) + --dev Start dev mode after plan finishes + -h, --help help for plan + --init Run terraform init also + -j, --jobs int Number of concurrent terraform jobs to run at one time (default 4) + --skip-build Skip build process (default: false) ``` ### Options inherited from parent commands diff --git a/docs/index.md b/docs/index.md index f666402..b2fc5e2 100644 --- a/docs/index.md +++ b/docs/index.md @@ -49,7 +49,7 @@ There are existing approaches to running Terraform (e.g. init, plan, apply) over Terraplate's templating is compatible with all the approaches above (in fact Terraplate can template your Terragrunt files! But let's not go there...). That means you can use Terraplate for **just templating and keeping things DRY** but it also comes with a thin wrapper for invoking Terraform (similar to Terragrunt). -### Drift detection (coming soon) +### Drift detection Terraplate can be run on a regular basis and detect drift in your infrastructure and create notifications. Currenly only Slack will be supported, but we welcome ideas/suggestions for more! @@ -65,6 +65,12 @@ The two main benefits Terraplate brings is: 1. Keeping your code DRY and more maintainable 2. Improve developer productivity by spending less time writing boilerplate and running Terraform across all your Root Modules +#### Dev mode + +Terraplate has a "dev" mode which launches a Terminal UI, for browsing Root modules and running commands: + +![dev-mode](../assets/images/tp-dev-mode.png) + ### Terraform users who want to make [Workspaces](https://www.terraform.io/cli/workspaces) more DRY or avoid them If you don't find workspaces completely solves the issue of DRY infra, or they are not right for you, Terraplate is worth considering. diff --git a/docs/tutorials/.pages b/docs/tutorials/.pages index 2b8be8f..3eb346a 100644 --- a/docs/tutorials/.pages +++ b/docs/tutorials/.pages @@ -1,4 +1,5 @@ arrange: - multiple-root-modules.md +- dev-mode.md - drift-slack-notifications.md diff --git a/docs/tutorials/dev-mode.md b/docs/tutorials/dev-mode.md new file mode 100644 index 0000000..59c9fb3 --- /dev/null +++ b/docs/tutorials/dev-mode.md @@ -0,0 +1,38 @@ +--- +title: "Dev mode (TUI)" +description: "Dev mode launches Terraplate's Terminal UI" +--- + +This tutorial gives a *very* quick overview of Terraplate's `dev` mode. + +## Dev mode + +Terraplate's `dev` mode launches a Terminal UI for working with multiple root modules and executing Terraform via Terraplate. + +![dev-mode](../assets/images/tp-dev-mode.png) + +## Entering dev mode + +You can either run `terraplate dev` or `terraplate plan --dev` to start dev mode. + +With the `dev` subcommand the TUI starts up immediately. +With the `plan --dev` subcommand, the plan finishes before starting the TUI. + +## Command mode + +Once in dev mode, you can press `:` to enter command mode, and you can do the following: + +- `b` for build +- `i` for init +- `u` for init -upgrade +- `p` for plan +- `a` for apply +- `A` for all (run over all visible root modules) + +Finally press enter to run the commands over the root module(s). + +For example the following keypresses will build, init (with upgrade) and plan all visible root modules: `:bupA`. + +## Help + +Check the help at the bottom of the TUI. You can press `?` to get the extended help. diff --git a/go.mod b/go.mod index a3a35ae..4922868 100644 --- a/go.mod +++ b/go.mod @@ -4,13 +4,16 @@ go 1.17 require ( github.com/Masterminds/sprig/v3 v3.2.2 + github.com/charmbracelet/bubbles v0.13.0 + github.com/charmbracelet/bubbletea v0.21.0 + github.com/charmbracelet/lipgloss v0.5.0 github.com/fatih/color v1.13.0 github.com/go-git/go-git/v5 v5.4.2 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/hcl/v2 v2.11.1 github.com/hashicorp/terraform-json v0.14.0 github.com/imdario/mergo v0.3.12 - github.com/remeh/sizedwaitgroup v1.0.0 + github.com/muesli/termenv v0.12.0 github.com/slack-go/slack v0.10.3 github.com/spf13/cobra v1.3.0 github.com/stretchr/testify v1.7.0 @@ -26,6 +29,8 @@ require ( github.com/acomagu/bufpipe v1.0.3 // indirect github.com/agext/levenshtein v1.2.3 // indirect github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect + github.com/atotto/clipboard v0.1.4 // indirect + github.com/containerd/console v1.0.3 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/emirpasic/gods v1.12.0 // indirect @@ -40,15 +45,21 @@ require ( github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect - github.com/kylelemons/godebug v1.1.0 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect + github.com/muesli/cancelreader v0.2.0 // indirect + github.com/muesli/reflow v0.3.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rivo/uniseg v0.2.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/sahilm/fuzzy v0.1.0 // indirect github.com/sergi/go-diff v1.1.0 // indirect github.com/shopspring/decimal v1.2.0 // indirect github.com/spf13/cast v1.4.1 // indirect diff --git a/go.sum b/go.sum index 72512da..8daae5d 100644 --- a/go.sum +++ b/go.sum @@ -85,6 +85,8 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= +github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= @@ -94,6 +96,13 @@ github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/charmbracelet/bubbles v0.13.0 h1:zP/ROH3wJEBqZWKIsD50ZKKlx3ydLInq3LdD/Nrlb8w= +github.com/charmbracelet/bubbles v0.13.0/go.mod h1:bbeTiXwPww4M031aGi8UK2HT9RDWoiNibae+1yCMtcc= +github.com/charmbracelet/bubbletea v0.21.0 h1:f3y+kanzgev5PA916qxmDybSHU3N804uOnKnhRPXTcI= +github.com/charmbracelet/bubbletea v0.21.0/go.mod h1:GgmJMec61d08zXsOhqRC/AiOx4K4pmz+VIcRIm1FKr4= +github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= +github.com/charmbracelet/lipgloss v0.5.0 h1:lulQHuVeodSgDez+3rGiuxlPVXSnhth442DATR2/8t8= +github.com/charmbracelet/lipgloss v0.5.0/go.mod h1:EZLha/HbzEt7cYqdFPovlqy5FZPj0xFhg5SaqxScmgs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -110,6 +119,8 @@ github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= +github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU= @@ -321,6 +332,8 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= @@ -338,6 +351,10 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= @@ -363,6 +380,17 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34= +github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= +github.com/muesli/cancelreader v0.2.0 h1:SOpr+CfyVNce341kKqvbhhzQhBPyJRXQaCtn03Pae1Q= +github.com/muesli/cancelreader v0.2.0/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= +github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ= +github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= +github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= +github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= +github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= +github.com/muesli/termenv v0.12.0 h1:KuQRUE3PgxRFWhq4gHvZtPSLCGDqM5q/cYr1pZ39ytc= +github.com/muesli/termenv v0.12.0/go.mod h1:WCCv32tusQ/EEZ5S8oUIIrC/nIuBcxCVqlN4Xfkv+7A= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= @@ -389,14 +417,17 @@ github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8b github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/remeh/sizedwaitgroup v1.0.0 h1:VNGGFwNo/R5+MJBf6yrsr110p0m4/OX4S3DCy7Kyl5E= -github.com/remeh/sizedwaitgroup v1.0.0/go.mod h1:3j2R4OIe/SeS6YDhICBy22RWjJC5eNCJ1V+9+NVNYlo= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= +github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI= +github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= @@ -631,6 +662,7 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -655,6 +687,7 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158 h1:rm+CHSpPEEW2IsXUib1ThaHIjuBVZjxNgSKmBLFfD4c= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= diff --git a/main_test.go b/main_test.go index f36edd1..6bb07da 100644 --- a/main_test.go +++ b/main_test.go @@ -4,7 +4,6 @@ import ( "testing" "github.com/stretchr/testify/require" - "github.com/verifa/terraplate/builder" "github.com/verifa/terraplate/parser" "github.com/verifa/terraplate/runner" ) @@ -43,11 +42,10 @@ func TestMain(t *testing.T) { Chdir: tc.dir, }) require.NoError(t, err) - buildErr := builder.Build(config) - require.NoError(t, buildErr) if !tc.skipTerraform { runner := runner.Run(config, + runner.RunBuild(), runner.RunValidate(), runner.RunInit(), runner.RunPlan(), diff --git a/parser/parser.go b/parser/parser.go index d8f2d9b..80fce1b 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -43,8 +43,15 @@ func Parse(config *Config) (*TerraConfig, error) { return nil, errors.New("no terraplate files found") } + // Get the absolute path to set as working directory + absPath, absErr := filepath.Abs(config.Chdir) + if absErr != nil { + return nil, fmt.Errorf("getting absolute path for working directory %s: %w", config.Chdir, absErr) + } + tfc := TerraConfig{ - Terrafiles: terrafiles, + Terrafiles: terrafiles, + WorkingDirectory: absPath, } // Terrafiles inherit values from ancestors. Let's resolve the root modules diff --git a/parser/terraconfig.go b/parser/terraconfig.go index 48e27be..d236d6e 100644 --- a/parser/terraconfig.go +++ b/parser/terraconfig.go @@ -7,7 +7,8 @@ import ( ) type TerraConfig struct { - Terrafiles []*Terrafile + Terrafiles []*Terrafile + WorkingDirectory string } // RootModules returns the Terrafiles that are considered root modules diff --git a/runner/cmds.go b/runner/cmds.go index a70e4f2..97402f4 100644 --- a/runner/cmds.go +++ b/runner/cmds.go @@ -1,32 +1,72 @@ package runner import ( - "context" "fmt" - "os" + "io" + "log" "os/exec" - "os/signal" - "syscall" "time" + "github.com/verifa/terraplate/builder" "github.com/verifa/terraplate/parser" "golang.org/x/text/cases" "golang.org/x/text/language" ) -func initCmd(run TerraRunOpts, tf *parser.Terrafile) *TaskResult { +type terraCmd string + +func (t terraCmd) Action() string { + switch t { + case terraBuild: + return "building" + case terraValidate: + return "validating" + case terraInit: + return "initializing" + case terraPlan: + return "planning" + case terraApply: + return "applying" + case terraShowPlan: + return "summarizing" + } + return "Unknown action" +} + +const ( + terraExe = "terraform" + + terraBuild terraCmd = "build" + terraValidate terraCmd = "validate" + terraInit terraCmd = "init" + terraPlan terraCmd = "plan" + terraApply terraCmd = "apply" + terraShowPlan terraCmd = "show" +) + +func buildCmd(opts TerraRunOpts, tf *parser.Terrafile) *TaskResult { + var task TaskResult + task.TerraCmd = terraBuild + task.Error = builder.BuildTerrafile(tf, &task.Output) + return &task +} + +func validateCmd(opts TerraRunOpts, tf *parser.Terrafile) *TaskResult { var args []string - args = append(args, tfCleanExtraArgs(run.extraArgs)...) - return runCmd(tf, terraInit, args) + args = append(args, tfCleanExtraArgs(opts.extraArgs)...) + return runCmd(opts.out, tf, terraValidate, args) } -func validateCmd(run TerraRunOpts, tf *parser.Terrafile) *TaskResult { +func initCmd(opts TerraRunOpts, tf *parser.Terrafile) *TaskResult { var args []string - args = append(args, tfCleanExtraArgs(run.extraArgs)...) - return runCmd(tf, terraValidate, args) + if opts.initUpgrade { + args = append(args, "-upgrade") + } + args = append(args, tfCleanExtraArgs(opts.extraArgs)...) + return runCmd(opts.out, tf, terraInit, args) } -func planCmd(run TerraRunOpts, tf *parser.Terrafile) *TaskResult { +func planCmd(opts TerraRunOpts, tf *parser.Terrafile) *TaskResult { plan := tf.ExecBlock.PlanBlock var args []string @@ -40,11 +80,11 @@ func planCmd(run TerraRunOpts, tf *parser.Terrafile) *TaskResult { ) } args = append(args, tfCleanExtraArgs(tf.ExecBlock.ExtraArgs)...) - args = append(args, tfCleanExtraArgs(run.extraArgs)...) - return runCmd(tf, terraPlan, args) + args = append(args, tfCleanExtraArgs(opts.extraArgs)...) + return runCmd(opts.out, tf, terraPlan, args) } -func showPlanCmd(run TerraRunOpts, tf *parser.Terrafile) *TaskResult { +func showPlanCmd(opts TerraRunOpts, tf *parser.Terrafile) *TaskResult { plan := tf.ExecBlock.PlanBlock if plan.SkipOut { return &TaskResult{ @@ -54,10 +94,10 @@ func showPlanCmd(run TerraRunOpts, tf *parser.Terrafile) *TaskResult { } var args []string args = append(args, "-json", plan.Out) - return runCmd(tf, terraShowPlan, args) + return runCmd(opts.out, tf, terraShowPlan, args) } -func applyCmd(run TerraRunOpts, tf *parser.Terrafile) *TaskResult { +func applyCmd(opts TerraRunOpts, tf *parser.Terrafile) *TaskResult { plan := tf.ExecBlock.PlanBlock var args []string @@ -66,35 +106,49 @@ func applyCmd(run TerraRunOpts, tf *parser.Terrafile) *TaskResult { fmt.Sprintf("-input=%v", plan.Input), ) args = append(args, tfCleanExtraArgs(tf.ExecBlock.ExtraArgs)...) - args = append(args, tfCleanExtraArgs(run.extraArgs)...) + args = append(args, tfCleanExtraArgs(opts.extraArgs)...) if !plan.SkipOut { args = append(args, plan.Out) } - return runCmd(tf, terraApply, args) + return runCmd(opts.out, tf, terraApply, args) } -func runCmd(tf *parser.Terrafile, tfCmd terraCmd, args []string) *TaskResult { - result := TaskResult{ +func runCmd(out io.Writer, tf *parser.Terrafile, tfCmd terraCmd, args []string) *TaskResult { + task := TaskResult{ TerraCmd: tfCmd, } cmdArgs := append(tfArgs(tf), string(tfCmd)) cmdArgs = append(cmdArgs, args...) - result.ExecCmd = exec.Command(terraExe, cmdArgs...) + task.ExecCmd = exec.Command(terraExe, cmdArgs...) // Create channel and start progress printer done := make(chan bool) - go printProgress(tf.RelativeDir(), tfCmd, done) + go printProgress(out, tf.Dir, tfCmd, done) defer func() { done <- true }() - var runErr error - result.Output, runErr = result.ExecCmd.CombinedOutput() + pr, pw := io.Pipe() + defer pw.Close() + task.ExecCmd.Stdout = pw + task.ExecCmd.Stderr = pw + + if err := task.ExecCmd.Start(); err != nil { + task.Error = fmt.Errorf("starting command: %w", err) + return &task + } + go func() { + if _, err := io.Copy(&task.Output, pr); err != nil { + log.Fatal(err) + } + }() + + runErr := task.ExecCmd.Wait() if runErr != nil { - result.Error = fmt.Errorf("%s: running %s command", tf.RelativeDir(), tfCmd) + task.Error = fmt.Errorf("%s: running %s command", tf.Dir, tfCmd) } - return &result + return &task } func tfArgs(tf *parser.Terrafile) []string { @@ -115,27 +169,7 @@ func tfCleanExtraArgs(args []string) []string { return cleanArgs } -// listenTerminateSignals returns a context that will be cancelled if an interrupt -// or termination signal is received. The context can be used to prevent further -// runs from being scheduled -func listenTerminateSignals() context.Context { - ctx, cancel := context.WithCancel(context.Background()) - signals := make(chan os.Signal, 1) - signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM) - go func() { - for { - <-signals - fmt.Println("") - fmt.Println("Terraplate: Interrupt received.") - fmt.Println("Terraplate: Sending interrupt to all Terraform processes and cancelling any queued runs.") - // Cancel the context, to stop any more runs from being executed - cancel() - } - }() - return ctx -} - -func printProgress(path string, cmd terraCmd, done <-chan bool) { +func printProgress(out io.Writer, path string, cmd terraCmd, done <-chan bool) { var ( interval = time.Second * 10 ticker = time.NewTicker(interval) @@ -143,12 +177,12 @@ func printProgress(path string, cmd terraCmd, done <-chan bool) { ) defer ticker.Stop() // Print initial line - fmt.Printf("%s: %s...\n", path, cases.Title(language.English).String(cmd.Action())) + fmt.Fprintf(out, "%s: %s...\n", path, cases.Title(language.English).String(cmd.Action())) for { select { case <-ticker.C: elapsed += interval - fmt.Printf("%s: Still %s... [%s elapsed]\n", path, cmd.Action(), elapsed) + fmt.Fprintf(out, "%s: Still %s... [%s elapsed]\n", path, cmd.Action(), elapsed) case <-done: return } diff --git a/runner/opts.go b/runner/opts.go new file mode 100644 index 0000000..dc67a72 --- /dev/null +++ b/runner/opts.go @@ -0,0 +1,108 @@ +package runner + +import ( + "io" + "os" +) + +func Jobs(jobs int) func(r *TerraRunOpts) { + return func(r *TerraRunOpts) { + r.jobs = jobs + } +} + +func RunBuild() func(r *TerraRunOpts) { + return func(r *TerraRunOpts) { + r.build = true + } +} + +func RunValidate() func(r *TerraRunOpts) { + return func(r *TerraRunOpts) { + r.validate = true + } +} + +func RunInit() func(r *TerraRunOpts) { + return func(r *TerraRunOpts) { + r.init = true + } +} + +func RunInitUpgrade() func(r *TerraRunOpts) { + return func(r *TerraRunOpts) { + r.init = true + r.initUpgrade = true + } +} + +func RunPlan() func(r *TerraRunOpts) { + return func(r *TerraRunOpts) { + r.plan = true + } +} + +func RunShowPlan() func(r *TerraRunOpts) { + return func(r *TerraRunOpts) { + r.showPlan = true + } +} + +func RunApply() func(r *TerraRunOpts) { + return func(r *TerraRunOpts) { + r.apply = true + } +} + +func Output(out io.Writer) func(r *TerraRunOpts) { + return func(r *TerraRunOpts) { + r.out = out + } +} + +func ExtraArgs(extraArgs []string) func(r *TerraRunOpts) { + return func(r *TerraRunOpts) { + r.extraArgs = extraArgs + } +} + +func FromOpts(opts TerraRunOpts) func(r *TerraRunOpts) { + return func(r *TerraRunOpts) { + r.jobs = opts.jobs + r.out = opts.out + } +} + +func NewOpts(opts ...func(r *TerraRunOpts)) TerraRunOpts { + // Initialise TerraRunOpts with defaults + runOpts := TerraRunOpts{ + jobs: DefaultJobs, + } + for _, opt := range opts { + opt(&runOpts) + } + + // Set default output + if runOpts.out == nil { + runOpts.out = os.Stdout + } + return runOpts +} + +// TerraRunOpts handles running Terraform over the root modules +type TerraRunOpts struct { + out io.Writer + + build bool + validate bool + init bool + initUpgrade bool + plan bool + showPlan bool + apply bool + + // Max number of concurrent jobs allowed + jobs int + // Terraform command flags + extraArgs []string +} diff --git a/runner/result.go b/runner/result.go deleted file mode 100644 index 75c10db..0000000 --- a/runner/result.go +++ /dev/null @@ -1 +0,0 @@ -package runner diff --git a/runner/rootmodule.go b/runner/rootmodule.go new file mode 100644 index 0000000..841b34b --- /dev/null +++ b/runner/rootmodule.go @@ -0,0 +1,79 @@ +package runner + +import ( + "errors" + "sync" + + "github.com/verifa/terraplate/parser" +) + +var ( + ErrRunInProgress = errors.New("run is already in progress") + ErrRunSkipped = errors.New("cannot run skipped module") +) + +// func newRootModule(tf *parser.Terrafile, opts TerraRunOpts) *RootModule { +func newRootModule(tf *parser.Terrafile, opts TerraRunOpts) *RootModule { + return &RootModule{ + Terrafile: tf, + Opts: opts, + } +} + +type RootModule struct { + // Terrafile is the terrafile for which this run was executed + Terrafile *parser.Terrafile + Opts TerraRunOpts + + // Run stores the current run, if one has been scheduled + Run *TerraRun + mu sync.RWMutex +} + +// ScheduleRun schedules a run on the RootModule by setting the state and +// adding to the waitgroup. +// If a run is already in progress an error is returned and the state is unchanged +func (r *RootModule) ScheduleRun(runQueue chan *TerraRun) error { + return r.ScheduleRunWithOpts(runQueue, r.Opts) +} + +// ScheduleRun schedules a run on the RootModule by setting the state and +// adding to the waitgroup. +// If a run is already in progress an error is returned and the state is unchanged +func (r *RootModule) ScheduleRunWithOpts(runQueue chan *TerraRun, opts TerraRunOpts) error { + // Don't schedule runs for modules that should be skipped + if r.Skip() { + return ErrRunSkipped + } + r.mu.Lock() + defer r.mu.Unlock() + + if r.Run != nil && r.Run.IsRunning() { + return ErrRunInProgress + } + newRun := newRunForQueue(r.Terrafile, opts) + r.Run = newRun + runQueue <- newRun + return nil +} + +func (r *RootModule) Wait() { + if r.Run != nil { + r.Run.Wait() + } +} + +func (r *RootModule) Skip() bool { + return r.Terrafile.ExecBlock.Skip +} + +func (r *RootModule) IsRunning() bool { + if r.Run == nil { + return false + } + return r.Run.IsRunning() +} + +func (r *RootModule) HasRun() bool { + return r.Run != nil +} diff --git a/runner/run.go b/runner/run.go index b1ba953..acdf979 100644 --- a/runner/run.go +++ b/runner/run.go @@ -1,124 +1,158 @@ package runner import ( - "errors" "fmt" + "strings" "sync" tfjson "github.com/hashicorp/terraform-json" "github.com/verifa/terraplate/parser" ) -var ( - ErrRunInProgress = errors.New("run is already in progress") +type state int + +const ( + finishedState state = iota + queueState + runState ) +func newRunForQueue(tf *parser.Terrafile, opts TerraRunOpts) *TerraRun { + + var r TerraRun + r.Terrafile = tf + r.Opts = opts + r.state = queueState + // Increment waitgroup so that we can wait on this run, even whilst it is + // queueing, until it is finished + r.wg.Add(1) + return &r +} + type TerraRun struct { // Terrafile is the terrafile for which this run was executed Terrafile *parser.Terrafile + Opts TerraRunOpts Tasks []*TaskResult Cancelled bool - Skipped bool Plan *tfjson.Plan PlanText []byte - mu sync.RWMutex - isRunning bool + wg sync.WaitGroup + state state } -// Run performs the run for this TerraRun i.e. invoking Terraform -func (r *TerraRun) Run(opts TerraRunOpts) error { - if startErr := r.startRun(); startErr != nil { - return startErr - } +// Run performs a blocking run for this TerraRun i.e. invoking Terraform +func (r *TerraRun) Run() { + r.Start() + r.Wait() +} + +// Start performs a non-blocking run for this TerraRun i.e. invoking Terraform +func (r *TerraRun) Start() { + r.startRun() + defer r.endRun() tf := r.Terrafile - // Check if root module should be skipped or not - if tf.ExecBlock.Skip { - fmt.Printf("%s: Skipping...\n", tf.RelativeDir()) - r.Skipped = true - return nil - } - if opts.init { - taskResult := initCmd(opts, tf) + if r.Opts.build { + taskResult := buildCmd(r.Opts, tf) r.Tasks = append(r.Tasks, taskResult) if taskResult.HasError() { - return nil + return } } - if opts.validate { - taskResult := validateCmd(opts, tf) + if r.Opts.init { + taskResult := initCmd(r.Opts, tf) r.Tasks = append(r.Tasks, taskResult) if taskResult.HasError() { - return nil + return } } - if opts.plan { - taskResult := planCmd(opts, tf) + if r.Opts.validate { + taskResult := validateCmd(r.Opts, tf) r.Tasks = append(r.Tasks, taskResult) if taskResult.HasError() { - return nil + return } } - if opts.showPlan { - taskResult := showPlanCmd(opts, tf) + if r.Opts.plan { + taskResult := planCmd(r.Opts, tf) + r.Tasks = append(r.Tasks, taskResult) + if taskResult.HasError() { + return + } + } + if r.Opts.showPlan { + taskResult := showPlanCmd(r.Opts, tf) r.ProcessPlan(taskResult) r.Tasks = append(r.Tasks, taskResult) if taskResult.HasError() { - return nil + return } } - if opts.apply { - taskResult := applyCmd(opts, tf) + if r.Opts.apply { + taskResult := applyCmd(r.Opts, tf) r.Tasks = append(r.Tasks, taskResult) if taskResult.HasError() { - return nil + return } } +} - r.endRun() - return nil +// Wait blocks and waits for the run to be finished +func (r *TerraRun) Wait() { + r.wg.Wait() } -func (r *TerraRun) startRun() error { - r.mu.Lock() - defer r.mu.Unlock() - if r.isRunning { - return ErrRunInProgress - } - r.isRunning = true - return nil +func (r *TerraRun) startRun() { + r.state = runState } func (r *TerraRun) endRun() { - r.mu.Lock() - defer r.mu.Unlock() - r.isRunning = false + defer r.wg.Done() + r.state = finishedState } -// PlanSummary returns a string summary to show after a plan -func (r *TerraRun) PlanSummary() string { - // If the run had errors, we want to show that - if r.HasError() { - return errorColor.Sprint("Error occurred") +func (r *TerraRun) Log(fullLog bool) string { + var log strings.Builder + log.WriteString(boldColor.Sprintf("Run for %s\n\n", r.Terrafile.Dir)) + + for _, task := range r.Tasks { + if fullLog || task.IsRelevant() { + log.WriteString(task.Log()) + } } - if r.Cancelled { + return log.String() +} + +// Summary returns a string summary to show after a plan +func (r *TerraRun) Summary() string { + switch { + case r.HasError(): + return errorColor.Sprint("Error occurred") + case r.Cancelled: return runCancelled.Sprint("Cancelled") + case r.IsApplied(): + return boldColor.Sprint("Applied") + case r.IsPlanned(): + if !r.HasPlan() { + return planNotAvailable.Sprint("Plan not available") + } + return r.Drift().Diff() + case r.IsInitd(): + return boldColor.Sprint("Initialized") + case r.IsBuilt(): + return boldColor.Sprint("Built") + default: + return "Unknown status" } - if r.Skipped { - return "Skipped" - } - if !r.HasPlan() { - return planNotAvailable.Sprint("Plan not available") - } - return r.Drift().Diff() } func (r *TerraRun) Drift() *Drift { - if !r.HasPlan() { + if r == nil || !r.HasPlan() { // Return an empty drift which means no drift (though user should check // if plan was available as well) return &Drift{} @@ -126,7 +160,77 @@ func (r *TerraRun) Drift() *Drift { return driftFromPlan(r.Plan) } +func (r *TerraRun) IsApplied() bool { + if r == nil { + return false + } + if r.state != finishedState { + return false + } + for _, task := range r.Tasks { + if task.TerraCmd == terraApply { + return true + } + } + return false +} + +func (r *TerraRun) IsPlanned() bool { + if r == nil { + return false + } + if r.state != finishedState { + return false + } + for _, task := range r.Tasks { + if task.TerraCmd == terraPlan { + return true + } + } + return false +} + +func (r *TerraRun) IsInitd() bool { + if r == nil { + return false + } + if r.state != finishedState { + return false + } + for _, task := range r.Tasks { + if task.TerraCmd == terraInit { + return true + } + } + return false +} + +func (r *TerraRun) IsBuilt() bool { + if r == nil { + return false + } + if r.state != finishedState { + return false + } + for _, task := range r.Tasks { + if task.TerraCmd == terraBuild { + return true + } + } + return false +} + +func (r *TerraRun) IsRunning() bool { + if r == nil { + return false + } + return r.state == queueState || r.state == runState +} + func (r *TerraRun) HasError() bool { + if r == nil { + return false + } for _, task := range r.Tasks { if task.HasError() { return true @@ -147,7 +251,7 @@ func (r *TerraRun) Errors() []error { func (r *TerraRun) HasRelevantTasks() bool { for _, task := range r.Tasks { - if task.HasRelevance() { + if task.IsRelevant() { return true } } @@ -155,6 +259,9 @@ func (r *TerraRun) HasRelevantTasks() bool { } func (r *TerraRun) HasPlan() bool { + if r == nil { + return false + } return r.Plan != nil } @@ -171,7 +278,7 @@ func (r *TerraRun) ProcessPlan(task *TaskResult) error { return nil } var tfPlan tfjson.Plan - if err := tfPlan.UnmarshalJSON(task.Output); err != nil { + if err := tfPlan.UnmarshalJSON(task.Output.Bytes()); err != nil { return fmt.Errorf("unmarshalling terraform show plan output: %w", err) } diff --git a/runner/run_test.go b/runner/run_test.go deleted file mode 100644 index 9a789e9..0000000 --- a/runner/run_test.go +++ /dev/null @@ -1,50 +0,0 @@ -package runner - -import ( - "sync" - "testing" - - "github.com/hashicorp/go-multierror" - "github.com/stretchr/testify/assert" - "github.com/verifa/terraplate/parser" -) - -func TestMultipleRunError(t *testing.T) { - // Setup TerraRun - tf := parser.DefaultTerrafile - tf.Dir = "testData" - r := TerraRun{ - Terrafile: &tf, - } - // Run the TerraRun twice, and one of those runs should result in an error - var ( - wg sync.WaitGroup - err error - ) - { - wg.Add(1) - go func() { - defer wg.Done() - err = multierror.Append(err, r.Run(TerraRunOpts{ - validate: true, - init: true, - plan: true, - jobs: 1, - })) - }() - } - { - wg.Add(1) - go func() { - defer wg.Done() - err = multierror.Append(err, r.Run(TerraRunOpts{ - validate: true, - init: true, - plan: true, - jobs: 1, - })) - }() - } - wg.Wait() - assert.ErrorIs(t, err, ErrRunInProgress) -} diff --git a/runner/runner.go b/runner/runner.go index ab7005b..ea46d3c 100644 --- a/runner/runner.go +++ b/runner/runner.go @@ -2,164 +2,111 @@ package runner import ( "context" - "errors" "fmt" + "os" + "os/signal" "strings" + "sync" + "syscall" "github.com/hashicorp/go-multierror" - "github.com/remeh/sizedwaitgroup" "github.com/verifa/terraplate/parser" ) -type terraCmd string - -func (t terraCmd) Action() string { - switch t { - case terraValidate: - return "validating" - case terraInit: - return "initializing" - case terraPlan: - return "planning" - case terraApply: - return "applying" - case terraShowPlan: - return "summarizing" - } - return "Unknown action" -} - const ( - terraExe = "terraform" - terraValidate terraCmd = "validate" - terraInit terraCmd = "init" - terraPlan terraCmd = "plan" - terraApply terraCmd = "apply" - terraShowPlan terraCmd = "show" - DefaultJobs = 4 + maxRunQueue = 200 ) +func Run(config *parser.TerraConfig, opts ...func(r *TerraRunOpts)) *Runner { + runner := New(config, opts...) + runner.RunAll() + return runner +} + func New(config *parser.TerraConfig, opts ...func(r *TerraRunOpts)) *Runner { - // Initialise TerraRunOpts with defaults - runOpts := TerraRunOpts{ - jobs: DefaultJobs, - } - for _, opt := range opts { - opt(&runOpts) - } + runOpts := NewOpts(opts...) + + // Create the runQueue which is used by the workers to schedule runs + runQueue := make(chan *TerraRun, maxRunQueue) runner := Runner{ - ctx: listenTerminateSignals(), - swg: sizedwaitgroup.New(runOpts.jobs), - opts: runOpts, - config: config, + ctx: listenTerminateSignals(runQueue), + runQueue: runQueue, + config: config, + Opts: runOpts, + } + // Initialize the workers in separate go routines + for workerID := 0; workerID < runOpts.jobs; workerID++ { + go runner.startWorker(workerID) } // Initialize result var ( - rootMods = config.RootModules() - runs = make([]*TerraRun, len(rootMods)) + // Get only root module Terrafiles + tfs = config.RootModules() + modules = make([]*RootModule, len(tfs)) ) - for index, tf := range config.RootModules() { - runs[index] = &TerraRun{ - Terrafile: tf, - } + for index, tf := range tfs { + modules[index] = newRootModule(tf, runOpts) } - runner.Runs = runs + runner.Modules = modules return &runner } type Runner struct { - opts TerraRunOpts - ctx context.Context - swg sizedwaitgroup.SizedWaitGroup + ctx context.Context + // runQueue is a channel for managing the run queue + runQueue chan *TerraRun + wg sync.WaitGroup config *parser.TerraConfig - Runs []*TerraRun -} - -func Run(config *parser.TerraConfig, opts ...func(r *TerraRunOpts)) *Runner { - - runner := New(config, opts...) - runner.RunAll() - return runner + Opts TerraRunOpts + Modules []*RootModule } -func Jobs(jobs int) func(r *TerraRunOpts) { - return func(r *TerraRunOpts) { - r.jobs = jobs - } +func (r *Runner) WorkingDirectory() string { + return r.config.WorkingDirectory } -func RunValidate() func(r *TerraRunOpts) { - return func(r *TerraRunOpts) { - r.validate = true - } -} - -func RunInit() func(r *TerraRunOpts) { - return func(r *TerraRunOpts) { - r.init = true - } +func (r *Runner) RunAll() { + r.Run(r.Modules) } -func RunPlan() func(r *TerraRunOpts) { - return func(r *TerraRunOpts) { - r.plan = true - } +func (r *Runner) Run(modules []*RootModule) { + r.Start(modules) + r.Wait() } -func RunShowPlan() func(r *TerraRunOpts) { - return func(r *TerraRunOpts) { - r.showPlan = true - } +func (r *Runner) Start(modules []*RootModule) { + r.StartWithOpts(modules, r.Opts) } -func RunApply() func(r *TerraRunOpts) { - return func(r *TerraRunOpts) { - r.apply = true +func (r *Runner) StartWithOpts(modules []*RootModule, opts TerraRunOpts) { + for _, mod := range modules { + // Check that the run is not in progress + if runErr := mod.ScheduleRunWithOpts(r.runQueue, opts); runErr != nil { + continue + } + // If run was scheduled, add to waitgroup + r.wg.Add(1) } } -func ExtraArgs(extraArgs []string) func(r *TerraRunOpts) { - return func(r *TerraRunOpts) { - r.extraArgs = extraArgs - } +func (r *Runner) Wait() { + r.wg.Wait() } -// TerraRunOpts handles running Terraform over the root modules -type TerraRunOpts struct { - validate bool - init bool - plan bool - showPlan bool - apply bool - - // Max number of concurrent jobs allowed - jobs int - // Terraform command flags - extraArgs []string -} - -func (r *Runner) RunAll() { - for _, run := range r.Runs { - addErr := r.swg.AddWithContext(r.ctx) - // Check if the process has been cancelled. - if errors.Is(addErr, context.Canceled) { - run.Cancelled = true - continue +// Runs returns the list of latest runs for the root modules +func (r *Runner) Runs() []*TerraRun { + var runs = make([]*TerraRun, 0) + for _, mod := range r.Modules { + if mod.Run != nil { + runs = append(runs, mod.Run) } - - // Set local run for goroutine - run := run - go func() { - defer r.swg.Done() - run.Run(r.opts) - }() } - r.swg.Wait() + return runs } // Log returns a string of the runs and tasks to print to the console @@ -169,20 +116,14 @@ func (r *Runner) Log() string { hasRelevantRuns bool ) summary.WriteString(textSeparator) - for _, run := range r.Runs { + for _, run := range r.Runs() { // Skip runs that have nothing relevant to show if !run.HasRelevantTasks() { continue } hasRelevantRuns = true - summary.WriteString(boldColor.Sprintf("Run for %s\n\n", run.Terrafile.RelativeDir())) - - for _, task := range run.Tasks { - if task.HasRelevance() { - summary.WriteString(task.Log()) - } - } + summary.WriteString(run.Log(false)) } // If there were no runs to output, return an empty string to avoid printing // separators and empty space @@ -193,19 +134,19 @@ func (r *Runner) Log() string { return summary.String() } -// PlanSummary returns a string summary to show after a plan -func (r *Runner) PlanSummary() string { +// Summary returns a string summary to show after a plan +func (r *Runner) Summary() string { var summary strings.Builder - summary.WriteString(boldColor.Sprint("\nTerraplate Plan Summary\n\n")) - for _, run := range r.Runs { - summary.WriteString(fmt.Sprintf("%s: %s\n", run.Terrafile.RelativeDir(), run.PlanSummary())) + summary.WriteString(boldColor.Sprint("\nTerraplate Summary\n\n")) + for _, run := range r.Runs() { + summary.WriteString(fmt.Sprintf("%s: %s\n", run.Terrafile.Dir, run.Summary())) } return summary.String() } func (r *Runner) RunsWithDrift() []*TerraRun { var runs []*TerraRun - for _, run := range r.Runs { + for _, run := range r.Runs() { if run.Drift().HasDrift() { runs = append(runs, run) } @@ -215,7 +156,7 @@ func (r *Runner) RunsWithDrift() []*TerraRun { func (r *Runner) RunsWithError() []*TerraRun { var runs []*TerraRun - for _, run := range r.Runs { + for _, run := range r.Runs() { if run.HasError() { runs = append(runs, run) } @@ -225,7 +166,7 @@ func (r *Runner) RunsWithError() []*TerraRun { // HasDrift returns true if any drift was detected in any of the runs func (r *Runner) HasDrift() bool { - for _, run := range r.Runs { + for _, run := range r.Runs() { if drift := run.Drift(); drift != nil { // If at least one of the runs has drifted, then our result has drift if drift.HasDrift() { @@ -237,7 +178,7 @@ func (r *Runner) HasDrift() bool { } func (r *Runner) HasError() bool { - for _, run := range r.Runs { + for _, run := range r.Runs() { if run.HasError() { return true } @@ -248,10 +189,32 @@ func (r *Runner) HasError() bool { // Errors returns a multierror with any errors found in any tasks within the runs func (r *Runner) Errors() error { var err error - for _, run := range r.Runs { + for _, run := range r.Runs() { if run.HasError() { err = multierror.Append(err, run.Errors()...) } } return err } + +// listenTerminateSignals returns a context that will be cancelled if an interrupt +// or termination signal is received. The context can be used to prevent further +// runs from being scheduled +func listenTerminateSignals(runQueue chan *TerraRun) context.Context { + ctx, cancel := context.WithCancel(context.Background()) + signals := make(chan os.Signal, 1) + signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM) + go func() { + for { + <-signals + fmt.Println("") + fmt.Println("Terraplate: Interrupt received.") + fmt.Println("Terraplate: Sending interrupt to all Terraform processes and cancelling any queued runs.") + fmt.Println("") + // Cancel the context and stop any more runs from being executed + cancel() + close(runQueue) + } + }() + return ctx +} diff --git a/runner/task.go b/runner/task.go index 3103cf0..7131971 100644 --- a/runner/task.go +++ b/runner/task.go @@ -6,13 +6,16 @@ import ( "fmt" "os/exec" "strings" + + "golang.org/x/text/cases" + "golang.org/x/text/language" ) type TaskResult struct { ExecCmd *exec.Cmd TerraCmd terraCmd - Output []byte + Output bytes.Buffer Error error Skipped bool } @@ -21,11 +24,11 @@ func (t *TaskResult) HasError() bool { return t.Error != nil } -// HasRelevance is an attempt at better UX. +// IsRelevant is an attempt at better UX. // We don't simply want to output everything. Things like successful inits and // terraform show output are not interesting for the user, so skip them by // default and therefore keep the output less -func (t *TaskResult) HasRelevance() bool { +func (t *TaskResult) IsRelevant() bool { // Errors are always relevant if t.HasError() { return true @@ -48,13 +51,28 @@ func (t *TaskResult) HasRelevance() bool { } func (t *TaskResult) Log() string { - var summary strings.Builder + var ( + summary strings.Builder + tmp bytes.Buffer + caser = cases.Title(language.English) + ) + + // Make a copy of the output bytes as the scanner below will Read the io + // and therefore "empty" it, and we don't want to empty the output bytes + if _, err := tmp.Write(t.Output.Bytes()); err != nil { + return "Error: writing task output to temporary buffer" + } - summary.WriteString(fmt.Sprintf("%s output: %s\n\n", strings.Title(string(t.TerraCmd)), t.ExecCmd.String())) + switch t.TerraCmd { + case terraBuild: + summary.WriteString("Build output:\n\n") + default: + summary.WriteString(fmt.Sprintf("%s output: %s\n\n", caser.String(string(t.TerraCmd)), t.ExecCmd.String())) + } if t.HasError() { summary.WriteString(fmt.Sprintf("Error: %s\n\n", t.Error.Error())) } - scanner := bufio.NewScanner(bytes.NewBuffer(t.Output)) + scanner := bufio.NewScanner(&tmp) for scanner.Scan() { summary.WriteString(fmt.Sprintf(" %s\n", scanner.Text())) } diff --git a/runner/test.sh b/runner/test.sh new file mode 100644 index 0000000..a6fc18f --- /dev/null +++ b/runner/test.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +for i in {1..3}; do + echo "step " $i + sleep 1s + echo >&2 "stderr!!" +done diff --git a/runner/worker.go b/runner/worker.go new file mode 100644 index 0000000..5dbbf01 --- /dev/null +++ b/runner/worker.go @@ -0,0 +1,18 @@ +package runner + +import ( + "context" + "errors" +) + +func (r *Runner) startWorker(workerID int) { + for run := range r.runQueue { + // Check if context has been cancelled + if errors.Is(r.ctx.Err(), context.Canceled) { + run.Cancelled = true + } else { + run.Run() + } + r.wg.Done() + } +} diff --git a/tui/entryui/commands.go b/tui/entryui/commands.go new file mode 100644 index 0000000..19294a4 --- /dev/null +++ b/tui/entryui/commands.go @@ -0,0 +1,47 @@ +package entryui + +import ( + "time" + + tea "github.com/charmbracelet/bubbletea" + "github.com/verifa/terraplate/runner" +) + +type BackMsg struct{} + +func backToListMsg() tea.Cmd { + return func() tea.Msg { + return BackMsg{} + } +} + +type runFinished struct{} + +type runInProgress struct{} + +func RunInProgressCmd(module *runner.RootModule) tea.Cmd { + + var cmds = make([]tea.Cmd, 0) + if module.IsRunning() { + cmds = append(cmds, tickRunInProgress(module)) + } + + cmds = append(cmds, func() tea.Msg { + module.Wait() + return runFinished{} + }) + + return tea.Batch(cmds...) +} + +func tickRunInProgress(module *runner.RootModule) tea.Cmd { + return func() tea.Msg { + time.Sleep(500 * time.Millisecond) + return runInProgress{} + } +} + +func runModuleCmd(r *runner.Runner, module *runner.RootModule) tea.Cmd { + r.Start([]*runner.RootModule{module}) + return RunInProgressCmd(module) +} diff --git a/tui/entryui/keys.go b/tui/entryui/keys.go new file mode 100644 index 0000000..76ba44d --- /dev/null +++ b/tui/entryui/keys.go @@ -0,0 +1,43 @@ +package entryui + +import "github.com/charmbracelet/bubbles/key" + +var keys = keymap{ + prevSection: key.NewBinding( + key.WithKeys("left", "h"), + key.WithHelp("/h", "previous section"), + ), + nextSection: key.NewBinding( + key.WithKeys("right", "l"), + key.WithHelp("/l", "next section"), + ), + back: key.NewBinding( + key.WithKeys("esc"), + key.WithHelp("esc", "back to list"), + ), + run: key.NewBinding( + key.WithKeys(" "), + key.WithHelp("␣", "run selected"), + ), +} + +type keymap struct { + back key.Binding + nextSection key.Binding + prevSection key.Binding + run key.Binding +} + +func (k keymap) ShortHelp() []key.Binding { + return []key.Binding{ + k.back, k.nextSection, k.prevSection, k.run, + } +} + +func (k keymap) FullHelp() [][]key.Binding { + return [][]key.Binding{ + { + k.back, k.nextSection, k.prevSection, k.run, + }, + } +} diff --git a/tui/entryui/model.go b/tui/entryui/model.go new file mode 100644 index 0000000..9300861 --- /dev/null +++ b/tui/entryui/model.go @@ -0,0 +1,197 @@ +package entryui + +import ( + "fmt" + "strings" + + "github.com/charmbracelet/bubbles/help" + "github.com/charmbracelet/bubbles/key" + "github.com/charmbracelet/bubbles/viewport" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" + "github.com/verifa/terraplate/runner" + "golang.org/x/text/cases" + "golang.org/x/text/language" +) + +var _ tea.Model = (*Model)(nil) + +func New(runner *runner.Runner, module *runner.RootModule, windowSize tea.WindowSizeMsg) tea.Model { + + m := Model{ + runner: runner, + activeTask: 0, + help: help.New(), + module: module, + windowSize: windowSize, + } + + m.viewport = viewport.New(0, 0) + m.syncViewportSize() + m.viewport.MouseWheelEnabled = true + m.viewport.SetContent(m.viewportContent()) + + return m +} + +type Model struct { + runner *runner.Runner + activeTask int + viewport viewport.Model + help help.Model + module *runner.RootModule + windowSize tea.WindowSizeMsg +} + +func (m Model) Init() tea.Cmd { + return nil +} + +func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + var cmd tea.Cmd + switch msg := msg.(type) { + case tea.WindowSizeMsg: + m.windowSize = msg + m.syncViewportSize() + case runInProgress: + if m.module.IsRunning() { + cmd = tickRunInProgress(m.module) + m.viewport.SetContent(m.viewportContent()) + m.viewport.GotoBottom() + m.syncViewportSize() + } + case runFinished: + m.viewport.SetContent(m.viewportContent()) + m.viewport.GotoTop() + m.syncViewportSize() + case tea.KeyMsg: + switch { + case msg.String() == "q": + cmd = backToListMsg() + case key.Matches(msg, keys.back): + cmd = backToListMsg() + case key.Matches(msg, keys.nextSection): + numTasks := m.numTasks() + if numTasks > 0 { + // We will use the index of this, so it has to be the length minus 1 + numTasks-- + } + m.activeTask = min(m.activeTask+1, numTasks) + m.viewport.SetContent(m.viewportContent()) + m.viewport.GotoTop() + case key.Matches(msg, keys.prevSection): + m.activeTask = max(m.activeTask-1, 0) + m.viewport.SetContent(m.viewportContent()) + m.viewport.GotoTop() + case key.Matches(msg, keys.run): + cmd = runModuleCmd(m.runner, m.module) + m.viewport.SetContent(m.viewportContent()) + m.viewport.GotoTop() + default: + m.viewport, cmd = m.viewport.Update(msg) + } + + case tea.MouseMsg: + m.viewport, cmd = m.viewport.Update(msg) + } + return m, cmd +} + +func (m Model) View() string { + return lipgloss.JoinVertical( + lipgloss.Left, + m.renderHeader(), + m.viewport.View(), + m.renderFooter(), + ) +} + +func (m Model) viewportContent() string { + style := lipgloss.NewStyle().Width(m.viewport.Width) + run := m.module.Run + switch { + case run == nil: + return style.Render("Not run.") + case run.IsRunning(): + return style.Render(run.Log(true)) + case m.numTasks() == 0: + return style.Render("No tasks to show") + } + return style.Render(run.Tasks[m.activeTask].Output.String()) +} + +func (m Model) renderHeader() string { + var ( + run = m.module.Run + doc strings.Builder + tabs []string + caser = cases.Title(language.English) + ) + // Don't render the tabs unless the run is finished + if !m.module.IsRunning() { + tabs = make([]string, m.numTasks()) + for i := 0; i < m.numTasks(); i++ { + task := run.Tasks[i] + if i == m.activeTask { + tabs[i] = activeTab.Render(caser.String(string(task.TerraCmd))) + continue + } + tabs[i] = tab.Render(caser.String(string(task.TerraCmd))) + + } + } + row := lipgloss.JoinHorizontal(lipgloss.Top, tabs...) + gap := tabGap.Render(strings.Repeat(" ", max(0, m.windowSize.Width-lipgloss.Width(row)))) + doc.WriteString(lipgloss.JoinHorizontal(lipgloss.Bottom, row, gap)) + return doc.String() + "\n" +} + +func (m Model) renderFooter() string { + info := infoStyle.Render(fmt.Sprintf("%3.f%%", m.viewport.ScrollPercent()*100)) + line := strings.Repeat("─", max(0, m.viewport.Width-lipgloss.Width(info))) + status := lipgloss.JoinHorizontal( + lipgloss.Center, + line, + info, + ) + + return lipgloss.JoinVertical( + lipgloss.Left, + status, + m.renderStatus(), + m.help.View(keys), + ) +} + +func (m Model) renderStatus() string { + return "Module: " + m.module.Terrafile.Dir +} + +func (m *Model) syncViewportSize() { + headerHeight := lipgloss.Height(m.renderHeader()) + footerHeight := lipgloss.Height(m.renderFooter()) + + m.viewport.Width = m.windowSize.Width + m.viewport.Height = max(0, m.windowSize.Height-headerHeight-footerHeight) +} + +func (m Model) numTasks() int { + if m.module.Run == nil { + return 0 + } + return len(m.module.Run.Tasks) +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +func min(a, b int) int { + if a > b { + return b + } + return a +} diff --git a/tui/entryui/styles.go b/tui/entryui/styles.go new file mode 100644 index 0000000..e83a13e --- /dev/null +++ b/tui/entryui/styles.go @@ -0,0 +1,52 @@ +package entryui + +import "github.com/charmbracelet/lipgloss" + +var ( + highlight = lipgloss.AdaptiveColor{Light: "#874BFD", Dark: "#7D56F4"} + + tabBorder = lipgloss.Border{ + Top: "─", + Bottom: "─", + Left: "│", + Right: "│", + TopLeft: "╭", + TopRight: "╮", + BottomLeft: "┴", + BottomRight: "┴", + } + activeTabBorder = lipgloss.Border{ + Top: "─", + Bottom: " ", + Left: "│", + Right: "│", + TopLeft: "╭", + TopRight: "╮", + BottomLeft: "┘", + BottomRight: "└", + } + + tab = lipgloss.NewStyle(). + Border(tabBorder, true). + BorderForeground(highlight). + Padding(0, 1) + + activeTab = tab.Copy().Border(activeTabBorder, true) + + tabGap = tab.Copy(). + BorderTop(false). + BorderLeft(false). + BorderRight(false) + + viewTitleStyle = func() lipgloss.Style { + b := lipgloss.RoundedBorder() + b.Right = "├" + return lipgloss.NewStyle().BorderStyle(b).Padding(0, 1) + }() + + infoStyle = func() lipgloss.Style { + b := lipgloss.RoundedBorder() + b.Left = "┤" + return viewTitleStyle.Copy().BorderStyle(b) + }() +) diff --git a/tui/model.go b/tui/model.go new file mode 100644 index 0000000..dc8675b --- /dev/null +++ b/tui/model.go @@ -0,0 +1,73 @@ +package tui + +import ( + tea "github.com/charmbracelet/bubbletea" + "github.com/verifa/terraplate/runner" + "github.com/verifa/terraplate/tui/entryui" + "github.com/verifa/terraplate/tui/modulesui" +) + +var _ tea.Model = (*MainModel)(nil) + +type MainModel struct { + runner *runner.Runner + + modules tea.Model + entry tea.Model + + state state + windowSize tea.WindowSizeMsg +} + +func New(runner *runner.Runner) MainModel { + return MainModel{ + runner: runner, + state: modulesView, + modules: modulesui.New(runner), + } +} + +func (m MainModel) Init() tea.Cmd { + return nil +} + +func (m MainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + var cmd tea.Cmd + var cmds []tea.Cmd + switch msg := msg.(type) { + case tea.WindowSizeMsg: + m.windowSize = msg // pass this along to the entry view so it uses the full window size when it's initialized + case tea.KeyMsg: + switch msg.String() { + case "ctrl+c": + return m, tea.Quit + } + case entryui.BackMsg: + m.state = modulesView + case modulesui.SelectMsg: + m.state = entryView + m.entry = entryui.New(m.runner, msg.Module, m.windowSize) + if msg.Module.IsRunning() { + cmds = append(cmds, entryui.RunInProgressCmd(msg.Module)) + } + } + + switch m.state { + case modulesView: + m.modules, cmd = m.modules.Update(msg) + case entryView: + m.entry, cmd = m.entry.Update(msg) + } + cmds = append(cmds, cmd) + return m, tea.Batch(cmds...) +} + +func (m MainModel) View() string { + switch m.state { + case modulesView: + return m.modules.View() + case entryView: + return m.entry.View() + } + return "Error: no view selected" +} diff --git a/tui/modulesui/columns.go b/tui/modulesui/columns.go new file mode 100644 index 0000000..e47c229 --- /dev/null +++ b/tui/modulesui/columns.go @@ -0,0 +1,45 @@ +package modulesui + +import "github.com/charmbracelet/lipgloss" + +type tableColumnsMap struct { + status column + rootModule column + summary column +} + +func (c tableColumnsMap) columns() []column { + return []column{ + c.status, c.rootModule, c.summary, + } +} + +var ( + tableColumns = tableColumnsMap{ + status: column{ + header: "Status", + width: 10, + grow: false, + }, + rootModule: column{ + header: "Root Module", + width: 60, + grow: true, + }, + summary: column{ + header: "Summary", + width: 60, + grow: true, + }, + } +) + +type column struct { + header string + width int + grow bool +} + +func (c column) render(style lipgloss.Style, text string) string { + return style.Width(c.width).Render(text) +} diff --git a/tui/modulesui/commands.go b/tui/modulesui/commands.go new file mode 100644 index 0000000..75f58ca --- /dev/null +++ b/tui/modulesui/commands.go @@ -0,0 +1,108 @@ +package modulesui + +import ( + "errors" + "fmt" + "time" + + tea "github.com/charmbracelet/bubbletea" + "github.com/verifa/terraplate/runner" +) + +type SelectMsg struct { + Module *runner.RootModule +} + +func (m Model) selectModuleCmd() tea.Cmd { + return func() tea.Msg { + return SelectMsg{Module: m.activeModule()} + } +} + +type updateModuleMsg struct { + Module *runner.RootModule +} + +func (m Model) runActiveModuleCmd() tea.Cmd { + modules := []*runner.RootModule{ + m.activeModule(), + } + return m.runModulesWithOptsCmd(modules, m.runner.Opts) +} + +func (m Model) runModulesWithOptsCmd(modules []*runner.RootModule, opts runner.TerraRunOpts) tea.Cmd { + m.runner.StartWithOpts(modules, opts) + + var cmds = make([]tea.Cmd, len(modules)) + for _, mod := range modules { + mod := mod + cmds = append(cmds, func() tea.Msg { + mod.Wait() + return updateModuleMsg{Module: mod} + }) + } + + return tea.Batch(cmds...) +} + +func (m Model) inputCmd() (tea.Cmd, error) { + opts, runAll, cmdErr := m.parseInputCmd(m.input.Value()) + if cmdErr != nil { + return nil, cmdErr + } + var modules []*runner.RootModule + if runAll { + modules = m.visibleModules() + } else { + modules = append(modules, m.activeModule()) + } + return m.runModulesWithOptsCmd(modules, opts), nil +} + +func (m Model) parseInputCmd(cmd string) (runner.TerraRunOpts, bool, error) { + var ( + opts []func(*runner.TerraRunOpts) + runAll bool + ) + if len(cmd) == 0 { + return runner.TerraRunOpts{}, runAll, errors.New("command is empty") + } + // Inherit from existing run, e.g. number of jobs and output + opts = append(opts, runner.FromOpts(m.runner.Opts)) + for _, c := range cmd { + switch c { + case 'b': + opts = append(opts, runner.RunBuild()) + case 'i': + opts = append(opts, runner.RunBuild(), runner.RunInit()) + case 'u': + opts = append(opts, runner.RunBuild(), runner.RunInitUpgrade()) + case 'p': + // If we are planning, also run the terraform show command to get + // the JSON output of the plan + opts = append(opts, runner.RunBuild(), runner.RunPlan(), runner.RunShowPlan()) + case 'a': + opts = append(opts, runner.RunApply()) + case 'A': + runAll = true + default: + return runner.TerraRunOpts{}, runAll, fmt.Errorf("unknown command: %c", c) + } + } + runOpts := runner.NewOpts(opts...) + return runOpts, runAll, nil +} + +type statusTimeoutMsg struct { + // uuid uuid.UUID + err error +} + +func statusTimeoutCmd(err error) tea.Cmd { + // uuid := uuid.New() + timer := time.NewTimer(2 * time.Second) + return func() tea.Msg { + <-timer.C + return statusTimeoutMsg{err: err} + } +} diff --git a/tui/modulesui/item.go b/tui/modulesui/item.go new file mode 100644 index 0000000..3f2d6c1 --- /dev/null +++ b/tui/modulesui/item.go @@ -0,0 +1,62 @@ +package modulesui + +import ( + "fmt" + "strings" + + "github.com/verifa/terraplate/runner" +) + +type listItem struct { + module *runner.RootModule +} + +func (i listItem) Title() string { + return i.module.Terrafile.Dir +} + +func (i listItem) Description() string { + return i.renderSummary() +} + +func (i listItem) FilterValue() string { + return i.module.Terrafile.Dir +} + +func (i listItem) renderSummary() string { + var ( + summary strings.Builder + module = i.module + run = module.Run + drift *runner.Drift + ) + if run != nil { + drift = run.Drift() + } + switch { + case module.Skip(): + summary.WriteString("Skip.") + case run == nil: + summary.WriteString("Not run.") + case run.IsRunning(): + summary.WriteString("Running...") + case run.HasError(): + summary.WriteString("Error occurred.") + case run.IsApplied(): + summary.WriteString("Applied.") + case !run.HasPlan(): + summary.WriteString("No plan available.") + case !drift.HasDrift(): + summary.WriteString("No changes.") + default: + // There is a plan, and there is drift, so show a summary + fmt.Fprintf( + &summary, + "+ %d ~ %d - %d", + len(drift.AddResources), + len(drift.ChangeResources), + len(drift.DestroyResources), + ) + } + return summary.String() +} diff --git a/tui/modulesui/keys.go b/tui/modulesui/keys.go new file mode 100644 index 0000000..a5b8ade --- /dev/null +++ b/tui/modulesui/keys.go @@ -0,0 +1,122 @@ +package modulesui + +import "github.com/charmbracelet/bubbles/key" + +var keys = keymap{ + enter: key.NewBinding( + key.WithKeys("enter"), + key.WithHelp("enter", "select"), + ), + colon: key.NewBinding( + key.WithKeys(":"), + key.WithHelp(":", "command mode"), + ), + esc: key.NewBinding( + key.WithKeys("esc"), + key.WithHelp("esc", "back to list"), + ), + run: key.NewBinding( + key.WithKeys(" "), + key.WithHelp("␣", "run selected"), + ), + // runAll: key.NewBinding( + // key.WithKeys("ctrl+r"), + // key.WithHelp("ctrl+r", "run all visible"), + // ), + tab: key.NewBinding( + key.WithKeys("tab"), + key.WithHelp("tab", "select section"), + ), + filterDrift: key.NewBinding( + key.WithKeys("D"), + key.WithHelp("D", "filter drift"), + ), + filterError: key.NewBinding( + key.WithKeys("E"), + key.WithHelp("E", "filter error"), + ), + toggleSummary: key.NewBinding( + key.WithKeys("S"), + key.WithHelp("S", "toggle summary"), + ), + togglePagination: key.NewBinding( + key.WithKeys("P"), + key.WithHelp("P", "toggle pagination"), + ), + toggleHelpMenu: key.NewBinding( + key.WithKeys("H"), + key.WithHelp("H", "toggle help"), + ), +} + +type keymap struct { + enter key.Binding + esc key.Binding + run key.Binding + // runAll key.Binding + tab key.Binding + colon key.Binding + filterDrift key.Binding + filterError key.Binding + toggleSummary key.Binding + togglePagination key.Binding + toggleHelpMenu key.Binding +} + +func (k keymap) ShortHelp() []key.Binding { + return []key.Binding{ + k.enter, k.colon, k.run, k.tab, k.filterDrift, + } +} + +func (k keymap) FullHelpKeys() []key.Binding { + return []key.Binding{ + k.enter, k.colon, k.run, k.tab, k.filterDrift, k.filterError, k.toggleSummary, + k.togglePagination, k.toggleHelpMenu, + } +} + +var inputKeys = inputKeyMap{ + build: key.NewBinding( + key.WithKeys("b"), + key.WithHelp("b", "build"), + ), + init: key.NewBinding( + key.WithKeys("i"), + key.WithHelp("i", "init"), + ), + upgrade: key.NewBinding( + key.WithKeys("u"), + key.WithHelp("u", "upgrade"), + ), + plan: key.NewBinding( + key.WithKeys("p"), + key.WithHelp("p", "plan"), + ), + apply: key.NewBinding( + key.WithKeys("a"), + key.WithHelp("a", "apply"), + ), + all: key.NewBinding( + key.WithKeys("A"), + key.WithHelp("A", "all modules"), + ), +} + +type inputKeyMap struct { + build key.Binding + init key.Binding + upgrade key.Binding + plan key.Binding + apply key.Binding + all key.Binding +} + +func (k inputKeyMap) ShortHelp() []key.Binding { + return []key.Binding{ + k.build, k.init, k.upgrade, k.plan, k.apply, k.all, + } +} +func (k inputKeyMap) FullHelp() [][]key.Binding { + return nil +} diff --git a/tui/modulesui/model.go b/tui/modulesui/model.go new file mode 100644 index 0000000..777bcae --- /dev/null +++ b/tui/modulesui/model.go @@ -0,0 +1,487 @@ +package modulesui + +import ( + "fmt" + "strings" + "text/tabwriter" + + "github.com/charmbracelet/bubbles/help" + "github.com/charmbracelet/bubbles/key" + "github.com/charmbracelet/bubbles/list" + "github.com/charmbracelet/bubbles/textinput" + "github.com/charmbracelet/bubbles/viewport" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" + tfjson "github.com/hashicorp/terraform-json" + "github.com/verifa/terraplate/runner" +) + +var _ tea.Model = (*Model)(nil) + +func New(runner *runner.Runner) tea.Model { + var m Model + m.state = listView + m.runner = runner + + // Setup list + l := list.New(m.filterItems(), list.NewDefaultDelegate(), 0, 0) + l.Title = "Root Modules" + l.SetShowTitle(true) + l.SetShowStatusBar(false) + l.SetShowHelp(false) + l.AdditionalShortHelpKeys = keys.ShortHelp + l.AdditionalFullHelpKeys = keys.FullHelpKeys + m.list = l + + // Setup viewport + m.viewport = viewport.New(0, 0) + m.viewport.MouseWheelEnabled = true + m.viewport.SetContent(m.viewportContent()) + + m.showSummary = true + m.showHelp = true + m.listBorder = lipgloss.NewStyle().Border(lipgloss.NormalBorder()) + m.summaryBorder = lipgloss.NewStyle().Border(lipgloss.NormalBorder()) + m.setActiveViewStyle() + + // Setup textinput for commands + input := textinput.New() + input.Prompt = ":" + m.input = input + + return m +} + +type Model struct { + state state + runner *runner.Runner + list list.Model + viewport viewport.Model + input textinput.Model + statusErr error + + filterDrift bool + filterError bool + showSummary bool + showHelp bool + + listBorder lipgloss.Style + summaryBorder lipgloss.Style + + windowSize tea.WindowSizeMsg +} + +func (m Model) Init() tea.Cmd { + return nil +} +func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + var cmd tea.Cmd + switch msg := msg.(type) { + case tea.WindowSizeMsg: + m.windowSize = msg + m.syncViewSizes() + case updateModuleMsg: + // If the selected module is updated, update the viewport + if msg.Module == m.activeModule() { + m.viewport.SetContent(m.viewportContent()) + m.viewport.GotoTop() + } + case statusTimeoutMsg: + if msg.err == m.statusErr { + m.statusErr = nil + } + + case list.FilterMatchesMsg: + cmd = m.handleListUpdate(msg) + case tea.KeyMsg: + if m.list.FilterState() == list.Filtering { + // If we are filtering the list, send all keys to the list + cmd = m.handleListUpdate(msg) + } else if m.input.Focused() { + switch { + case key.Matches(msg, keys.enter): + var cmdErr error + cmd, cmdErr = m.inputCmd() + if cmdErr != nil { + m.statusErr = cmdErr + cmd = statusTimeoutCmd(cmdErr) + } + m.input.Blur() + m.input.Reset() + m.syncViewSizes() + m.setActiveViewStyle() + case key.Matches(msg, keys.esc): + m.input.Blur() + m.input.Reset() + m.syncViewSizes() + m.setActiveViewStyle() + default: + m.input, cmd = m.input.Update(msg) + } + } else { + switch { + case key.Matches(msg, keys.enter): + cmd = m.selectModuleCmd() + case key.Matches(msg, keys.colon): + cmd = m.input.Focus() + m.syncViewSizes() + m.setActiveViewStyle() + case key.Matches(msg, keys.esc): + switch m.state { + case listView: + cmd = m.handleListUpdate(msg) + case summaryView: + m.state = listView + } + m.setActiveViewStyle() + case key.Matches(msg, keys.run): + cmd = m.runActiveModuleCmd() + m.viewport.SetContent(m.viewportContent()) + m.viewport.GotoTop() + case key.Matches(msg, keys.tab): + if m.state == listView { + m.state = summaryView + } else { + m.state = listView + } + m.setActiveViewStyle() + return m, nil + case key.Matches(msg, keys.filterDrift): + m.filterDrift = !m.filterDrift + m.list.SetItems(m.filterItems()) + // Reset the list filter. Slightly strange behaviour if we don't + // do this. + m.list.ResetFilter() + m.viewport.SetContent(m.viewportContent()) + m.viewport.GotoTop() + return m, nil + case key.Matches(msg, keys.filterError): + m.filterError = !m.filterError + m.list.SetItems(m.filterItems()) + // Reset the list filter. Slightly strange behaviour if we don't + // do this. + m.list.ResetFilter() + m.viewport.SetContent(m.viewportContent()) + m.viewport.GotoTop() + return m, nil + case key.Matches(msg, keys.togglePagination): + m.list.SetShowPagination(!m.list.ShowPagination()) + case key.Matches(msg, keys.toggleSummary): + m.showSummary = !m.showSummary + m.state = listView + m.setActiveViewStyle() + m.syncViewSizes() + return m, nil + case key.Matches(msg, keys.toggleHelpMenu): + m.showHelp = !m.showHelp + m.syncViewSizes() + return m, nil + + default: + if key.Matches(msg, m.list.KeyMap.CloseFullHelp, m.list.KeyMap.ShowFullHelp) { + cmd = m.handleListUpdate(msg) + m.syncViewSizes() + break + } + switch m.state { + case listView: + cmd = m.handleListUpdate(msg) + case summaryView: + m.viewport, cmd = m.viewport.Update(msg) + } + } + } + } + return m, cmd +} + +func (m Model) View() string { + + views := []string{ + m.listBorder.Render(m.list.View()), + } + if m.showSummary { + views = append(views, m.summaryBorder.Render(m.viewport.View())) + } + + stacks := []string{ + lipgloss.JoinHorizontal( + lipgloss.Top, + views..., + ), + } + if m.input.Focused() { + stacks = append(stacks, m.input.View()) + } else { + stacks = append(stacks, m.renderStatus()) + } + stacks = append(stacks, m.renderFooter()) + + return lipgloss.JoinVertical( + lipgloss.Left, + stacks..., + ) +} + +func (m *Model) handleListUpdate(msg tea.Msg) tea.Cmd { + var cmd tea.Cmd + // Call the list delegate, and check if the selected item has changed. + // If the selected item has changed, update the viewport to show the + // correct summary + prevItem := m.list.SelectedItem() + prevState := m.list.FilterState() + m.list, cmd = m.list.Update(msg) + newItem := m.list.SelectedItem() + newState := m.list.FilterState() + if prevItem != newItem || prevState != newState { + m.viewport.SetContent(m.viewportContent()) + m.viewport.GotoTop() + } + return cmd +} + +func (m Model) filterItems() []list.Item { + var items = make([]list.Item, 0) + for _, mod := range m.runner.Modules { + var ( + run = mod.Run + drift = run.Drift() + ) + if m.filterDrift && !drift.HasDrift() { + continue + } + if m.filterError && !run.HasError() { + continue + } + items = append(items, listItem{ + module: mod, + }) + } + return items +} + +func (m Model) activeModule() *runner.RootModule { + item := m.list.SelectedItem() + if item == nil { + return nil + } + return item.(listItem).module +} + +func (m Model) visibleModules() []*runner.RootModule { + items := m.list.VisibleItems() + modules := make([]*runner.RootModule, len(items)) + for index, item := range items { + modules[index] = item.(listItem).module + } + return modules +} + +func (m *Model) setActiveViewStyle() { + if m.input.Focused() { + m.summaryBorder.BorderForeground(dimmedColor) + m.listBorder.BorderForeground(dimmedColor) + return + } + if m.state == listView { + m.summaryBorder.BorderForeground(dimmedColor) + m.listBorder.UnsetBorderForeground() + } else { + m.listBorder.BorderForeground(dimmedColor) + m.summaryBorder.UnsetBorderForeground() + } + +} + +func (m *Model) syncViewSizes() { + var ( + footerHeight = lipgloss.Height(m.renderFooter()) + statusHeight int + listViewWidth, listViewHeight int + summaryViewWidth, summaryViewHeight int + ) + if m.input.Focused() { + statusHeight = lipgloss.Height(m.input.View()) + } else { + statusHeight = 1 + } + listViewHeight = m.windowSize.Height - footerHeight - statusHeight + summaryViewHeight = listViewHeight + if m.showSummary { + summaryViewWidth = m.windowSize.Width / 2 + listViewWidth = m.windowSize.Width - summaryViewWidth + } else { + listViewWidth = m.windowSize.Width + summaryViewWidth = 0 + } + + m.listBorder.Width(listViewWidth - m.listBorder.GetHorizontalBorderSize()) + m.listBorder.Height(listViewHeight - m.listBorder.GetVerticalBorderSize()) + m.summaryBorder.Width(summaryViewWidth - m.summaryBorder.GetHorizontalBorderSize()) + m.summaryBorder.Height(summaryViewHeight - m.summaryBorder.GetVerticalBorderSize()) + + listWidth := listViewWidth - m.listBorder.GetHorizontalFrameSize() + listHeight := listViewHeight - m.listBorder.GetVerticalFrameSize() + m.list.SetSize(listWidth, listHeight) + + summaryWidth := summaryViewWidth - m.summaryBorder.GetHorizontalFrameSize() + summaryHeight := summaryViewHeight - m.summaryBorder.GetVerticalFrameSize() + m.viewport.Width = summaryWidth + m.viewport.Height = summaryHeight + viewportContentStyle.Width(summaryWidth) +} + +func (m Model) viewportContent() string { + var content string + if m.list.FilterState() == list.Filtering { + content = "Filtering..." + } else { + content = m.renderSummaryContent(m.list.SelectedItem()) + } + return viewportContentStyle.Render(content) +} + +func (m Model) renderStatus() string { + if m.statusErr != nil { + return "Error: " + m.statusErr.Error() + } + return "Base: " + m.runner.WorkingDirectory() +} + +func (m Model) renderFooter() string { + if m.input.Focused() { + return help.New().View(inputKeys) + } + if m.showHelp { + return m.list.Help.View(m.list) + } + return "" +} + +func (m Model) renderSummaryContent(item list.Item) string { + if item == nil { + return "No module selected." + } + + var ( + s strings.Builder + hasDrift bool + noOpResources []*tfjson.ResourceChange + addResources []*tfjson.ResourceChange + changeResources []*tfjson.ResourceChange + replaceResources []*tfjson.ResourceChange + destroyResources []*tfjson.ResourceChange + ) + + module := item.(listItem).module + run := module.Run + // Initialize tabwriter for making pretty tab-indented text + tw := tabwriter.NewWriter(&s, 0, 0, 2, ' ', tabwriter.DiscardEmptyColumns) + + if run.IsRunning() { + fmt.Fprintln(&s, inProgress("Running...")) + fmt.Fprintf(&s, "Run is in progress...") + return s.String() + } + if run.IsApplied() { + fmt.Fprintln(&s, appliedLabel("Applied")) + fmt.Fprintf(&s, "Terraform applied.") + return s.String() + } + if !run.HasPlan() { + fmt.Fprintln(&s, noPlanLabel("No plan")) + fmt.Fprintf(&s, "No plan available.") + return s.String() + } + if run.HasError() { + fmt.Fprintln(&s, errorLabel("Error")) + fmt.Fprintf(&s, "Error running Terraform.") + return s.String() + } + for _, r := range run.Plan.ResourceChanges { + actions := r.Change.Actions + switch { + case actions.NoOp(): + noOpResources = append(noOpResources, r) + case actions.Create(): + hasDrift = true + addResources = append(addResources, r) + case actions.Update(): + hasDrift = true + changeResources = append(changeResources, r) + case actions.Replace(): + hasDrift = true + replaceResources = append(replaceResources, r) + case actions.Delete(): + hasDrift = true + destroyResources = append(destroyResources, r) + } + } + + // Header + if hasDrift { + fmt.Fprintln(&s, driftLabel("Drift detected")) + } else { + fmt.Fprintln(&s, noChangesLabel("No changes")) + } + + // Overview + fmt.Fprintf(&s, "%s\n\n", boldStyle.Render("Overview")) + fmt.Fprintf(tw, "Terraform version\t%s\n", run.Plan.TerraformVersion) + fmt.Fprintf(tw, "Total resources\t%d\n", len(run.Plan.ResourceChanges)) + tw.Flush() + + // Plan summary + fmt.Fprintf(&s, "\n%s\n", boldStyle.Render("Plan summary")) + if !hasDrift { + fmt.Fprintln(&s, "No changes.") + } + if len(addResources) != 0 { + fmt.Fprintf(&s, "\nAdd:\n") + for _, r := range addResources { + fmt.Fprintf(&s, "%s %s\n", addSymbol, r.Address) + } + } + if len(changeResources) != 0 { + fmt.Fprintf(&s, "\nChange:\n") + for _, r := range changeResources { + fmt.Fprintf(&s, "%s %s\n", changeSymbol, r.Address) + } + } + if len(replaceResources) != 0 { + fmt.Fprintf(&s, "\nReplace:\n") + for _, r := range replaceResources { + fmt.Fprintf(&s, "%s %s\n", replaceSymbol, r.Address) + } + } + if len(destroyResources) != 0 { + fmt.Fprintf(&s, "\nDestroy:\n") + for _, r := range destroyResources { + fmt.Fprintf(&s, "%s %s\n", destroySymbol, r.Address) + } + } + + // Modules + fmt.Fprintf(&s, "\n%s\n\n", boldStyle.Render("Modules")) + if run.Plan.Config.RootModule.ModuleCalls == nil { + fmt.Fprintln(&s, "No modules calls.") + } + for name, mc := range run.Plan.Config.RootModule.ModuleCalls { + fmt.Fprintf(tw, "%s\t%s\n", name, mc.Source) + tw.Flush() + } + // Providers + fmt.Fprintf(&s, "\n%s\n\n", boldStyle.Render("Providers")) + for name, config := range run.Plan.Config.ProviderConfigs { + var pName string + if config.Alias != "" { + pName = fmt.Sprintf("%s (%s)", name, config.Alias) + } else { + pName = name + } + fmt.Fprintf(tw, "%s\t%s\n", pName, config.VersionConstraint) + tw.Flush() + } + + return s.String() +} diff --git a/tui/modulesui/state.go b/tui/modulesui/state.go new file mode 100644 index 0000000..cd7f47b --- /dev/null +++ b/tui/modulesui/state.go @@ -0,0 +1,8 @@ +package modulesui + +type state int + +const ( + listView state = iota + summaryView +) diff --git a/tui/modulesui/styles.go b/tui/modulesui/styles.go new file mode 100644 index 0000000..ec95d30 --- /dev/null +++ b/tui/modulesui/styles.go @@ -0,0 +1,45 @@ +package modulesui + +import ( + "github.com/charmbracelet/lipgloss" + "github.com/muesli/termenv" +) + +var ( + labelStyle = lipgloss.NewStyle(). + Padding(0, 1).MarginBottom(1) + appliedLabel = labelStyle.Copy(). + Background(lipgloss.Color(termenv.ANSIGreen.String())). + Render + noChangesLabel = labelStyle.Copy(). + Background(lipgloss.Color(termenv.ANSIGreen.String())). + Render + driftLabel = labelStyle.Copy(). + Background(lipgloss.Color(termenv.ANSIYellow.String())). + Render + noPlanLabel = labelStyle.Copy(). + Background(lipgloss.Color(termenv.ANSICyan.String())). + Render + inProgress = labelStyle.Copy(). + Background(lipgloss.Color(termenv.ANSIMagenta.String())). + Render + errorLabel = labelStyle.Copy(). + Background(lipgloss.Color(termenv.ANSIRed.String())). + Render + + boldStyle = lipgloss.NewStyle().Bold(true) + viewportContentStyle = lipgloss.NewStyle().Padding(0, 2) + + addSymbol = lipgloss.NewStyle(). + Foreground(lipgloss.Color(termenv.ANSIGreen.String())). + Render("+") + changeSymbol = lipgloss.NewStyle(). + Foreground(lipgloss.Color(termenv.ANSIYellow.String())). + Render("~") + destroySymbol = lipgloss.NewStyle(). + Foreground(lipgloss.Color(termenv.ANSIRed.String())). + Render("-") + replaceSymbol = destroySymbol + addSymbol + + dimmedColor = lipgloss.AdaptiveColor{Light: "#A49FA5", Dark: "#777777"} +) diff --git a/tui/state.go b/tui/state.go new file mode 100644 index 0000000..c5b1b58 --- /dev/null +++ b/tui/state.go @@ -0,0 +1,8 @@ +package tui + +type state int + +const ( + modulesView state = iota + entryView +)