From a310baa95620ea5a6b6978c431a870ad33150e3f Mon Sep 17 00:00:00 2001 From: Bill Monkman Date: Fri, 8 Nov 2019 11:40:43 -0800 Subject: [PATCH] Move kuberenetes stuff out into separate runs, fix backend, fix some issues with generated code --- internal/config/config.go | 1 + internal/generate/golang/generate.go | 1 + internal/generate/http/generate.go | 2 +- internal/generate/kubernetes/generate.go | 45 ++++++++++++++++--- internal/util/util.go | 9 +++- templates/golang/main.tmpl | 4 +- templates/golang/server.tmpl | 6 +-- .../remote-state/main.tf | 10 +++++ .../development/kubernetes/main.tf | 32 +++++++++++++ .../environments/development/main.tf | 37 ++++++++------- .../production/kubernetes/main.tf | 37 +++++++++++++++ .../terraform/environments/production/main.tf | 4 +- .../environments/staging/kubernetes/main.tf | 37 +++++++++++++++ .../terraform/environments/staging/main.tf | 5 ++- .../terraform/modules/environment/main.tf | 14 ------ .../kubernetes/terraform/modules/vpc/main.tf | 4 +- 16 files changed, 198 insertions(+), 50 deletions(-) rename templates/kubernetes/terraform/{global => bootstrap}/remote-state/main.tf (64%) create mode 100644 templates/kubernetes/terraform/environments/development/kubernetes/main.tf create mode 100644 templates/kubernetes/terraform/environments/production/kubernetes/main.tf create mode 100644 templates/kubernetes/terraform/environments/staging/kubernetes/main.tf diff --git a/internal/config/config.go b/internal/config/config.go index 51580a963..8ca1a09a9 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -87,6 +87,7 @@ type terraform struct { type eks struct { ClusterName string `yaml:"clusterName"` + WorkerAMI string `yaml:"workerAMI"` Deploy bool } diff --git a/internal/generate/golang/generate.go b/internal/generate/golang/generate.go index 84287a343..207d4a3c2 100644 --- a/internal/generate/golang/generate.go +++ b/internal/generate/golang/generate.go @@ -24,6 +24,7 @@ func Generate(t *templator.Templator, cfg *config.Commit0Config, service config. util.TemplateFileIfDoesNotExist(basePath, "main.go", t.Go.GoMain, wg, data) util.TemplateFileIfDoesNotExist(basePath, "go.mod", t.Go.GoMod, wg, data) + util.TemplateFileIfDoesNotExist(basePath, "server.go", t.Go.GoServer, wg, data) util.TemplateFileIfDoesNotExist(healthPath, "health.go", t.Go.GoHealthServer, wg, data) file := fmt.Sprintf("%s.go", service.Name) diff --git a/internal/generate/http/generate.go b/internal/generate/http/generate.go index 593356d00..c104e4f17 100644 --- a/internal/generate/http/generate.go +++ b/internal/generate/http/generate.go @@ -10,5 +10,5 @@ import ( func GenerateGoHTTPGW(templator *templator.Templator, data templator.GolangTemplateData, basePath string, wg *sync.WaitGroup) { path := filepath.Join(basePath, "http") - util.TemplateFileAndOverwrite(path, "main.go", templator.Go.GoHTTPGW, wg, data) + util.TemplateFileIfDoesNotExist(path, "main.go", templator.Go.GoHTTPGW, wg, data) } diff --git a/internal/generate/kubernetes/generate.go b/internal/generate/kubernetes/generate.go index a8b38a2a7..37a26047a 100644 --- a/internal/generate/kubernetes/generate.go +++ b/internal/generate/kubernetes/generate.go @@ -16,6 +16,8 @@ import ( "github.com/commitdev/commit0/internal/config" "github.com/commitdev/commit0/internal/templator" "github.com/commitdev/commit0/internal/util" + "github.com/kyokomi/emoji" + "github.com/logrusorgru/aurora" "github.com/manifoldco/promptui" "gopkg.in/yaml.v2" ) @@ -29,9 +31,26 @@ type Secrets struct { } } +// @TODO : These are specific to a k8s version. If we make the version a config option we will need to change this +var amiLookup = map[string]string{ + "us-east-1": "ami-0392bafc801b7520f", + "us-east-2": "ami-082bb518441d3954c", + "us-west-2": "ami-05d586e6f773f6abf", + "eu-west-1": "ami-059c6874350e63ca9", + "eu-central-1": "ami-0e21bc066a9dbabfa", +} + // Generate templates func Generate(t *templator.Templator, cfg *config.Commit0Config, wg *sync.WaitGroup, pathPrefix string) { - data := templator.GenericTemplateData{*cfg} + if cfg.Infrastructure.AWS.EKS.WorkerAMI == "" { + ami, found := amiLookup[cfg.Infrastructure.AWS.Region] + if !found { + log.Fatalln(aurora.Red(emoji.Sprintf(":exclamation: Unable to look up an AMI for the chosen region"))) + } + + cfg.Infrastructure.AWS.EKS.WorkerAMI = ami + } + data := templator.GenericTemplateData{Config: *cfg} t.Kubernetes.TemplateFiles(data, false, wg, pathPrefix) } @@ -50,17 +69,31 @@ func Execute(config *config.Commit0Config, pathPrefix string) { } envars := getAwsEnvars(readSecrets()) - log.Println("Planning infrastructure...") - execute(exec.Command("terraform", "init"), pathPrefix, envars) - execute(exec.Command("terraform", "plan"), pathPrefix, envars) + + pathPrefix = filepath.Join(pathPrefix, "kubernetes/terraform") + + // @TODO : A check here would be nice to see if this stuff exists first, mostly for testing + log.Println(aurora.Cyan(emoji.Sprintf(":alarm_clock: Initializing remote backend..."))) + execute(exec.Command("terraform", "init"), filepath.Join(pathPrefix, "bootstrap/remote-state"), envars) + execute(exec.Command("terraform", "apply", "-auto-approve"), filepath.Join(pathPrefix, "bootstrap/remote-state"), envars) + + log.Println(aurora.Cyan(":alarm_clock: Planning infrastructure...")) + execute(exec.Command("terraform", "init"), filepath.Join(pathPrefix, "environments/staging"), envars) + execute(exec.Command("terraform", "plan"), filepath.Join(pathPrefix, "environments/staging"), envars) + + log.Println(aurora.Cyan(":alarm_clock: Applying infrastructure configuration...")) + execute(exec.Command("terraform", "apply"), filepath.Join(pathPrefix, "environments/staging"), envars) + + log.Println(aurora.Cyan(":alarm_clock: Applying kubernetes configuration...")) + execute(exec.Command("terraform", "init"), filepath.Join(pathPrefix, "environments/staging/kubernetes"), envars) + execute(exec.Command("terraform", "plan"), filepath.Join(pathPrefix, "environments/staging/kubernetes"), envars) } } func execute(cmd *exec.Cmd, pathPrefix string, envars []string) { dir := util.GetCwd() - kubDir := path.Join(pathPrefix, "kubernetes/terraform/environments/staging") - cmd.Dir = path.Join(dir, kubDir) + cmd.Dir = path.Join(dir, pathPrefix) stdoutPipe, _ := cmd.StdoutPipe() stderrPipe, _ := cmd.StderrPipe() diff --git a/internal/util/util.go b/internal/util/util.go index fb941c41a..87245931d 100644 --- a/internal/util/util.go +++ b/internal/util/util.go @@ -21,9 +21,14 @@ func CreateDirIfDoesNotExist(path string) error { return nil } +func CleanGoIdentifier(identifier string) string { + return strings.ReplaceAll(identifier, "-", "") +} + var FuncMap = template.FuncMap{ - "Title": strings.Title, - "ToLower": strings.ToLower, + "Title": strings.Title, + "ToLower": strings.ToLower, + "CleanGoIdentifier": CleanGoIdentifier, } func GetCwd() string { diff --git a/templates/golang/main.tmpl b/templates/golang/main.tmpl index 60076eba8..944eb1a91 100644 --- a/templates/golang/main.tmpl +++ b/templates/golang/main.tmpl @@ -22,8 +22,8 @@ func main() { //Server initialization & registration healthServer := health.NewHealthServer() healthpb.RegisterHealthServer(s, healthServer) - {{ .Config.Name }}Server := {{ .Config.Name }}.New{{ .Config.Name | Title}}Server() - {{ .Config.Name }}pb.Register{{ .Config.Name | Title}}Server(s, {{ .Config.Name }}Server) + {{ .Config.Name | CleanGoIdentifier }}Server := {{ .Config.Name | CleanGoIdentifier }}.New{{ .Config.Name | Title | CleanGoIdentifier}}Server() + {{ .Config.Name | CleanGoIdentifier }}pb.Register{{ .Config.Name | Title | CleanGoIdentifier}}Server(s, {{ .Config.Name | CleanGoIdentifier }}Server) log.Printf("Starting grpc server on %v...", grpcAddr) diff --git a/templates/golang/server.tmpl b/templates/golang/server.tmpl index 05e74fae0..890888ad3 100644 --- a/templates/golang/server.tmpl +++ b/templates/golang/server.tmpl @@ -10,11 +10,11 @@ type {{ .Service.Name | Title }}Server struct { } -func New{{ .Service.Name | Title }}Server() *{{ .Service.Name | Title }}Server { - return &{{ .Service.Name | Title }}Server{} +func New{{ .Service.Name | Title | CleanGoIdentifier }}Server() *{{ .Service.Name | Title }}Server { + return &{{ .Service.Name | Title | CleanGoIdentifier }}Server{} } -func (s *{{ .Service.Name | Title }}Server) Check(ctx context.Context, req *health_api.HealthCheckRequest) (*health_api.HealthCheckResponse, error) { +func (s *{{ .Service.Name | Title | CleanGoIdentifier }}Server) Check(ctx context.Context, req *health_api.HealthCheckRequest) (*health_api.HealthCheckResponse, error) { resp := &health_api.HealthCheckResponse{ Status: health_api.HealthCheckResponse_SERVING, } diff --git a/templates/kubernetes/terraform/global/remote-state/main.tf b/templates/kubernetes/terraform/bootstrap/remote-state/main.tf similarity index 64% rename from templates/kubernetes/terraform/global/remote-state/main.tf rename to templates/kubernetes/terraform/bootstrap/remote-state/main.tf index 9fe6702a2..cbf53d65b 100644 --- a/templates/kubernetes/terraform/global/remote-state/main.tf +++ b/templates/kubernetes/terraform/bootstrap/remote-state/main.tf @@ -11,6 +11,16 @@ resource "aws_s3_bucket" "terraform_remote_state" { } } +resource "aws_s3_bucket_public_access_block" "terraform_remote_state" { + bucket = "${aws_s3_bucket.terraform_remote_state.id}" + + + block_public_acls = true + block_public_policy = true + ignore_public_acls = true + restrict_public_buckets = true +} + resource "aws_dynamodb_table" "terraform_state_locks" { name = "{{ .Config.Name }}-terraform-state-locks" read_capacity = 2 diff --git a/templates/kubernetes/terraform/environments/development/kubernetes/main.tf b/templates/kubernetes/terraform/environments/development/kubernetes/main.tf new file mode 100644 index 000000000..9e3d5e20d --- /dev/null +++ b/templates/kubernetes/terraform/environments/development/kubernetes/main.tf @@ -0,0 +1,32 @@ +terraform { + backend "s3" { + bucket = "project-{{ .Config.Name }}-terraform-state" + key = "infrastructure/terraform/environments/development/main" + encrypt = true + region = "{{ .Config.Infrastructure.AWS.Region }}" + dynamodb_table = "{{ .Config.Name }}-terraform-state-locks" + } +} + +# Instantiate the development environment +module "development" { + source = "../../../modules/environment" + environment = "development" + + # Project configuration + project = "{{ .Config.Infrastructure.AWS.EKS.ClusterName }}" + region = "{{ .Config.Infrastructure.AWS.Region }}" + allowed_account_ids = ["{{ .Config.Infrastructure.AWS.AccountId }}"] + + # ECR configuration + ecr_repositories = ["{{ .Config.Infrastructure.AWS.EKS.ClusterName }}"] + + # EKS configuration + eks_worker_instance_type = "t2.small" + eks_worker_asg_max_size = 2 + + # EKS-Optimized AMI for your region: https://docs.aws.amazon.com/eks/latest/userguide/eks-optimized-ami.html + # https://us-east-1.console.aws.amazon.com/systems-manager/parameters/%252Faws%252Fservice%252Feks%252Foptimized-ami%252F1.14%252Famazon-linux-2%252Frecommended%252Fimage_id/description?region=us-east-1 + eks_worker_ami = "{{ .Config.Infrastructure.AWS.EKS.WorkerAMI }}" + +} diff --git a/templates/kubernetes/terraform/environments/development/main.tf b/templates/kubernetes/terraform/environments/development/main.tf index 3406fc638..3083063de 100644 --- a/templates/kubernetes/terraform/environments/development/main.tf +++ b/templates/kubernetes/terraform/environments/development/main.tf @@ -4,29 +4,34 @@ terraform { key = "infrastructure/terraform/environments/development/main" encrypt = true region = "{{ .Config.Infrastructure.AWS.Region }}" - dynamodb_table = "terraform-state-locks" + dynamodb_table = "{{ .Config.Name }}-terraform-state-locks" } } -# Instantiate the development environment -module "development" { - source = "../../modules/environment" +# Provision kubernetes resources required to run services/applications +module "kubernetes" { + source = "../../modules/kubernetes" + environment = "development" + region = "{{ .Config.Infrastructure.AWS.Region }}" - # Project configuration - project = "{{ .Config.Infrastructure.AWS.EKS.ClusterName }}" - region = "{{ .Config.Infrastructure.AWS.Region }}" - allowed_account_ids = ["{{ .Config.Infrastructure.AWS.AccountId }}"] + # Authenticate with the EKS cluster via the cluster id + cluster_name = "{{ .Config.Infrastructure.AWS.EKS.ClusterName }}" - # ECR configuration - ecr_repositories = ["{{ .Config.Infrastructure.AWS.EKS.ClusterName }}"] + # Assume-role policy used by monitoring fluentd daemonset + assume_role_policy = data.aws_iam_policy_document.assumerole_root_policy.json +} - # EKS configuration - eks_worker_instance_type = "t2.small" - eks_worker_asg_max_size = 2 +# Data sources for EKS IAM +data "aws_caller_identity" "current" {} - # EKS-Optimized AMI for your region: https://docs.aws.amazon.com/eks/latest/userguide/eks-optimized-ami.html - # https://us-east-1.console.aws.amazon.com/systems-manager/parameters/%252Faws%252Fservice%252Feks%252Foptimized-ami%252F1.14%252Famazon-linux-2%252Frecommended%252Fimage_id/description?region=us-east-1 - eks_worker_ami = "ami-0392bafc801b7520f" +data "aws_iam_policy_document" "assumerole_root_policy" { + statement { + actions = ["sts:AssumeRole"] + principals { + type = "AWS" + identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"] + } + } } diff --git a/templates/kubernetes/terraform/environments/production/kubernetes/main.tf b/templates/kubernetes/terraform/environments/production/kubernetes/main.tf new file mode 100644 index 000000000..39e4e34a7 --- /dev/null +++ b/templates/kubernetes/terraform/environments/production/kubernetes/main.tf @@ -0,0 +1,37 @@ +terraform { + backend "s3" { + bucket = "project-{{ .Config.Name }}-terraform-state" + key = "infrastructure/terraform/environments/production/main" + encrypt = true + region = "{{ .Config.Infrastructure.AWS.Region }}" + dynamodb_table = "{{ .Config.Name }}-terraform-state-locks" + } +} + +# Provision kubernetes resources required to run services/applications +module "kubernetes" { + source = "../../../modules/kubernetes" + + environment = "production" + region = "{{ .Config.Infrastructure.AWS.Region }}" + + # Authenticate with the EKS cluster via the cluster id + cluster_name = "{{ .Config.Infrastructure.AWS.EKS.ClusterName }}" + + # Assume-role policy used by monitoring fluentd daemonset + assume_role_policy = data.aws_iam_policy_document.assumerole_root_policy.json +} + +# Data sources for EKS IAM +data "aws_caller_identity" "current" {} + +data "aws_iam_policy_document" "assumerole_root_policy" { + statement { + actions = ["sts:AssumeRole"] + + principals { + type = "AWS" + identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"] + } + } +} diff --git a/templates/kubernetes/terraform/environments/production/main.tf b/templates/kubernetes/terraform/environments/production/main.tf index 8da1a250e..ce83256fc 100644 --- a/templates/kubernetes/terraform/environments/production/main.tf +++ b/templates/kubernetes/terraform/environments/production/main.tf @@ -4,7 +4,7 @@ terraform { key = "infrastructure/terraform/environments/production/main" encrypt = true region = "{{ .Config.Infrastructure.AWS.Region }}" - dynamodb_table = "terraform-state-locks" + dynamodb_table = "{{ .Config.Name }}-terraform-state-locks" } } @@ -27,5 +27,5 @@ module "production" { # EKS-Optimized AMI for your region: https://docs.aws.amazon.com/eks/latest/userguide/eks-optimized-ami.html # https://us-east-1.console.aws.amazon.com/systems-manager/parameters/%252Faws%252Fservice%252Feks%252Foptimized-ami%252F1.14%252Famazon-linux-2%252Frecommended%252Fimage_id/description?region=us-east-1 - eks_worker_ami = "ami-0392bafc801b7520f" + eks_worker_ami = "{{ .Config.Infrastructure.AWS.EKS.WorkerAMI }}" } diff --git a/templates/kubernetes/terraform/environments/staging/kubernetes/main.tf b/templates/kubernetes/terraform/environments/staging/kubernetes/main.tf new file mode 100644 index 000000000..5a4d0d706 --- /dev/null +++ b/templates/kubernetes/terraform/environments/staging/kubernetes/main.tf @@ -0,0 +1,37 @@ +terraform { + backend "s3" { + bucket = "project-{{ .Config.Name }}-terraform-state" + key = "infrastructure/terraform/environments/staging/kubernetes" + encrypt = true + region = "{{ .Config.Infrastructure.AWS.Region }}" + dynamodb_table = "{{ .Config.Name }}-terraform-state-locks" + } +} + +# Provision kubernetes resources required to run services/applications +module "kubernetes" { + source = "../../../modules/kubernetes" + + environment = "staging" + region = "{{ .Config.Infrastructure.AWS.Region }}" + + # Authenticate with the EKS cluster via the cluster id + cluster_name = "{{ .Config.Infrastructure.AWS.EKS.ClusterName }}" + + # Assume-role policy used by monitoring fluentd daemonset + assume_role_policy = data.aws_iam_policy_document.assumerole_root_policy.json +} + +# Data sources for EKS IAM +data "aws_caller_identity" "current" {} + +data "aws_iam_policy_document" "assumerole_root_policy" { + statement { + actions = ["sts:AssumeRole"] + + principals { + type = "AWS" + identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"] + } + } +} diff --git a/templates/kubernetes/terraform/environments/staging/main.tf b/templates/kubernetes/terraform/environments/staging/main.tf index 5bca8af82..e23628e52 100644 --- a/templates/kubernetes/terraform/environments/staging/main.tf +++ b/templates/kubernetes/terraform/environments/staging/main.tf @@ -4,7 +4,7 @@ terraform { key = "infrastructure/terraform/environments/staging/main" encrypt = true region = "{{ .Config.Infrastructure.AWS.Region }}" - dynamodb_table = "terraform-state-locks" + dynamodb_table = "{{ .Config.Name }}-terraform-state-locks" } } @@ -24,8 +24,9 @@ module "staging" { # EKS configuration eks_worker_instance_type = "t2.small" eks_worker_asg_max_size = 2 + # EKS-Optimized AMI for your region: https://docs.aws.amazon.com/eks/latest/userguide/eks-optimized-ami.html # https://us-east-1.console.aws.amazon.com/systems-manager/parameters/%252Faws%252Fservice%252Feks%252Foptimized-ami%252F1.14%252Famazon-linux-2%252Frecommended%252Fimage_id/description?region=us-east-1 - eks_worker_ami = "ami-0392bafc801b7520f" + eks_worker_ami = "{{ .Config.Infrastructure.AWS.EKS.WorkerAMI }}" } diff --git a/templates/kubernetes/terraform/modules/environment/main.tf b/templates/kubernetes/terraform/modules/environment/main.tf index 04d7a4252..041069501 100644 --- a/templates/kubernetes/terraform/modules/environment/main.tf +++ b/templates/kubernetes/terraform/modules/environment/main.tf @@ -42,17 +42,3 @@ module "kube2iam" { eks_worker_iam_role_name = module.eks.worker_iam_role_name iam_account_id = data.aws_caller_identity.current.account_id } - -# Provision kubernetes resources required to run services/applications -module "kubernetes" { - source = "../../modules/kubernetes" - - environment = var.environment - region = var.region - - # Authenticate with the EKS cluster via the cluster id - cluster_name = module.eks.cluster_id - - # Assume-role policy used by monitoring fluentd daemonset - assume_role_policy = data.aws_iam_policy_document.assumerole_root_policy.json -} diff --git a/templates/kubernetes/terraform/modules/vpc/main.tf b/templates/kubernetes/terraform/modules/vpc/main.tf index 6df129e2b..1d050ef49 100644 --- a/templates/kubernetes/terraform/modules/vpc/main.tf +++ b/templates/kubernetes/terraform/modules/vpc/main.tf @@ -5,9 +5,9 @@ module "vpc" { cidr = "10.20.0.0/16" azs = ["${var.region}a", "${var.region}b", "${var.region}c"] # Most regions have 3+ azs - private_subnets = ["10.20.40.0/24", "10.20.42.0/24", "10.20.44.0/24"] + private_subnets = ["10.20.10.0/22", "10.20.14.0/22", "10.20.18.0/22"] public_subnets = ["10.20.41.0/24", "10.20.43.0/24", "10.20.45.0/24"] - database_subnets = ["10.20.50.0/24", "10.20.52.0/24", "10.20.54.0/24"] + database_subnets = ["10.20.60.0/24", "10.20.62.0/24", "10.20.64.0/24"] # Allow kubernetes ALB ingress controller to auto-detect private_subnet_tags = {