Skip to content

Commit

Permalink
Merge pull request #472 from hazelops/IZE-582-IZE-361-IZE-446
Browse files Browse the repository at this point in the history
  • Loading branch information
psihachina authored Sep 30, 2022
2 parents bcc58f4 + fa1d3a9 commit 9153744
Show file tree
Hide file tree
Showing 24 changed files with 856 additions and 400 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ ize
.terraform
examples/**/.ize/env/*
!examples/**/.ize/env/testnut
backend.tf
terraform.tfvars
Empty file.
11 changes: 11 additions & 0 deletions examples/multistate-monorepo/.ize/env/testnut/api/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
variable "env" {}
variable "namespace" {}
variable "aws_profile" {}
variable "aws_region" {}
variable "ssh_public_key" {}
variable "ec2_key_pair_name" {}

locals {
env = var.env
namespace = var.namespace
}
60 changes: 60 additions & 0 deletions examples/multistate-monorepo/.ize/env/testnut/ize.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
aws_region = "us-east-1" # (required) AWS Region of this environment should be specified here. Can be overriden by AWS_PROFILE env var or --aws-region flag.
namespace = "testnut" # (required) Namespace of the project can be specified here. It is used as a base for all naming. It can be overridden by NAMESPACE env var or --namespace flag.
terraform_version = "1.2.6" # (optional) Terraform version can be set here. 1.1.3 by default
# prefer_runtime = "" # (optional) Prefer a specific runtime. (native or docker) (default 'native')
# tag = "" # (optional) Tag can be set statically. Normally it is being constructed automatically based on the git revision.
# plain_text = false # (optional) Plain text output can be enabled here. Default is false. Can be overridden by IZE_PLAIN_TEXT env var or --plain-text flag.
# env = "dev" # (optional) Environment name can be specified here. Normally it should be passed via `ENV` variable or --env flag.
# env_dir = "" # (optional) Environment directory can be specified here. Normally it's calculated automatically based on the directory structure convention.
# docker_registry = "" # (optional) Docker registry can be set here. By default it uses ECR repo with the name of the service.
# tf_log_path = "" # (optional) TF_LOG_PATH can be set here.
# custom_prompt = false # (optional) Custom prompt can be enabled here for all console connections. Default: false.
# aws_profile = "" # (optional) AWS Profile can be specified here (but normally it's specified via AWS_PROFILE env var)
# log_level = "" # (optional) Log level can be specified here. Possible levels: info, debug, trace, panic, warn, error, fatal(default). Can be overridden via IZE_LOG_LEVEL env var or via --log-level flag.
# ize_dir = "" # (optional) Ize directory can be specified here. Normally it's assumed to be .infra or .ize in the current repo.
# apps_path = "" # (optional) Path to apps directory can be set. By default apps are searched in 'apps' and 'projects' directories. This is needed in case your repo structure is not purely ize-structured (let's say you have 'src' repo in your dotnet app, as an example)
# root_dir = "" # (optional) Project directory can be set here. By default it's the current directory, but in case you prefer to run ize from the outside of repo it may be useful (uncommon).
# tf_log = "" # (optional) Terraform TF_LOG can be set here. Can be TRACE, DEBUG, INFO, WARN or ERROR.
# config_file = "" # (optional) Path to ize.toml config file can be specified, but normally it's read from the environment's directory automatically.
# home = "" # (optional) User home directory can be specified here. Normally $HOME is used.

[terraform.infra]
aws_region = "us-east-1" # (optional) Terraform-specific AWS Region of this environment should be specified here. Normally global AWS_REGION is used.
# aws_profile = "" # (optional) Terraform-specific AWS profile (optional) can be specified here (but normally it should be inherited from a global AWS_PROFILE).
# version = "" # (optional) Terraform version can be set here. 1.1.3 by default.
# state_bucket_region = "" # (optional) Terraform state bucket region can be specified here. Normally AWS_REGION is used here. Can be overriden via env vars or flags.
# state_bucket_name = "" # (optional) Terraform state bucket name can be specified here. Normally it's generated and defaults to <NAMESPACE>-tf-state
# root_domain_name = "" # (optional) Root domain name can be set here. This is the main domain that will be passed to the terraform. Generally if your app lives at 'api.dev.nutcorp.net' the root domain is `nutcorp.net`

[terraform.api]
# aws_profile = "" # (optional) Terraform-specific AWS profile (optional) can be specified here (but normally it should be inherited from a global AWS_PROFILE).
# version = "" # (optional) Terraform version can be set here. 1.1.3 by default.
# state_bucket_region = "" # (optional) Terraform state bucket region can be specified here. Normally AWS_REGION is used here. Can be overriden via env vars or flags.
# state_bucket_name = "" # (optional) Terraform state bucket name can be specified here. Normally it's generated and defaults to <NAMESPACE>-tf-state
# root_domain_name = "" # (optional) Root domain name can be set here. This is the main domain that will be passed to the terraform. Generally if your app lives at 'api.dev.nutcorp.net' the root domain is `nutcorp.net`
depends_on = ["vpc"]

[terraform.vpc]
# aws_profile = "" # (optional) Terraform-specific AWS profile (optional) can be specified here (but normally it should be inherited from a global AWS_PROFILE).
# version = "" # (optional) Terraform version can be set here. 1.1.3 by default.
# state_bucket_region = "" # (optional) Terraform state bucket region can be specified here. Normally AWS_REGION is used here. Can be overriden via env vars or flags.
# state_bucket_name = "" # (optional) Terraform state bucket name can be specified here. Normally it's generated and defaults to <NAMESPACE>-tf-state
# root_domain_name = "" # (optional) Root domain name can be set here. This is the main domain that will be passed to the terraform. Generally if your app lives at 'api.dev.nutcorp.net' the root domain is `nutcorp.net`
depends_on = ["infra"]

# [ecs.<app>]
# timeout = "" # (optional) ECS deployment timeout can be specified here.
# docker_registry = "" # (optional) Docker registry can be set here. By default it uses ECR repo with the name of the service.
# skip_deploy = false # skip deploy app
# path = "" # (optional) Path to ecs app folder can be specified here. By default it's derived from apps path and app name.
# unsafe = false # (optional) Enables unsafe mode that increases deploy time on a cost of shorter healtchecks.
# image = "" # (optional) Docker image can be specified here. By default it's derived from the app name.
# cluster = "" # (optional) ECS cluster can be specified here. By default it's derived from env & namespace
# task_definition_revision = "" # (optional) Task definition revision can be specified here. By default latest revision is used to perform a deployment. Normally this parameter can be used via cli during specific deployment needs.

# [serverless.<name>]
# node_version = "16" # (optional) Node version that will be used by nvm can be specified here that. Default is v14.
# path = "" # (optional) Path to the serverless app directory can be specified here. Normally it's derived from app directory and app name.
# sls_node_modules_cache_mount = "" # (optional) SLS node_modules cache mount path can be specified here. It's used to store cache during CI/CD process.
# file = "" # (optional) Path to serverless file can be specified here. Normally it's serverless.yml in the app directory.
# create_domain = false # (optional) Create domain for the serverless domain manager during the deployment.
Empty file.
11 changes: 11 additions & 0 deletions examples/multistate-monorepo/.ize/env/testnut/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
variable "env" {}
variable "namespace" {}
variable "aws_profile" {}
variable "aws_region" {}
variable "ssh_public_key" {}
variable "ec2_key_pair_name" {}

locals {
env = var.env
namespace = var.namespace
}
Empty file.
11 changes: 11 additions & 0 deletions examples/multistate-monorepo/.ize/env/testnut/vpc/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
variable "env" {}
variable "namespace" {}
variable "aws_profile" {}
variable "aws_region" {}
variable "ssh_public_key" {}
variable "ec2_key_pair_name" {}

locals {
env = var.env
namespace = var.namespace
}
4 changes: 2 additions & 2 deletions examples/sls-apps-monorepo/.ize/env/testnut/ize.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ terraform_version = "1.2.6" # (optional) Terraform version can be se
# config_file = "" # (optional) Path to ize.toml config file can be specified, but normally it's read from the environment's directory automatically.
# home = "" # (optional) User home directory can be specified here. Normally $HOME is used.

# [terraform.infra]
# aws_region = "" # (optional) Terraform-specific AWS Region of this environment should be specified here. Normally global AWS_REGION is used.
[terraform.infra]
aws_region = "us-east-1" # (optional) Terraform-specific AWS Region of this environment should be specified here. Normally global AWS_REGION is used.
# aws_profile = "" # (optional) Terraform-specific AWS profile (optional) can be specified here (but normally it should be inherited from a global AWS_PROFILE).
# version = "" # (optional) Terraform version can be set here. 1.1.3 by default.
# state_bucket_region = "" # (optional) Terraform state bucket region can be specified here. Normally AWS_REGION is used here. Can be overriden via env vars or flags.
Expand Down
124 changes: 55 additions & 69 deletions internal/commands/down.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/pterm/pterm"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"time"
)

type DownOptions struct {
Expand Down Expand Up @@ -114,20 +115,21 @@ func (o *DownOptions) Complete(cmd *cobra.Command, args []string) error {
}

if o.Config.Terraform == nil {
o.Config.Terraform = map[string]*config.Terraform{}
o.Config.Terraform["infra"] = &config.Terraform{}
return fmt.Errorf("you must specify at least one terraform stack in ize.toml")
}

if len(o.Config.Terraform["infra"].AwsProfile) == 0 {
o.Config.Terraform["infra"].AwsProfile = o.Config.AwsProfile
}
if _, ok := o.Config.Terraform["infra"]; ok {
if len(o.Config.Terraform["infra"].AwsProfile) == 0 {
o.Config.Terraform["infra"].AwsProfile = o.Config.AwsProfile
}

if len(o.Config.Terraform["infra"].AwsRegion) == 0 {
o.Config.Terraform["infra"].AwsRegion = o.Config.AwsRegion
}
if len(o.Config.Terraform["infra"].AwsRegion) == 0 {
o.Config.Terraform["infra"].AwsRegion = o.Config.AwsRegion
}

if len(o.Config.Terraform["infra"].Version) == 0 {
o.Config.Terraform["infra"].Version = o.Config.TerraformVersion
if len(o.Config.Terraform["infra"].Version) == 0 {
o.Config.Terraform["infra"].Version = o.Config.TerraformVersion
}
}
} else {
if err := requirements.CheckRequirements(requirements.WithIzeStructure(), requirements.WithConfigFile()); err != nil {
Expand Down Expand Up @@ -172,7 +174,7 @@ func (o *DownOptions) Run() error {
return err
}
} else {
err := destroyApp(ui, o)
err := destroyApp(o.AppName, o.Config, ui)
if err != nil {
return err
}
Expand Down Expand Up @@ -218,58 +220,34 @@ func destroyAll(ui terminal.UI, o *DownOptions) error {
err := manager.InReversDependencyOrder(aws.BackgroundContext(), o.Config.GetApps(), func(c context.Context, name string) error {
o.Config.AwsProfile = o.Config.Terraform["infra"].AwsProfile

var m manager.Manager

if app, ok := o.Config.Serverless[name]; ok {
app.Name = name
m = &serverless.Manager{
Project: o.Config,
App: app,
}
}
if app, ok := o.Config.Alias[name]; ok {
app.Name = name
m = &alias.Manager{
Project: o.Config,
App: app,
}
}
if app, ok := o.Config.Ecs[name]; ok {
app.Name = name
m = &ecs.Manager{
Project: o.Config,
App: app,
}
}

// destroy
err := m.Destroy(ui)
if err != nil {
return fmt.Errorf("can't destroy app: %w", err)
}

return nil
return destroyApp(name, o.Config, ui)
})
if err != nil {
return err
}

err = destroyInfra(ui, o.Config, o.SkipGen)
if err != nil {
return err
if _, ok := o.Config.Terraform["infra"]; ok {
err = destroyInfra("infra", o.Config, o.SkipGen, ui)
if err != nil {
return err
}
}

err = manager.InReversDependencyOrder(aws.BackgroundContext(), o.Config.GetStates(), func(c context.Context, name string) error {
o.Config.AwsProfile = o.Config.Terraform["infra"].AwsProfile

return destroyInfra(name, o.Config, o.SkipGen, ui)
})

ui.Output("Destroy all completed!\n", terminal.WithSuccessStyle())
time.Sleep(time.Millisecond * 200)

return nil
}

func destroyInfra(ui terminal.UI, config *config.Project, skipGen bool) error {
func destroyInfra(state string, config *config.Project, skipGen bool, ui terminal.UI) error {
if !skipGen {
err := GenerateTerraformFiles(
config,
"",
)
err := GenerateTerraformFiles(state, "", config)
if err != nil {
return err
}
Expand All @@ -296,9 +274,9 @@ func destroyInfra(ui terminal.UI, config *config.Project, skipGen bool) error {

switch config.PreferRuntime {
case "docker":
tf = terraform.NewDockerTerraform(config.Terraform["infra"].Version, []string{"destroy", "-auto-approve"}, env, nil, config)
tf = terraform.NewDockerTerraform(state, []string{"init", "-input=true"}, env, nil, config)
case "native":
tf = terraform.NewLocalTerraform(config.Terraform["infra"].Version, []string{"destroy", "-auto-approve"}, env, nil, config)
tf = terraform.NewLocalTerraform(state, []string{"init", "-input=true"}, env, nil, config)
err = tf.Prepare()
if err != nil {
return fmt.Errorf("can't destroy infra: %w", err)
Expand All @@ -307,47 +285,57 @@ func destroyInfra(ui terminal.UI, config *config.Project, skipGen bool) error {
return fmt.Errorf("can't supported %s runtime", config.PreferRuntime)
}

ui.Output("Running terraform destroy...", terminal.WithHeaderStyle())
ui.Output("Execution terraform init...", terminal.WithHeaderStyle())

err = tf.RunUI(ui)
if err != nil {
return err
}

//terraform destroy run options
tf.NewCmd([]string{"destroy", "-auto-approve"})

ui.Output("Execution terraform destroy...", terminal.WithHeaderStyle())

err = tf.RunUI(ui)
if err != nil {
return fmt.Errorf("can't deploy infra: %w", err)
}

ui.Output("Terraform destroy completed!\n", terminal.WithSuccessStyle())

return nil
}

func destroyApp(ui terminal.UI, o *DownOptions) error {
func destroyApp(name string, cfg *config.Project, ui terminal.UI) error {
var m manager.Manager
var icon string

m = &ecs.Manager{
Project: o.Config,
App: &config.Ecs{Name: o.AppName},
Project: cfg,
App: &config.Ecs{Name: name},
}

if app, ok := o.Config.Serverless[o.AppName]; ok {
app.Name = o.AppName
if app, ok := cfg.Serverless[name]; ok {
app.Name = name
m = &serverless.Manager{
Project: o.Config,
Project: cfg,
App: app,
}
icon = app.Icon
}
if app, ok := o.Config.Alias[o.AppName]; ok {
app.Name = o.AppName
if app, ok := cfg.Alias[name]; ok {
app.Name = name
m = &alias.Manager{
Project: o.Config,
Project: cfg,
App: app,
}
icon = app.Icon
}
if app, ok := o.Config.Ecs[o.AppName]; ok {
app.Name = o.AppName
if app, ok := cfg.Ecs[name]; ok {
app.Name = name
m = &ecs.Manager{
Project: o.Config,
Project: cfg,
App: app,
}
icon = app.Icon
Expand All @@ -357,16 +345,14 @@ func destroyApp(ui terminal.UI, o *DownOptions) error {
icon += " "
}

ui.Output("Destroying %s%s app...\n", icon, o.AppName, terminal.WithHeaderStyle())
sg := ui.StepGroup()
defer sg.Wait()
ui.Output("Destroying %s%s app...\n", icon, name, terminal.WithHeaderStyle())

err := m.Destroy(ui)
if err != nil {
return fmt.Errorf("can't down: %w", err)
}

ui.Output("Destroy app %s%s completed\n", icon, o.AppName, terminal.WithSuccessStyle())
ui.Output("Destroy app %s%s completed\n", icon, name, terminal.WithSuccessStyle())

return nil
}
Loading

0 comments on commit 9153744

Please sign in to comment.