diff --git a/go.mod b/go.mod index d54ff461..3ee52034 100644 --- a/go.mod +++ b/go.mod @@ -7,12 +7,12 @@ require ( github.com/containerd/containerd v1.5.4 // indirect github.com/docker/docker v20.10.7+incompatible github.com/docker/go-connections v0.4.0 // indirect - github.com/hairyhenderson/gomplate v3.5.0+incompatible github.com/hashicorp/hcl/v2 v2.10.1 github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 github.com/morikuni/aec v1.0.0 // indirect github.com/pterm/pterm v0.12.31 github.com/spf13/cobra v1.2.1 github.com/spf13/viper v1.8.1 + github.com/zclconf/go-cty v1.8.0 go.uber.org/zap v1.19.0 ) diff --git a/go.sum b/go.sum index 53a5f661..dc300586 100644 --- a/go.sum +++ b/go.sum @@ -416,8 +416,6 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hairyhenderson/gomplate v3.5.0+incompatible h1:6nGYcdaY6UvcOG79HZl1zYURQ2pTGTvX6sjmoB8GODU= -github.com/hairyhenderson/gomplate v3.5.0+incompatible/go.mod h1:eZqaEBQQCLueOLnZ2NWeVbi1zYJy8/rRl3uQHmT9QPQ= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= diff --git a/internal/commands/env.go b/internal/commands/env.go index 85f42384..f719bcb4 100644 --- a/internal/commands/env.go +++ b/internal/commands/env.go @@ -39,7 +39,7 @@ func (b *commandsBuilder) newEnvCmd() *envCmd { pterm.DefaultSection.Printfln("Starting generate terrafrom files") - err = template.GenereateBackendTf(template.BackendOpts{ + err = template.GenerateBackendTf(template.BackendOpts{ ENV: cc.config.Env, LOCALSTACK_ENDPOINT: "", TERRAFORM_STATE_BUCKET_NAME: fmt.Sprintf("%s-tf-state", cc.config.Namespace), diff --git a/internal/template/backend.go b/internal/template/backend.go deleted file mode 100644 index 04891f5b..00000000 --- a/internal/template/backend.go +++ /dev/null @@ -1,54 +0,0 @@ -package template - -var backendTemplate = `{{if or (.ENV | strings.Contains "localstack") (.ENV | strings.Contains "local") }}terraform { - backend "local" {} -} - -provider "aws" { - profile = var.aws_profile - region = var.aws_region - s3_force_path_style = true - secret_key = "mock_secret_key" - skip_credentials_validation = true - skip_metadata_api_check = true - skip_requesting_account_id = true - - endpoints { - apigateway = "{{ .LOCALSTACK_ENDPOINT }}" - acm = "{{ .LOCALSTACK_ENDPOINT }}" - cloudformation = "{{ .LOCALSTACK_ENDPOINT }}" - cloudwatch = "{{ .LOCALSTACK_ENDPOINT }}" - ec2 = "{{ .LOCALSTACK_ENDPOINT }}" - dynamodb = "{{ .LOCALSTACK_ENDPOINT }}" - es = "{{ .LOCALSTACK_ENDPOINT }}" - firehose = "{{ .LOCALSTACK_ENDPOINT }}" - iam = "{{ .LOCALSTACK_ENDPOINT }}" - kinesis = "{{ .LOCALSTACK_ENDPOINT }}" - lambda = "{{ .LOCALSTACK_ENDPOINT }}" - route53 = "{{ .LOCALSTACK_ENDPOINT }}" - redshift = "{{ .LOCALSTACK_ENDPOINT }}" - s3 = "{{ .LOCALSTACK_ENDPOINT }}" - secretsmanager = "{{ .LOCALSTACK_ENDPOINT }}" - ses = "{{ .LOCALSTACK_ENDPOINT }}" - sns = "{{ .LOCALSTACK_ENDPOINT }}" - sqs = "{{ .LOCALSTACK_ENDPOINT }}" - ssm = "{{ .LOCALSTACK_ENDPOINT }}" - stepfunctions = "{{ .LOCALSTACK_ENDPOINT }}" - sts = "{{ .LOCALSTACK_ENDPOINT }}" - ecs = "{{ .LOCALSTACK_ENDPOINT }}" - ecr = "{{ .LOCALSTACK_ENDPOINT }}" - } -}{{else}}provider "aws" { - profile = var.aws_profile - region = var.aws_region -} - -terraform { - backend "s3" { - bucket = "{{if .TERRAFORM_STATE_BUCKET_NAME}}{{ .TERRAFORM_STATE_BUCKET_NAME }}{{else}}nutcorpnet-tf-state{{end}}" - key = "{{if .TERRAFORM_STATE_KEY}}{{ .TERRAFORM_STATE_KEY }}{{else}}foo/terraform.tfstate{{end}}" - region = "{{if .TERRAFORM_STATE_REGION}}{{ .TERRAFORM_STATE_REGION }}{{else}}us-east-1{{end}}" - profile = "{{if .TERRAFORM_STATE_PROFILE}}{{ .TERRAFORM_STATE_PROFILE }}{{else}}nutcorp-dev{{end}}" - dynamodb_table = "{{if .TERRAFORM_STATE_DYNAMODB_TABLE}}{{ .TERRAFORM_STATE_DYNAMODB_TABLE }}{{else}}tf-state-lock{{end}}" - } -}{{end}}` diff --git a/internal/template/template.go b/internal/template/template.go index f4144dd1..cbd99b90 100644 --- a/internal/template/template.go +++ b/internal/template/template.go @@ -1,13 +1,13 @@ package template import ( - "context" "fmt" "os" "strings" - "text/template" - "github.com/hairyhenderson/gomplate/conv" + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/hclwrite" + "github.com/zclconf/go-cty/cty" ) const ( @@ -15,45 +15,119 @@ const ( vars = "terraform.tfvars" ) -func GenereateBackendTf(opts BackendOpts, path string) error { - tmpl, err := template.New(backend).Funcs(CreateStringFuncs(context.Background())).Parse(backendTemplate) - if err != nil { - return err - } +func GenerateVarsTf(opts VarsOpts, path string) error { + f := hclwrite.NewEmptyFile() - file, err := os.Create(fmt.Sprintf("%s/%s", path, backend)) + rootBody := f.Body() + + rootBody.SetAttributeValue("env", cty.StringVal(opts.ENV)) + rootBody.SetAttributeValue("aws_profile", cty.StringVal(opts.AWS_PROFILE)) + rootBody.SetAttributeValue("aws_region", cty.StringVal(opts.AWS_REGION)) + rootBody.SetAttributeValue("ec2_key_pair_name", cty.StringVal(opts.EC2_KEY_PAIR_NAME)) + rootBody.SetAttributeValue("docker_image_tag", cty.StringVal(opts.TAG)) + rootBody.SetAttributeValue("ssh_public_key", cty.StringVal(opts.SSH_PUBLIC_KEY)) + rootBody.SetAttributeValue("docker_registry", cty.StringVal(opts.DOCKER_REGISTRY)) + rootBody.SetAttributeValue("namespace", cty.StringVal(opts.NAMESPACE)) + + file, err := os.Create(fmt.Sprintf("%s/%s", path, vars)) if err != nil { return err } - err = tmpl.Execute(file, opts) + defer file.Close() + + _, err = f.WriteTo(file) if err != nil { return err } - file.Close() - return nil } -func GenerateVarsTf(opts VarsOpts, path string) error { - tmpl, err := template.New(vars).Funcs(CreateStringFuncs(context.Background())).Parse(varsTemplate) - if err != nil { - return err +func GenerateBackendTf(opts BackendOpts, path string) error { + f := hclwrite.NewEmptyFile() + + if strings.Contains(opts.ENV, "localstack") || strings.Contains(opts.ENV, "local") { + rootBody := f.Body() + // AWS Provider block + providerBlock := rootBody.AppendNewBlock("provider", []string{"aws"}) + providerBlock.Body().SetAttributeTraversal("profile", hcl.Traversal{ + hcl.TraverseRoot{Name: "var"}, + hcl.TraverseAttr{Name: "aws_profile"}, + }) + providerBlock.Body().SetAttributeTraversal("region", hcl.Traversal{ + hcl.TraverseRoot{Name: "var"}, + hcl.TraverseAttr{Name: "aws_region"}, + }) + providerBlock.Body().SetAttributeValue("s3_force_path_style", cty.True) + providerBlock.Body().SetAttributeValue("secret_key", cty.StringVal("mock_secret_key")) + providerBlock.Body().SetAttributeValue("skip_credentials_validation", cty.True) + providerBlock.Body().SetAttributeValue("skip_metadata_api_check", cty.True) + providerBlock.Body().SetAttributeValue("skip_requesting_account_id", cty.True) + rootBody.AppendNewline() + + // Endpoints + endpointBlock := rootBody.AppendNewBlock("endpoints", []string{}) + endpointBlock.Body().SetAttributeValue("apigateway", cty.StringVal(opts.LOCALSTACK_ENDPOINT)) + endpointBlock.Body().SetAttributeValue("acm", cty.StringVal(opts.LOCALSTACK_ENDPOINT)) + endpointBlock.Body().SetAttributeValue("cloudformation", cty.StringVal(opts.LOCALSTACK_ENDPOINT)) + endpointBlock.Body().SetAttributeValue("cloudwatch", cty.StringVal(opts.LOCALSTACK_ENDPOINT)) + endpointBlock.Body().SetAttributeValue("ec2", cty.StringVal(opts.LOCALSTACK_ENDPOINT)) + endpointBlock.Body().SetAttributeValue("dynamodb", cty.StringVal(opts.LOCALSTACK_ENDPOINT)) + endpointBlock.Body().SetAttributeValue("es", cty.StringVal(opts.LOCALSTACK_ENDPOINT)) + endpointBlock.Body().SetAttributeValue("firehose", cty.StringVal(opts.LOCALSTACK_ENDPOINT)) + endpointBlock.Body().SetAttributeValue("iam", cty.StringVal(opts.LOCALSTACK_ENDPOINT)) + endpointBlock.Body().SetAttributeValue("kinesis", cty.StringVal(opts.LOCALSTACK_ENDPOINT)) + endpointBlock.Body().SetAttributeValue("lambda", cty.StringVal(opts.LOCALSTACK_ENDPOINT)) + endpointBlock.Body().SetAttributeValue("route53", cty.StringVal(opts.LOCALSTACK_ENDPOINT)) + endpointBlock.Body().SetAttributeValue("redshift", cty.StringVal(opts.LOCALSTACK_ENDPOINT)) + endpointBlock.Body().SetAttributeValue("s3", cty.StringVal(opts.LOCALSTACK_ENDPOINT)) + endpointBlock.Body().SetAttributeValue("secretsmanager", cty.StringVal(opts.LOCALSTACK_ENDPOINT)) + endpointBlock.Body().SetAttributeValue("ses", cty.StringVal(opts.LOCALSTACK_ENDPOINT)) + endpointBlock.Body().SetAttributeValue("sns", cty.StringVal(opts.LOCALSTACK_ENDPOINT)) + endpointBlock.Body().SetAttributeValue("sqs", cty.StringVal(opts.LOCALSTACK_ENDPOINT)) + endpointBlock.Body().SetAttributeValue("ssm", cty.StringVal(opts.LOCALSTACK_ENDPOINT)) + endpointBlock.Body().SetAttributeValue("stepfunctions", cty.StringVal(opts.LOCALSTACK_ENDPOINT)) + endpointBlock.Body().SetAttributeValue("sts", cty.StringVal(opts.LOCALSTACK_ENDPOINT)) + endpointBlock.Body().SetAttributeValue("ecs", cty.StringVal(opts.LOCALSTACK_ENDPOINT)) + endpointBlock.Body().SetAttributeValue("ecr", cty.StringVal(opts.LOCALSTACK_ENDPOINT)) + } else { + rootBody := f.Body() + // AWS Provider block + providerBlock := rootBody.AppendNewBlock("provider", []string{"aws"}) + providerBlock.Body().SetAttributeTraversal("profile", hcl.Traversal{ + hcl.TraverseRoot{Name: "var"}, + hcl.TraverseAttr{Name: "aws_profile"}, + }) + providerBlock.Body().SetAttributeTraversal("region", hcl.Traversal{ + hcl.TraverseRoot{Name: "var"}, + hcl.TraverseAttr{Name: "aws_region"}, + }) + rootBody.AppendNewline() + + // Terraform block + terraformBlock := f.Body().AppendNewBlock("terraform", []string{}) + // backend s3 block + backendBlock := terraformBlock.Body().AppendNewBlock("backend", []string{"s3"}) + backendBlock.Body().SetAttributeValue("bucket", cty.StringVal(opts.TERRAFORM_STATE_BUCKET_NAME)) + backendBlock.Body().SetAttributeValue("key", cty.StringVal(opts.TERRAFORM_STATE_KEY)) + backendBlock.Body().SetAttributeValue("region", cty.StringVal(opts.TERRAFORM_STATE_REGION)) + backendBlock.Body().SetAttributeValue("profile", cty.StringVal(opts.TERRAFORM_STATE_PROFILE)) + backendBlock.Body().SetAttributeValue("dynamodb_table", cty.StringVal(opts.TERRAFORM_STATE_DYNAMODB_TABLE)) } - file, err := os.Create(fmt.Sprintf("%s/%s", path, vars)) + file, err := os.Create(fmt.Sprintf("%s/%s", path, backend)) if err != nil { return err } - err = tmpl.Execute(file, opts) + defer file.Close() + + _, err = f.WriteTo(file) if err != nil { return err } - file.Close() - return nil } @@ -78,23 +152,3 @@ type BackendOpts struct { TERRAFORM_STATE_DYNAMODB_TABLE string TERRAFORM_AWS_PROVIDER_VERSION string } - -func CreateStringFuncs(ctx context.Context) map[string]interface{} { - f := map[string]interface{}{} - - ns := &StringFuncs{ctx} - f["strings"] = func() interface{} { return ns } - - f["contains"] = strings.Contains - - return f -} - -// Contains - -func (StringFuncs) Contains(substr string, s interface{}) bool { - return strings.Contains(conv.ToString(s), substr) -} - -type StringFuncs struct { - ctx context.Context -} diff --git a/internal/template/vars.go b/internal/template/vars.go deleted file mode 100644 index f3ea1e32..00000000 --- a/internal/template/vars.go +++ /dev/null @@ -1,10 +0,0 @@ -package template - -var varsTemplate = `env="{{ .ENV }}" -aws_profile="{{ .AWS_PROFILE }}" -aws_region="{{ .AWS_REGION }}" -ec2_key_pair_name="{{ .EC2_KEY_PAIR_NAME }}" -docker_image_tag="{{ .TAG }}" -ssh_public_key="{{ .SSH_PUBLIC_KEY }}" -docker_registry="{{ .DOCKER_REGISTRY }}" -namespace="{{ .NAMESPACE }}"`