@@ -5,14 +5,15 @@ import (
55 "regexp"
66
77 "github.com/gruntwork-io/terragrunt/config"
8+ "github.com/gruntwork-io/terragrunt/configstack"
89 "github.com/gruntwork-io/terragrunt/errors"
910 "github.com/gruntwork-io/terragrunt/locks"
1011 "github.com/gruntwork-io/terragrunt/options"
1112 "github.com/gruntwork-io/terragrunt/remote"
1213 "github.com/gruntwork-io/terragrunt/shell"
13- "github.com/gruntwork-io/terragrunt/spin"
1414 "github.com/gruntwork-io/terragrunt/util"
1515 "github.com/urfave/cli"
16+ "io"
1617)
1718
1819const OPT_TERRAGRUNT_CONFIG = "terragrunt-config"
@@ -27,10 +28,24 @@ var ALL_TERRAGRUNT_STRING_OPTS = []string{OPT_TERRAGRUNT_CONFIG, OPT_TERRAGRUNT_
2728
2829const CMD_ACQUIRE_LOCK = "acquire-lock"
2930const CMD_RELEASE_LOCK = "release-lock"
31+
32+ const CMD_APPLY_ALL = "apply-all"
33+ const CMD_DESTROY_ALL = "destroy-all"
34+ const CMD_OUTPUT_ALL = "output-all"
35+
36+ // CMD_SPIN_UP is deprecated.
3037const CMD_SPIN_UP = "spin-up"
38+
39+ // CMD_TEAR_DOWN is deprecated.
3140const CMD_TEAR_DOWN = "tear-down"
3241
33- var MULTI_MODULE_COMMANDS = []string {CMD_SPIN_UP , CMD_TEAR_DOWN }
42+ var MULTI_MODULE_COMMANDS = []string {CMD_APPLY_ALL , CMD_DESTROY_ALL , CMD_OUTPUT_ALL }
43+
44+ // DEPRECATED_COMMANDS is a map of deprecated commands to the commands that replace them.
45+ var DEPRECATED_COMMANDS = map [string ]string {
46+ CMD_SPIN_UP : CMD_APPLY_ALL ,
47+ CMD_TEAR_DOWN : CMD_DESTROY_ALL ,
48+ }
3449
3550// Since Terragrunt is just a thin wrapper for Terraform, and we don't want to repeat every single Terraform command
3651// in its definition, we don't quite fit into the model of any Go CLI library. Fortunately, urfave/cli allows us to
@@ -53,8 +68,9 @@ COMMANDS:
5368 remote push Acquire a lock and run 'terraform remote push'
5469 acquire-lock Acquire a long-term lock for these templates
5570 release-lock Release a long-term lock or a lock that failed to clean up
56- spin-up Spin up a 'stack' by running 'terragrunt apply' in each subfolder
57- tear-down Tear down a 'stack' by running 'terragrunt destroy' in each subfolder
71+ apply-all Apply a 'stack' by running 'terragrunt apply' in each subfolder
72+ output-all Display the outputs of a 'stack' by running 'terragrunt output' in each subfolder
73+ destroy-all Destroy a 'stack' by running 'terragrunt destroy' in each subfolder
5874 * Terragrunt forwards all other commands directly to Terraform
5975
6076GLOBAL OPTIONS:
@@ -78,7 +94,7 @@ var MODULE_REGEX = regexp.MustCompile(`module ".+"`)
7894const TERRAFORM_EXTENSION_GLOB = "*.tf"
7995
8096// Create the Terragrunt CLI App
81- func CreateTerragruntCli (version string ) * cli.App {
97+ func CreateTerragruntCli (version string , writer io. Writer , errwriter io. Writer ) * cli.App {
8298 cli .OsExiter = func (exitCode int ) {
8399 // Do nothing. We just need to override this function, as the default value calls os.Exit, which
84100 // kills the app (or any automated test) dead in its tracks.
@@ -93,6 +109,8 @@ func CreateTerragruntCli(version string) *cli.App {
93109 app .Version = version
94110 app .Action = runApp
95111 app .Usage = "terragrunt <COMMAND>"
112+ app .Writer = writer
113+ app .ErrWriter = errwriter
96114 app .UsageText = fmt .Sprintf (`Terragrunt is a thin wrapper for [Terraform](https://www.terraform.io/) that supports locking
97115 via Amazon's DynamoDB and enforces best practices. Terragrunt forwards almost all commands, arguments, and options
98116 directly to Terraform, using whatever version of Terraform you already have installed. However, before running
@@ -119,11 +137,28 @@ func runApp(cliContext *cli.Context) (finalErr error) {
119137 return err
120138 }
121139
122- if isMultiModuleCommand (cliContext .Args ().First ()) {
123- return runMultiModuleCommand (cliContext .Args ().First (), terragruntOptions )
124- } else {
125- return runTerragrunt (terragruntOptions )
140+ givenCommand := cliContext .Args ().First ()
141+ command := checkDeprecated (givenCommand )
142+ return runCommand (command , terragruntOptions )
143+ }
144+
145+ // checkDeprecated checks if the given command is deprecated. If so: prints a message and returns the new command.
146+ func checkDeprecated (command string ) string {
147+ newCommand , deprecated := DEPRECATED_COMMANDS [command ]
148+ if deprecated {
149+ fmt .Printf ("%v is deprecated; running %v instead.\n " , command , newCommand )
150+ return newCommand
151+ }
152+ return command
153+ }
154+
155+ // runCommand runs one or many terraform commands based on the type of
156+ // terragrunt command
157+ func runCommand (command string , terragruntOptions * options.TerragruntOptions ) (finalEff error ) {
158+ if isMultiModuleCommand (command ) {
159+ return runMultiModuleCommand (command , terragruntOptions )
126160 }
161+ return runTerragrunt (terragruntOptions )
127162}
128163
129164// Run Terragrunt with the given options and CLI args. This will forward all the args directly to Terraform, enforcing
@@ -163,18 +198,20 @@ func runTerragrunt(terragruntOptions *options.TerragruntOptions) error {
163198}
164199
165200// Returns true if the command the user wants to execute is supposed to affect multiple Terraform modules, such as the
166- // spin-up or tear-down command.
201+ // apply-all or destroy-all command.
167202func isMultiModuleCommand (command string ) bool {
168203 return util .ListContainsElement (MULTI_MODULE_COMMANDS , command )
169204}
170205
171- // Execute a command that affects multiple Terraform modules, such as the spin-up or tear-down command.
206+ // Execute a command that affects multiple Terraform modules, such as the apply-all or destroy-all command.
172207func runMultiModuleCommand (command string , terragruntOptions * options.TerragruntOptions ) error {
173208 switch command {
174- case CMD_SPIN_UP :
175- return spinUp (terragruntOptions )
176- case CMD_TEAR_DOWN :
177- return tearDown (terragruntOptions )
209+ case CMD_APPLY_ALL :
210+ return applyAll (terragruntOptions )
211+ case CMD_DESTROY_ALL :
212+ return destroyAll (terragruntOptions )
213+ case CMD_OUTPUT_ALL :
214+ return outputAll (terragruntOptions )
178215 default :
179216 return errors .WithStackTrace (UnrecognizedCommand (command ))
180217 }
@@ -253,19 +290,19 @@ func runTerraformCommandWithLock(lock locks.Lock, terragruntOptions *options.Ter
253290
254291// Spin up an entire "stack" by running 'terragrunt apply' in each subfolder, processing them in the right order based
255292// on terraform_remote_state dependencies.
256- func spinUp (terragruntOptions * options.TerragruntOptions ) error {
257- stack , err := spin .FindStackInSubfolders (terragruntOptions )
293+ func applyAll (terragruntOptions * options.TerragruntOptions ) error {
294+ stack , err := configstack .FindStackInSubfolders (terragruntOptions )
258295 if err != nil {
259296 return err
260297 }
261298
262299 terragruntOptions .Logger .Printf ("%s" , stack .String ())
263- shouldSpinUp , err := shell .PromptUserForYesNo ("Are you sure you want to run 'terragrunt apply' in each folder of the stack described above?" , terragruntOptions )
300+ shouldApplyAll , err := shell .PromptUserForYesNo ("Are you sure you want to run 'terragrunt apply' in each folder of the stack described above?" , terragruntOptions )
264301 if err != nil {
265302 return err
266303 }
267304
268- if shouldSpinUp {
305+ if shouldApplyAll {
269306 return stack .Apply (terragruntOptions )
270307 }
271308
@@ -274,25 +311,37 @@ func spinUp(terragruntOptions *options.TerragruntOptions) error {
274311
275312// Tear down an entire "stack" by running 'terragrunt destroy' in each subfolder, processing them in the right order
276313// based on terraform_remote_state dependencies.
277- func tearDown (terragruntOptions * options.TerragruntOptions ) error {
278- stack , err := spin .FindStackInSubfolders (terragruntOptions )
314+ func destroyAll (terragruntOptions * options.TerragruntOptions ) error {
315+ stack , err := configstack .FindStackInSubfolders (terragruntOptions )
279316 if err != nil {
280317 return err
281318 }
282319
283320 terragruntOptions .Logger .Printf ("%s" , stack .String ())
284- shouldTearDown , err := shell .PromptUserForYesNo ("WARNING: Are you sure you want to run `terragrunt destroy` in each folder of the stack described above? There is no undo!" , terragruntOptions )
321+ shouldDestroyAll , err := shell .PromptUserForYesNo ("WARNING: Are you sure you want to run `terragrunt destroy` in each folder of the stack described above? There is no undo!" , terragruntOptions )
285322 if err != nil {
286323 return err
287324 }
288325
289- if shouldTearDown {
326+ if shouldDestroyAll {
290327 return stack .Destroy (terragruntOptions )
291328 }
292329
293330 return nil
294331}
295332
333+ // outputAll prints the outputs from all configuration in a stack, in the order
334+ // specified in the terraform_remote_state dependencies
335+ func outputAll (terragruntOptions * options.TerragruntOptions ) error {
336+ stack , err := configstack .FindStackInSubfolders (terragruntOptions )
337+ if err != nil {
338+ return err
339+ }
340+
341+ terragruntOptions .Logger .Printf ("%s" , stack .String ())
342+ return stack .Output (terragruntOptions )
343+ }
344+
296345// Acquire a lock. This can be useful for locking down a deploy for a long time, such as during a major deployment.
297346func acquireLock (lock locks.Lock , terragruntOptions * options.TerragruntOptions ) error {
298347 shouldAcquireLock , err := shell .PromptUserForYesNo ("Are you sure you want to acquire a long-term lock?" , terragruntOptions )
0 commit comments