Skip to content

Commit

Permalink
Improve terraform import. Add help for all atmos commands (#94)
Browse files Browse the repository at this point in the history
* Update `terraform import`

* `atmos help`

* `atmos help`

* `atmos help`

* `atmos help`

* `atmos help`

* `atmos help`

* `atmos help`

* `atmos help`

* `atmos help`

* `atmos help`

* `atmos help`

* `atmos help`

* `atmos help`

* `atmos help`
  • Loading branch information
aknysh committed Dec 21, 2021
1 parent ba5a880 commit 89a2ad0
Show file tree
Hide file tree
Showing 17 changed files with 154 additions and 52 deletions.
2 changes: 1 addition & 1 deletion LICENSE
Expand Up @@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright 2020-2021 Cloud Posse, LLC
Copyright 2020-2022 Cloud Posse, LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion cmd/describe.go
Expand Up @@ -7,7 +7,7 @@ import (
// describeCmd describes configuration for stacks and components
var describeCmd = &cobra.Command{
Use: "describe",
Short: "describe",
Short: "Execute 'describe' commands",
Long: `This command shows configuration for CLI, stacks and components`,
FParseErrWhitelist: struct{ UnknownFlags bool }{UnknownFlags: true},
}
Expand Down
6 changes: 3 additions & 3 deletions cmd/describe_component.go
Expand Up @@ -10,8 +10,8 @@ import (
// describeComponentCmd describes configuration for components
var describeComponentCmd = &cobra.Command{
Use: "component",
Short: "describe component",
Long: `This command shows configuration for components`,
Short: "Execute 'describe component' command",
Long: `This command shows configuration for a component in a stack: atmos describe component <component> -s <stack>`,
FParseErrWhitelist: struct{ UnknownFlags bool }{UnknownFlags: true},
Run: func(cmd *cobra.Command, args []string) {
err := e.ExecuteDescribeComponent(cmd, args)
Expand All @@ -24,7 +24,7 @@ var describeComponentCmd = &cobra.Command{

func init() {
describeComponentCmd.DisableFlagParsing = false
describeComponentCmd.PersistentFlags().StringP("stack", "s", "", "")
describeComponentCmd.PersistentFlags().StringP("stack", "s", "", "atmos describe component <component> -s <stack>")

err := describeComponentCmd.MarkPersistentFlagRequired("stack")
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions cmd/describe_config.go
Expand Up @@ -11,8 +11,8 @@ import (
// describeComponentCmd describes configuration for components
var describeConfigCmd = &cobra.Command{
Use: "config",
Short: "describe config",
Long: `This command shows CLI configuration`,
Short: "Execute 'describe config' command",
Long: `This command shows the final (deep-merged) CLI configuration: atmos describe config`,
FParseErrWhitelist: struct{ UnknownFlags bool }{UnknownFlags: true},
Run: func(cmd *cobra.Command, args []string) {
err := e.ExecuteDescribeConfig(cmd, args)
Expand Down
13 changes: 3 additions & 10 deletions cmd/helmfile.go
Expand Up @@ -10,8 +10,8 @@ import (
// terraformCmd represents the base command for all terraform sub-commands
var helmfileCmd = &cobra.Command{
Use: "helmfile",
Short: "helmfile command",
Long: `This command runs helmfile sub-commands`,
Short: "Execute 'helmfile' commands",
Long: `This command runs helmfile commands`,
FParseErrWhitelist: struct{ UnknownFlags bool }{UnknownFlags: true},
Run: func(cmd *cobra.Command, args []string) {
err := e.ExecuteHelmfile(cmd, args)
Expand All @@ -25,13 +25,6 @@ var helmfileCmd = &cobra.Command{
func init() {
// https://github.com/spf13/cobra/issues/739
helmfileCmd.DisableFlagParsing = true
helmfileCmd.PersistentFlags().StringP("stack", "s", "", "")

err := helmfileCmd.MarkPersistentFlagRequired("stack")
if err != nil {
color.Red("%s\n\n", err)
os.Exit(1)
}

helmfileCmd.PersistentFlags().StringP("stack", "s", "", "atmos helmfile <helmfile_command> <component> -s <stack>")
RootCmd.AddCommand(helmfileCmd)
}
2 changes: 1 addition & 1 deletion cmd/root.go
Expand Up @@ -8,7 +8,7 @@ import (
var RootCmd = &cobra.Command{
Use: "atmos",
Short: "Universal Tool for DevOps and Cloud Automation",
Long: `'atmos'' is universal tool for DevOps and cloud automation used for provisioning, managing and orchestrating workflows across various toolchains`,
Long: `'atmos'' is a universal tool for DevOps and cloud automation used for provisioning, managing and orchestrating workflows across various toolchains`,
}

// Execute adds all child commands to the root command and sets flags appropriately.
Expand Down
13 changes: 3 additions & 10 deletions cmd/terraform.go
Expand Up @@ -10,8 +10,8 @@ import (
// terraformCmd represents the base command for all terraform sub-commands
var terraformCmd = &cobra.Command{
Use: "terraform",
Short: "terraform command",
Long: `This command runs terraform sub-commands`,
Short: "Execute 'terraform' commands",
Long: `This command runs terraform commands`,
FParseErrWhitelist: struct{ UnknownFlags bool }{UnknownFlags: true},
Run: func(cmd *cobra.Command, args []string) {
err := e.ExecuteTerraform(cmd, args)
Expand All @@ -25,13 +25,6 @@ var terraformCmd = &cobra.Command{
func init() {
// https://github.com/spf13/cobra/issues/739
terraformCmd.DisableFlagParsing = true
terraformCmd.PersistentFlags().StringP("stack", "s", "", "")

err := terraformCmd.MarkPersistentFlagRequired("stack")
if err != nil {
color.Red("%s\n\n", err)
os.Exit(1)
}

terraformCmd.PersistentFlags().StringP("stack", "s", "", "atmos terraform <terraform_command> <component> -s <stack>")
RootCmd.AddCommand(terraformCmd)
}
2 changes: 1 addition & 1 deletion cmd/terraform_generate.go
Expand Up @@ -7,7 +7,7 @@ import (
// terraformGenerateCmd generates backends and variables for terraform components
var terraformGenerateCmd = &cobra.Command{
Use: "generate",
Short: "generate",
Short: "Execute 'terraform generate' commands",
Long: "This command generates backend configs and variable configs for terraform components",
FParseErrWhitelist: struct{ UnknownFlags bool }{UnknownFlags: true},
}
Expand Down
6 changes: 3 additions & 3 deletions cmd/terraform_generate_backend.go
Expand Up @@ -10,8 +10,8 @@ import (
// terraformGenerateBackendCmd generates backend config for a terraform components
var terraformGenerateBackendCmd = &cobra.Command{
Use: "backend",
Short: "generate backend",
Long: `This command generates the backend config for a terraform component`,
Short: "Execute 'terraform generate backend' command",
Long: `This command generates the backend config for a terraform component: atmos terraform generate backend <component> -s <stack>`,
FParseErrWhitelist: struct{ UnknownFlags bool }{UnknownFlags: false},
Run: func(cmd *cobra.Command, args []string) {
err := e.ExecuteTerraformGenerateBackend(cmd, args)
Expand All @@ -24,7 +24,7 @@ var terraformGenerateBackendCmd = &cobra.Command{

func init() {
terraformGenerateBackendCmd.DisableFlagParsing = false
terraformGenerateBackendCmd.PersistentFlags().StringP("stack", "s", "", "")
terraformGenerateBackendCmd.PersistentFlags().StringP("stack", "s", "", "atmos terraform generate backend <component> -s <stack>")

err := terraformGenerateBackendCmd.MarkPersistentFlagRequired("stack")
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions cmd/terraform_generate_backends.go
Expand Up @@ -10,7 +10,7 @@ import (
// terraformGenerateBackendsCmd generates backend configs for all terraform components
var terraformGenerateBackendsCmd = &cobra.Command{
Use: "backends",
Short: "generate backends",
Short: "Execute 'terraform generate backends' command",
Long: `This command generates the backend configs for all terraform components`,
FParseErrWhitelist: struct{ UnknownFlags bool }{UnknownFlags: false},
Run: func(cmd *cobra.Command, args []string) {
Expand All @@ -32,5 +32,5 @@ func init() {
os.Exit(1)
}

terraformGenerateCmd.AddCommand(terraformGenerateBackendsCmd)
// terraformGenerateCmd.AddCommand(terraformGenerateBackendsCmd)
}
2 changes: 1 addition & 1 deletion cmd/version.go
Expand Up @@ -9,7 +9,7 @@ var Version = "0.0.1"

var versionCmd = &cobra.Command{
Use: "version",
Short: "version command",
Short: "Print the CLI version",
Long: `This command prints the CLI version`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(Version)
Expand Down
4 changes: 4 additions & 0 deletions internal/exec/helmfile.go
Expand Up @@ -20,6 +20,10 @@ func ExecuteHelmfile(cmd *cobra.Command, args []string) error {
return err
}

if info.NeedHelp == true {
return nil
}

if len(info.Stack) < 1 {
return errors.New("stack must be specified")
}
Expand Down
61 changes: 61 additions & 0 deletions internal/exec/help.go
@@ -0,0 +1,61 @@
package exec

import (
"fmt"
"github.com/fatih/color"
)

// processHelp processes help commands
func processHelp(componentType string, command string) error {
if len(command) == 0 {
fmt.Println(fmt.Sprintf("'atmos' supports all native '%s' commands.", componentType))
fmt.Println(fmt.Sprintf("In addition, 'component' and 'stack' are required in order to generate variables for the component in the stack."))
color.Cyan(fmt.Sprintf("atmos %s <command> <component> -s <stack> [options]", componentType))
color.Cyan(fmt.Sprintf("atmos %s <command> <component> --stack <stack> [options]", componentType))

if componentType == "terraform" {
fmt.Println()
color.Cyan("Differences from native terraform:")
fmt.Println(" - before executing other 'terraform' commands, 'atmos' calls 'terraform init'")
fmt.Println(" - 'atmos' supports 'terraform deploy' command which calls 'terraform plan' and then 'terraform apply'")
fmt.Println(" - 'terraform deploy' command supports '--deploy-run-init=true/false' flag to enable/disable running 'terraform init' " +
"before executing the command")
fmt.Println(" - 'terraform deploy' command sets '-auto-approve' flag before running 'terraform apply'")
fmt.Println(" - 'terraform apply' and 'terraform deploy' commands support '--from-plan' flag. If the flag is specified, the commands " +
"will use the previously generated 'planfile' instead of generating a new 'varfile'")
fmt.Println(" - 'atmos' supports 'terraform clean' command which deletes the '.terraform' folder, '.terraform.lock.hcl' lock file, " +
"and the previously generated 'planfile' and 'varfile' for the specified component and stack")
fmt.Println(" - 'atmos terraform workspace' command first calls 'terraform init -reconfigure', then 'terraform workspace select', " +
"and if the workspace was not created before, it then calls 'terraform workspace new'")
fmt.Println(" - 'atmos terraform import' looks for 'region' in the variables for the specified component and stack, and if it finds it, " +
"sets 'AWS_REGION=<region>' ENV var before executing the command")
}

if componentType == "helmfile" {
fmt.Println()
color.Cyan("Differences from native helmfile:")
fmt.Println(" - 'atmos helmfile' commands support '[global options]' in the command-line argument '--global-options'. " +
"Usage: atmos helmfile <command> <component> -s <stack> [command options] [arguments...] --global-options=\"--no-color --namespace=test\"")
fmt.Println(" - before executing the 'helmfile' commands, 'atmos' calls 'aws eks update-kubeconfig' to read kubeconfig from the EKS cluster " +
"and use it to authenticate with the cluster")
}

err := execCommand(componentType, []string{"--help"}, "", nil)
if err != nil {
return err
}
} else {
fmt.Println(fmt.Sprintf("'atmos' supports native '%s %s' command with all the options, arguments and flags.", componentType, command))
fmt.Println(fmt.Sprintf("In addition, 'component' and 'stack' are required in order to generate variables for the component in the stack."))
color.Cyan(fmt.Sprintf("atmos %s %s <component> -s <stack> [options]", componentType, command))
color.Cyan(fmt.Sprintf("atmos %s %s <component> --stack <stack> [options]", componentType, command))

err := execCommand(componentType, []string{command, "--help"}, "", nil)
if err != nil {
return err
}
}

fmt.Println()
return nil
}
11 changes: 11 additions & 0 deletions internal/exec/terraform.go
Expand Up @@ -23,6 +23,10 @@ func ExecuteTerraform(cmd *cobra.Command, args []string) error {
return err
}

if info.NeedHelp == true {
return nil
}

if len(info.Stack) < 1 {
return errors.New("stack must be specified")
}
Expand Down Expand Up @@ -277,6 +281,13 @@ func ExecuteTerraform(cmd *cobra.Command, args []string) error {
}
}

// Check `region` for `terraform import`
if info.SubCommand == "import" {
if region, regionExist := info.ComponentVarsSection["region"].(string); regionExist {
info.ComponentEnvList = append(info.ComponentEnvList, fmt.Sprintf("AWS_REGION=%s", region))
}
}

// Execute the command
if info.SubCommand != "workspace" {
err = execCommand(info.Command, allArgsAndFlags, componentPath, info.ComponentEnvList)
Expand Down
69 changes: 52 additions & 17 deletions internal/exec/utils.go
Expand Up @@ -28,6 +28,8 @@ var (
g.DeployRunInitFlag,
g.AutoGenerateBackendFileFlag,
g.FromPlanFlag,
g.HelpFlag1,
g.HelpFlag2,
}
)

Expand Down Expand Up @@ -129,12 +131,6 @@ func processConfigAndStacks(componentType string, cmd *cobra.Command, args []str
if err != nil {
return configAndStacksInfo, err
}
flags := cmd.Flags()

configAndStacksInfo.Stack, err = flags.GetString("stack")
if err != nil {
return configAndStacksInfo, err
}

argsAndFlagsInfo, err := processArgsAndFlags(args)
if err != nil {
Expand All @@ -152,10 +148,33 @@ func processConfigAndStacks(componentType string, cmd *cobra.Command, args []str
configAndStacksInfo.DeployRunInit = argsAndFlagsInfo.DeployRunInit
configAndStacksInfo.AutoGenerateBackendFile = argsAndFlagsInfo.AutoGenerateBackendFile
configAndStacksInfo.UseTerraformPlan = argsAndFlagsInfo.UseTerraformPlan
configAndStacksInfo.NeedHelp = argsAndFlagsInfo.NeedHelp

// Check if `-h` or `--help` flags are specified
if argsAndFlagsInfo.NeedHelp == true {
err = processHelp(componentType, argsAndFlagsInfo.SubCommand)
if err != nil {
return configAndStacksInfo, err
}
return configAndStacksInfo, nil
}

flags := cmd.Flags()
configAndStacksInfo.Stack, err = flags.GetString("stack")
if err != nil {
return configAndStacksInfo, err
}

// Check if stack was provided
if len(configAndStacksInfo.Stack) < 1 {
message := fmt.Sprintf("'stack' is required. Usage: atmos %s <command> <component> -s <stack>", componentType)
return configAndStacksInfo, errors.New(message)
}

// Check if component was provided
if len(configAndStacksInfo.ComponentFromArg) < 1 {
return configAndStacksInfo, errors.New("'component' is required")
message := fmt.Sprintf("'component' is required. Usage: atmos %s <command> <component> <arguments_and_flags>", componentType)
return configAndStacksInfo, errors.New(message)
}

// Process and merge CLI configurations
Expand Down Expand Up @@ -454,6 +473,10 @@ func processArgsAndFlags(inputArgsAndFlags []string) (c.ArgsAndFlagsInfo, error)
info.UseTerraformPlan = true
}

if arg == g.HelpFlag1 || arg == g.HelpFlag2 {
info.NeedHelp = true
}

for _, f := range commonFlags {
if arg == f {
indexesToRemove = append(indexesToRemove, i)
Expand All @@ -479,18 +502,30 @@ func processArgsAndFlags(inputArgsAndFlags []string) (c.ArgsAndFlagsInfo, error)
}
}

// Handle the legacy command `terraform write varfile`
if additionalArgsAndFlags[0] == "write" && additionalArgsAndFlags[1] == "varfile" {
info.SubCommand = "write varfile"
info.ComponentFromArg = additionalArgsAndFlags[2]
info.AdditionalArgsAndFlags = additionalArgsAndFlags[3:]
} else {
info.SubCommand = additionalArgsAndFlags[0]
info.ComponentFromArg = additionalArgsAndFlags[1]
info.AdditionalArgsAndFlags = additionalArgsAndFlags[2:]
info.GlobalOptions = globalOptions

if info.NeedHelp == true {
if len(additionalArgsAndFlags) > 0 {
info.SubCommand = additionalArgsAndFlags[0]
}
return info, nil
}

info.GlobalOptions = globalOptions
if len(additionalArgsAndFlags) > 1 {
// Handle the legacy command `terraform write varfile`
if additionalArgsAndFlags[0] == "write" && additionalArgsAndFlags[1] == "varfile" {
info.SubCommand = "write varfile"
info.ComponentFromArg = additionalArgsAndFlags[2]
info.AdditionalArgsAndFlags = additionalArgsAndFlags[3:]
} else {
info.SubCommand = additionalArgsAndFlags[0]
info.ComponentFromArg = additionalArgsAndFlags[1]
info.AdditionalArgsAndFlags = additionalArgsAndFlags[2:]
}
} else {
message := "invalid number of arguments. Usage: atmos <command> <component> <arguments_and_flags>"
return info, errors.New(message)
}

return info, nil
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/config/schema.go
Expand Up @@ -68,6 +68,7 @@ type ArgsAndFlagsInfo struct {
DeployRunInit string
AutoGenerateBackendFile string
UseTerraformPlan bool
NeedHelp bool
}

type ConfigAndStacksInfo struct {
Expand Down Expand Up @@ -97,4 +98,5 @@ type ConfigAndStacksInfo struct {
AutoGenerateBackendFile string
UseTerraformPlan bool
ComponentInheritanceChain []string
NeedHelp bool
}
3 changes: 3 additions & 0 deletions pkg/globals/globals.go
Expand Up @@ -18,6 +18,9 @@ const (
AutoGenerateBackendFileFlag = "--auto-generate-backend-file"

FromPlanFlag = "--from-plan"

HelpFlag1 = "-h"
HelpFlag2 = "--help"
)

var (
Expand Down

0 comments on commit 89a2ad0

Please sign in to comment.