From 64655458467475c27c5d52a83b10de08d6115728 Mon Sep 17 00:00:00 2001 From: Andriy Knysh Date: Mon, 30 Sep 2019 23:43:38 -0400 Subject: [PATCH] Convert to TF 0.12. Add tests. Add Codefresh test pipeline (#22) * Convert to TF 0.12 * Convert to TF 0.12 * Convert to TF 0.12 * Convert to TF 0.12 * Convert to TF 0.12 * Convert to TF 0.12 * Convert to TF 0.12 * Convert to TF 0.12 * Convert to TF 0.12 * Convert to TF 0.12 * Convert to TF 0.12 * Convert to TF 0.12 * Convert to TF 0.12 * Convert to TF 0.12 * Convert to TF 0.12 * Convert to TF 0.12 * Convert to TF 0.12 * Convert to TF 0.12 * Convert to TF 0.12 * Convert to TF 0.12 * Convert to TF 0.12 * Convert to TF 0.12 * Convert to TF 0.12 * Convert to TF 0.12 * Add test for worker nodes joining the cluster * Add test for worker nodes joining the cluster * Add test for worker nodes joining the cluster * Update test.yaml * Update test.yml * Update test.yml * Update tests * Update ConfigMap * Update ConfigMap * Update README * Update template * Update tests --- .gitignore | 3 - .travis.yml | 16 -- README.md | 222 +++++++++++--------- README.yaml | 180 ++++++++-------- auth.tf | 91 ++++++++ codefresh/test.yml | 77 +++++++ configmap-auth.yaml.tpl | 19 ++ docs/terraform.md | 42 ++-- examples/complete/fixtures.us-east-2.tfvars | 31 +++ examples/complete/kubectl.tf | 41 ---- examples/complete/main.tf | 152 ++++++-------- examples/complete/outputs.tf | 74 ++++--- examples/complete/variables.tf | 152 ++++++-------- examples/complete/versions.tf | 10 + kubeconfig.tpl | 28 --- main.tf | 105 ++++----- outputs.tf | 29 ++- test/.gitignore | 1 + test/Makefile | 43 ++++ test/Makefile.alpine | 5 + test/src/.gitignore | 2 + test/src/Gopkg.lock | 92 ++++++++ test/src/Gopkg.toml | 7 + test/src/Makefile | 50 +++++ test/src/examples_complete_test.go | 122 +++++++++++ variables.tf | 108 +++++++--- versions.tf | 10 + 27 files changed, 1114 insertions(+), 598 deletions(-) delete mode 100644 .travis.yml create mode 100644 auth.tf create mode 100644 codefresh/test.yml create mode 100644 configmap-auth.yaml.tpl create mode 100644 examples/complete/fixtures.us-east-2.tfvars delete mode 100644 examples/complete/kubectl.tf create mode 100644 examples/complete/versions.tf delete mode 100644 kubeconfig.tpl create mode 100644 test/.gitignore create mode 100644 test/Makefile create mode 100644 test/Makefile.alpine create mode 100644 test/src/.gitignore create mode 100644 test/src/Gopkg.lock create mode 100644 test/src/Gopkg.toml create mode 100644 test/src/Makefile create mode 100644 test/src/examples_complete_test.go create mode 100644 versions.tf diff --git a/.gitignore b/.gitignore index 10db0013..fd716565 100644 --- a/.gitignore +++ b/.gitignore @@ -5,9 +5,6 @@ *.tfstate *.tfstate.* -# .tfvars files -*.tfvars - **/.idea **/*.iml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 241026ee..00000000 --- a/.travis.yml +++ /dev/null @@ -1,16 +0,0 @@ -addons: - apt: - packages: - - git - - make - - curl - -install: - - make init - -script: - - make terraform/install - - make terraform/get-plugins - - make terraform/get-modules - - make terraform/lint - - make terraform/validate diff --git a/README.md b/README.md index 83bb092e..22f4d71e 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Cloud Posse][logo]](https://cpco.io/homepage) -# terraform-aws-eks-cluster [![Build Status](https://travis-ci.org/cloudposse/terraform-aws-eks-cluster.svg?branch=master)](https://travis-ci.org/cloudposse/terraform-aws-eks-cluster) [![Latest Release](https://img.shields.io/github/release/cloudposse/terraform-aws-eks-cluster.svg)](https://github.com/cloudposse/terraform-aws-eks-cluster/releases/latest) [![Slack Community](https://slack.cloudposse.com/badge.svg)](https://slack.cloudposse.com) +# terraform-aws-eks-cluster [![Codefresh Build Status](https://g.codefresh.io/api/badges/pipeline/cloudposse/terraform-modules%2Fterraform-aws-eks-cluster?type=cf-1)](https://g.codefresh.io/public/accounts/cloudposse/pipelines/5d8cd583941e46a098d3992d) [![Latest Release](https://img.shields.io/github/release/cloudposse/terraform-aws-eks-cluster.svg)](https://github.com/cloudposse/terraform-aws-eks-cluster/releases/latest) [![Slack Community](https://slack.cloudposse.com/badge.svg)](https://slack.cloudposse.com) Terraform module to provision an [EKS](https://aws.amazon.com/eks/) cluster on AWS. @@ -63,84 +63,96 @@ Module usage examples: - [terraform-root-modules/eks-backing-services-peering](https://github.com/cloudposse/terraform-root-modules/tree/master/aws/eks-backing-services-peering) - example of VPC peering between the EKS VPC and backing services VPC ```hcl -provider "aws" { - region = "us-west-1" -} - -variable "tags" { - type = "map" - default = {} - description = "Additional tags (e.g. map('BusinessUnit','XYZ')" -} - -locals { - # The usage of the specific kubernetes.io/cluster/* resource tags below are required - # for EKS and Kubernetes to discover and manage networking resources - # https://www.terraform.io/docs/providers/aws/guides/eks-getting-started.html#base-vpc-networking - tags = "${merge(var.tags, map("kubernetes.io/cluster/eg-testing-cluster", "shared"))}" -} - -module "vpc" { - source = "git::https://github.com/cloudposse/terraform-aws-vpc.git?ref=master" - namespace = "eg" - stage = "testing" - name = "cluster" - tags = "${local.tags}" - cidr_block = "10.0.0.0/16" -} - -module "subnets" { - source = "git::https://github.com/cloudposse/terraform-aws-dynamic-subnets.git?ref=master" - availability_zones = ["us-west-1a", "us-west-1b", "us-west-1c", "us-west-1d"] - namespace = "eg" - stage = "testing" - name = "cluster" - tags = "${local.tags}" - region = "us-west-1" - vpc_id = "${module.vpc.vpc_id}" - igw_id = "${module.vpc.igw_id}" - cidr_block = "${module.vpc.vpc_cidr_block}" - nat_gateway_enabled = "true" -} - -module "eks_cluster" { - source = "git::https://github.com/cloudposse/terraform-aws-eks-cluster.git?ref=master" - namespace = "eg" - stage = "testing" - name = "cluster" - tags = "${var.tags}" - vpc_id = "${module.vpc.vpc_id}" - subnet_ids = ["${module.subnets.public_subnet_ids}"] - - # `workers_security_group_count` is needed to prevent `count can't be computed` errors - workers_security_group_ids = ["${module.eks_workers.security_group_id}"] - workers_security_group_count = 1 -} - -module "eks_workers" { - source = "git::https://github.com/cloudposse/terraform-aws-eks-workers.git?ref=master" - namespace = "eg" - stage = "testing" - name = "cluster" - tags = "${var.tags}" - instance_type = "t2.medium" - vpc_id = "${module.vpc.vpc_id}" - subnet_ids = ["${module.subnets.public_subnet_ids}"] - health_check_type = "EC2" - min_size = 1 - max_size = 3 - wait_for_capacity_timeout = "10m" - associate_public_ip_address = true - cluster_name = "eg-testing-cluster" - cluster_endpoint = "${module.eks_cluster.eks_cluster_endpoint}" - cluster_certificate_authority_data = "${module.eks_cluster.eks_cluster_certificate_authority_data}" - cluster_security_group_id = "${module.eks_cluster.security_group_id}" - - # Auto-scaling policies and CloudWatch metric alarms - autoscaling_policies_enabled = "true" - cpu_utilization_high_threshold_percent = "80" - cpu_utilization_low_threshold_percent = "20" -} + provider "aws" { + region = var.region + } + + module "label" { + source = "git::https://github.com/cloudposse/terraform-null-label.git?ref=master" + namespace = var.namespace + name = var.name + stage = var.stage + delimiter = var.delimiter + attributes = compact(concat(var.attributes, list("cluster"))) + tags = var.tags + } + + locals { + # The usage of the specific kubernetes.io/cluster/* resource tags below are required + # for EKS and Kubernetes to discover and manage networking resources + # https://www.terraform.io/docs/providers/aws/guides/eks-getting-started.html#base-vpc-networking + tags = merge(var.tags, map("kubernetes.io/cluster/${module.label.id}", "shared")) + } + + module "vpc" { + source = "git::https://github.com/cloudposse/terraform-aws-vpc.git?ref=master" + namespace = var.namespace + stage = var.stage + name = var.name + attributes = var.attributes + cidr_block = "172.16.0.0/16" + tags = local.tags + } + + module "subnets" { + source = "git::https://github.com/cloudposse/terraform-aws-dynamic-subnets.git?ref=master" + availability_zones = var.availability_zones + namespace = var.namespace + stage = var.stage + name = var.name + attributes = var.attributes + vpc_id = module.vpc.vpc_id + igw_id = module.vpc.igw_id + cidr_block = module.vpc.vpc_cidr_block + nat_gateway_enabled = false + nat_instance_enabled = false + tags = local.tags + } + + module "eks_workers" { + source = "git::https://github.com/cloudposse/terraform-aws-eks-workers.git?ref=master" + namespace = var.namespace + stage = var.stage + name = var.name + attributes = var.attributes + tags = var.tags + instance_type = var.instance_type + vpc_id = module.vpc.vpc_id + subnet_ids = module.subnets.public_subnet_ids + health_check_type = var.health_check_type + min_size = var.min_size + max_size = var.max_size + wait_for_capacity_timeout = var.wait_for_capacity_timeout + cluster_name = module.label.id + cluster_endpoint = module.eks_cluster.eks_cluster_endpoint + cluster_certificate_authority_data = module.eks_cluster.eks_cluster_certificate_authority_data + cluster_security_group_id = module.eks_cluster.security_group_id + + # Auto-scaling policies and CloudWatch metric alarms + autoscaling_policies_enabled = var.autoscaling_policies_enabled + cpu_utilization_high_threshold_percent = var.cpu_utilization_high_threshold_percent + cpu_utilization_low_threshold_percent = var.cpu_utilization_low_threshold_percent + } + + module "eks_cluster" { + source = "git::https://github.com/cloudposse/terraform-aws-eks-cluster.git?ref=master" + namespace = var.namespace + stage = var.stage + name = var.name + attributes = var.attributes + tags = var.tags + vpc_id = module.vpc.vpc_id + subnet_ids = module.subnets.public_subnet_ids + + kubernetes_version = var.kubernetes_version + + # `workers_security_group_count` is needed to prevent `count can't be computed` errors + workers_security_group_ids = [module.eks_workers.security_group_id] + workers_security_group_count = 1 + + workers_role_arns = [module.eks_workers.workers_role_arn] + kubeconfig_path = var.kubeconfig_path + } ``` @@ -162,35 +174,41 @@ Available targets: | Name | Description | Type | Default | Required | |------|-------------|:----:|:-----:|:-----:| -| allowed_cidr_blocks | List of CIDR blocks to be allowed to connect to the EKS cluster | list | `` | no | -| allowed_security_groups | List of Security Group IDs to be allowed to connect to the EKS cluster | list | `` | no | -| attributes | Additional attributes (e.g. `1`) | list | `` | no | +| allowed_cidr_blocks | List of CIDR blocks to be allowed to connect to the EKS cluster | list(string) | `` | no | +| allowed_security_groups | List of Security Group IDs to be allowed to connect to the EKS cluster | list(string) | `` | no | +| apply_config_map_aws_auth | Whether to generate local files from `kubeconfig` and `config-map-aws-auth` templates and perform `kubectl apply` to apply the ConfigMap to allow worker nodes to join the EKS cluster | bool | `true` | no | +| associate_public_ip_address | Associate a public IP address with an instance in a VPC | bool | `true` | no | +| attributes | Additional attributes (e.g. `1`) | list(string) | `` | no | | delimiter | Delimiter to be used between `name`, `namespace`, `stage`, etc. | string | `-` | no | -| enabled | Whether to create the resources. Set to `false` to prevent the module from creating any resources | string | `true` | no | -| enabled_cluster_log_types | A list of the desired control plane logging to enable. For more information, see https://docs.aws.amazon.com/en_us/eks/latest/userguide/control-plane-logs.html. Possible values [`api`, `audit`, `authenticator`, `controllerManager`, `scheduler`] | list | `` | no | -| endpoint_private_access | Indicates whether or not the Amazon EKS private API server endpoint is enabled. Default to AWS EKS resource and it is false | string | `false` | no | -| endpoint_public_access | Indicates whether or not the Amazon EKS public API server endpoint is enabled. Default to AWS EKS resource and it is true | string | `true` | no | -| environment | Environment, e.g. 'testing', 'UAT' | string | `` | no | -| kubernetes_version | Desired Kubernetes master version. If you do not specify a value, the latest available version is used. | string | `` | no | -| name | Solution name, e.g. 'app' or 'cluster' | string | `app` | no | -| namespace | Namespace, which could be your organization name, e.g. 'eg' or 'cp' | string | - | yes | -| stage | Stage, e.g. 'prod', 'staging', 'dev', or 'test' | string | - | yes | -| subnet_ids | A list of subnet IDs to launch the cluster in | list | - | yes | -| tags | Additional tags (e.g. `map('BusinessUnit`,`XYZ`) | map | `` | no | +| enabled | Whether to create the resources. Set to `false` to prevent the module from creating any resources | bool | `true` | no | +| enabled_cluster_log_types | A list of the desired control plane logging to enable. For more information, see https://docs.aws.amazon.com/en_us/eks/latest/userguide/control-plane-logs.html. Possible values [`api`, `audit`, `authenticator`, `controllerManager`, `scheduler`] | list(string) | `` | no | +| endpoint_private_access | Indicates whether or not the Amazon EKS private API server endpoint is enabled. Default to AWS EKS resource and it is false | bool | `false` | no | +| endpoint_public_access | Indicates whether or not the Amazon EKS public API server endpoint is enabled. Default to AWS EKS resource and it is true | bool | `true` | no | +| kubeconfig_path | The path to `kubeconfig` file | string | `~/.kube/config` | no | +| kubernetes_version | Desired Kubernetes master version. If you do not specify a value, the latest available version is used | string | `1.14` | no | +| map_additional_aws_accounts | Additional AWS account numbers to add to `config-map-aws-auth` ConfigMap | list(string) | `` | no | +| map_additional_iam_roles | Additional IAM roles to add to `config-map-aws-auth` ConfigMap | object | `` | no | +| map_additional_iam_users | Additional IAM users to add to `config-map-aws-auth` ConfigMap | object | `` | no | +| name | Solution name, e.g. 'app' or 'cluster' | string | - | yes | +| namespace | Namespace, which could be your organization name, e.g. 'eg' or 'cp' | string | `` | no | +| region | AWS Region | string | - | yes | +| stage | Stage, e.g. 'prod', 'staging', 'dev', or 'test' | string | `` | no | +| subnet_ids | A list of subnet IDs to launch the cluster in | list(string) | - | yes | +| tags | Additional tags (e.g. `map('BusinessUnit`,`XYZ`) | map(string) | `` | no | | vpc_id | VPC ID for the EKS cluster | string | - | yes | -| workers_security_group_count | Count of the worker Security Groups. Needed to prevent Terraform error `count can't be computed` | string | - | yes | -| workers_security_group_ids | Security Group IDs of the worker nodes | list | - | yes | +| workers_role_arns | List of Role ARNs of the worker nodes | list(string) | - | yes | +| workers_security_group_count | Count of the worker Security Groups. Needed to prevent Terraform error `count can't be computed` | number | - | yes | +| workers_security_group_ids | Security Group IDs of the worker nodes | list(string) | - | yes | ## Outputs | Name | Description | |------|-------------| | eks_cluster_arn | The Amazon Resource Name (ARN) of the cluster | -| eks_cluster_certificate_authority_data | The base64 encoded certificate data required to communicate with the cluster | +| eks_cluster_certificate_authority_data | The Kubernetes cluster certificate authority data | | eks_cluster_endpoint | The endpoint for the Kubernetes API server | | eks_cluster_id | The name of the cluster | | eks_cluster_version | The Kubernetes server version of the cluster | -| kubeconfig | `kubeconfig` configuration to connect to the cluster using `kubectl`. https://www.terraform.io/docs/providers/aws/guides/eks-getting-started.html#configuring-kubectl-for-eks | | security_group_arn | ARN of the EKS cluster Security Group | | security_group_id | ID of the EKS cluster Security Group | | security_group_name | Name of the EKS cluster Security Group | @@ -213,7 +231,7 @@ Check out these related projects. - [terraform-aws-ec2-autoscale-group](https://github.com/cloudposse/terraform-aws-ec2-autoscale-group) - Terraform module to provision Auto Scaling Group and Launch Template on AWS - [terraform-aws-ecs-container-definition](https://github.com/cloudposse/terraform-aws-ecs-container-definition) - Terraform module to generate well-formed JSON documents (container definitions) that are passed to the aws_ecs_task_definition Terraform resource - [terraform-aws-ecs-alb-service-task](https://github.com/cloudposse/terraform-aws-ecs-alb-service-task) - Terraform module which implements an ECS service which exposes a web service via ALB -- [erraform-aws-ecs-web-app](https://github.com/cloudposse/terraform-aws-ecs-web-app) - Terraform module that implements a web app on ECS and supports autoscaling, CI/CD, monitoring, ALB integration, and much more +- [terraform-aws-ecs-web-app](https://github.com/cloudposse/terraform-aws-ecs-web-app) - Terraform module that implements a web app on ECS and supports autoscaling, CI/CD, monitoring, ALB integration, and much more - [terraform-aws-ecs-codepipeline](https://github.com/cloudposse/terraform-aws-ecs-codepipeline) - Terraform module for CI/CD with AWS Code Pipeline and Code Build for ECS - [terraform-aws-ecs-cloudwatch-autoscaling](https://github.com/cloudposse/terraform-aws-ecs-cloudwatch-autoscaling) - Terraform module to autoscale ECS Service based on CloudWatch metrics - [terraform-aws-ecs-cloudwatch-sns-alarms](https://github.com/cloudposse/terraform-aws-ecs-cloudwatch-sns-alarms) - Terraform module to create CloudWatch Alarms on ECS Service level metrics @@ -339,8 +357,8 @@ Check out [our other projects][github], [follow us on twitter][twitter], [apply ### Contributors -| [![Erik Osterman][osterman_avatar]][osterman_homepage]
[Erik Osterman][osterman_homepage] | [![Andriy Knysh][aknysh_avatar]][aknysh_homepage]
[Andriy Knysh][aknysh_homepage] | [![Igor Rodionov][goruha_avatar]][goruha_homepage]
[Igor Rodionov][goruha_homepage] | -|---|---|---| +| [![Erik Osterman][osterman_avatar]][osterman_homepage]
[Erik Osterman][osterman_homepage] | [![Andriy Knysh][aknysh_avatar]][aknysh_homepage]
[Andriy Knysh][aknysh_homepage] | [![Igor Rodionov][goruha_avatar]][goruha_homepage]
[Igor Rodionov][goruha_homepage] | [![Oscar][osulli_avatar]][osulli_homepage]
[Oscar][osulli_homepage] | +|---|---|---|---| [osterman_homepage]: https://github.com/osterman @@ -355,6 +373,10 @@ Check out [our other projects][github], [follow us on twitter][twitter], [apply [goruha_avatar]: http://s.gravatar.com/avatar/bc70834d32ed4517568a1feb0b9be7e2?s=144 + [osulli_homepage]: https://github.com/osulli/ + [osulli_avatar]: https://avatars1.githubusercontent.com/u/46930728?v=4&s=144 + + [![README Footer][readme_footer_img]][readme_footer_link] diff --git a/README.yaml b/README.yaml index ee4aa504..122b5822 100644 --- a/README.yaml +++ b/README.yaml @@ -18,9 +18,9 @@ github_repo: cloudposse/terraform-aws-eks-cluster # Badges to display badges: - - name: "Build Status" - image: "https://travis-ci.org/cloudposse/terraform-aws-eks-cluster.svg?branch=master" - url: "https://travis-ci.org/cloudposse/terraform-aws-eks-cluster" + - name: "Codefresh Build Status" + image: "https://g.codefresh.io/api/badges/pipeline/cloudposse/terraform-modules%2Fterraform-aws-eks-cluster?type=cf-1" + url: "https://g.codefresh.io/public/accounts/cloudposse/pipelines/5d8cd583941e46a098d3992d" - name: "Latest Release" image: "https://img.shields.io/github/release/cloudposse/terraform-aws-eks-cluster.svg" url: "https://github.com/cloudposse/terraform-aws-eks-cluster/releases/latest" @@ -41,7 +41,7 @@ related: - name: "terraform-aws-ecs-alb-service-task" description: "Terraform module which implements an ECS service which exposes a web service via ALB" url: "https://github.com/cloudposse/terraform-aws-ecs-alb-service-task" - - name: "erraform-aws-ecs-web-app" + - name: "terraform-aws-ecs-web-app" description: "Terraform module that implements a web app on ECS and supports autoscaling, CI/CD, monitoring, ALB integration, and much more" url: "https://github.com/cloudposse/terraform-aws-ecs-web-app" - name: "terraform-aws-ecs-codepipeline" @@ -82,84 +82,96 @@ usage: |- - [terraform-root-modules/eks-backing-services-peering](https://github.com/cloudposse/terraform-root-modules/tree/master/aws/eks-backing-services-peering) - example of VPC peering between the EKS VPC and backing services VPC ```hcl - provider "aws" { - region = "us-west-1" - } - - variable "tags" { - type = "map" - default = {} - description = "Additional tags (e.g. map('BusinessUnit','XYZ')" - } - - locals { - # The usage of the specific kubernetes.io/cluster/* resource tags below are required - # for EKS and Kubernetes to discover and manage networking resources - # https://www.terraform.io/docs/providers/aws/guides/eks-getting-started.html#base-vpc-networking - tags = "${merge(var.tags, map("kubernetes.io/cluster/eg-testing-cluster", "shared"))}" - } - - module "vpc" { - source = "git::https://github.com/cloudposse/terraform-aws-vpc.git?ref=master" - namespace = "eg" - stage = "testing" - name = "cluster" - tags = "${local.tags}" - cidr_block = "10.0.0.0/16" - } - - module "subnets" { - source = "git::https://github.com/cloudposse/terraform-aws-dynamic-subnets.git?ref=master" - availability_zones = ["us-west-1a", "us-west-1b", "us-west-1c", "us-west-1d"] - namespace = "eg" - stage = "testing" - name = "cluster" - tags = "${local.tags}" - region = "us-west-1" - vpc_id = "${module.vpc.vpc_id}" - igw_id = "${module.vpc.igw_id}" - cidr_block = "${module.vpc.vpc_cidr_block}" - nat_gateway_enabled = "true" - } - - module "eks_cluster" { - source = "git::https://github.com/cloudposse/terraform-aws-eks-cluster.git?ref=master" - namespace = "eg" - stage = "testing" - name = "cluster" - tags = "${var.tags}" - vpc_id = "${module.vpc.vpc_id}" - subnet_ids = ["${module.subnets.public_subnet_ids}"] - - # `workers_security_group_count` is needed to prevent `count can't be computed` errors - workers_security_group_ids = ["${module.eks_workers.security_group_id}"] - workers_security_group_count = 1 - } - - module "eks_workers" { - source = "git::https://github.com/cloudposse/terraform-aws-eks-workers.git?ref=master" - namespace = "eg" - stage = "testing" - name = "cluster" - tags = "${var.tags}" - instance_type = "t2.medium" - vpc_id = "${module.vpc.vpc_id}" - subnet_ids = ["${module.subnets.public_subnet_ids}"] - health_check_type = "EC2" - min_size = 1 - max_size = 3 - wait_for_capacity_timeout = "10m" - associate_public_ip_address = true - cluster_name = "eg-testing-cluster" - cluster_endpoint = "${module.eks_cluster.eks_cluster_endpoint}" - cluster_certificate_authority_data = "${module.eks_cluster.eks_cluster_certificate_authority_data}" - cluster_security_group_id = "${module.eks_cluster.security_group_id}" - - # Auto-scaling policies and CloudWatch metric alarms - autoscaling_policies_enabled = "true" - cpu_utilization_high_threshold_percent = "80" - cpu_utilization_low_threshold_percent = "20" - } + provider "aws" { + region = var.region + } + + module "label" { + source = "git::https://github.com/cloudposse/terraform-null-label.git?ref=master" + namespace = var.namespace + name = var.name + stage = var.stage + delimiter = var.delimiter + attributes = compact(concat(var.attributes, list("cluster"))) + tags = var.tags + } + + locals { + # The usage of the specific kubernetes.io/cluster/* resource tags below are required + # for EKS and Kubernetes to discover and manage networking resources + # https://www.terraform.io/docs/providers/aws/guides/eks-getting-started.html#base-vpc-networking + tags = merge(var.tags, map("kubernetes.io/cluster/${module.label.id}", "shared")) + } + + module "vpc" { + source = "git::https://github.com/cloudposse/terraform-aws-vpc.git?ref=master" + namespace = var.namespace + stage = var.stage + name = var.name + attributes = var.attributes + cidr_block = "172.16.0.0/16" + tags = local.tags + } + + module "subnets" { + source = "git::https://github.com/cloudposse/terraform-aws-dynamic-subnets.git?ref=master" + availability_zones = var.availability_zones + namespace = var.namespace + stage = var.stage + name = var.name + attributes = var.attributes + vpc_id = module.vpc.vpc_id + igw_id = module.vpc.igw_id + cidr_block = module.vpc.vpc_cidr_block + nat_gateway_enabled = false + nat_instance_enabled = false + tags = local.tags + } + + module "eks_workers" { + source = "git::https://github.com/cloudposse/terraform-aws-eks-workers.git?ref=master" + namespace = var.namespace + stage = var.stage + name = var.name + attributes = var.attributes + tags = var.tags + instance_type = var.instance_type + vpc_id = module.vpc.vpc_id + subnet_ids = module.subnets.public_subnet_ids + health_check_type = var.health_check_type + min_size = var.min_size + max_size = var.max_size + wait_for_capacity_timeout = var.wait_for_capacity_timeout + cluster_name = module.label.id + cluster_endpoint = module.eks_cluster.eks_cluster_endpoint + cluster_certificate_authority_data = module.eks_cluster.eks_cluster_certificate_authority_data + cluster_security_group_id = module.eks_cluster.security_group_id + + # Auto-scaling policies and CloudWatch metric alarms + autoscaling_policies_enabled = var.autoscaling_policies_enabled + cpu_utilization_high_threshold_percent = var.cpu_utilization_high_threshold_percent + cpu_utilization_low_threshold_percent = var.cpu_utilization_low_threshold_percent + } + + module "eks_cluster" { + source = "git::https://github.com/cloudposse/terraform-aws-eks-cluster.git?ref=master" + namespace = var.namespace + stage = var.stage + name = var.name + attributes = var.attributes + tags = var.tags + vpc_id = module.vpc.vpc_id + subnet_ids = module.subnets.public_subnet_ids + + kubernetes_version = var.kubernetes_version + + # `workers_security_group_count` is needed to prevent `count can't be computed` errors + workers_security_group_ids = [module.eks_workers.security_group_id] + workers_security_group_count = 1 + + workers_role_arns = [module.eks_workers.workers_role_arn] + kubeconfig_path = var.kubeconfig_path + } ``` include: @@ -180,3 +192,7 @@ contributors: homepage: "https://github.com/goruha/" avatar: "http://s.gravatar.com/avatar/bc70834d32ed4517568a1feb0b9be7e2?s=144" github: "goruha" + - name: "Oscar" + homepage: "https://github.com/osulli/" + avatar: "https://avatars1.githubusercontent.com/u/46930728?v=4&s=144" + github: "osulli" diff --git a/auth.tf b/auth.tf new file mode 100644 index 00000000..c93c2882 --- /dev/null +++ b/auth.tf @@ -0,0 +1,91 @@ +# The EKS service does not provide a cluster-level API parameter or resource to automatically configure the underlying Kubernetes cluster +# to allow worker nodes to join the cluster via AWS IAM role authentication. + +# NOTE: To automatically apply the Kubernetes configuration to the cluster (which allows the worker nodes to join the cluster), +# the requirements outlined here must be met: +# https://learn.hashicorp.com/terraform/aws/eks-intro#preparation +# https://learn.hashicorp.com/terraform/aws/eks-intro#configuring-kubectl-for-eks +# https://learn.hashicorp.com/terraform/aws/eks-intro#required-kubernetes-configuration-to-join-worker-nodes + +# Additional links +# https://learn.hashicorp.com/terraform/aws/eks-intro +# https://itnext.io/how-does-client-authentication-work-on-amazon-eks-c4f2b90d943b +# https://docs.aws.amazon.com/eks/latest/userguide/create-kubeconfig.html +# https://docs.aws.amazon.com/eks/latest/userguide/add-user-role.html +# https://docs.aws.amazon.com/cli/latest/reference/eks/update-kubeconfig.html +# https://docs.aws.amazon.com/en_pv/eks/latest/userguide/create-kubeconfig.html +# https://itnext.io/kubernetes-authorization-via-open-policy-agent-a9455d9d5ceb +# http://marcinkaszynski.com/2018/07/12/eks-auth.html +# https://cloud.google.com/kubernetes-engine/docs/concepts/configmap +# http://yaml-multiline.info +# https://github.com/terraform-providers/terraform-provider-kubernetes/issues/216 + + +locals { + certificate_authority_data_list = coalescelist(aws_eks_cluster.default.*.certificate_authority, [[{ data : "" }]]) + certificate_authority_data_list_internal = local.certificate_authority_data_list[0] + certificate_authority_data_map = local.certificate_authority_data_list_internal[0] + certificate_authority_data = local.certificate_authority_data_map["data"] + + configmap_auth_template_file = "${path.module}/configmap-auth.yaml.tpl" + configmap_auth_file = "${path.module}/configmap-auth.yaml" + + cluster_name = join("", aws_eks_cluster.default.*.id) + + # Add worker nodes role ARNs (could be from many worker groups) to the ConfigMap + map_worker_roles = [ + for role_arn in var.workers_role_arns : { + rolearn : role_arn + username : "system:node:{{EC2PrivateDNSName}}" + groups : [ + "system:bootstrappers", + "system:nodes" + ] + } + ] + + map_worker_roles_yaml = trimspace(yamlencode(local.map_worker_roles)) + map_additional_iam_roles_yaml = trimspace(yamlencode(var.map_additional_iam_roles)) + map_additional_iam_users_yaml = trimspace(yamlencode(var.map_additional_iam_users)) + map_additional_aws_accounts_yaml = trimspace(yamlencode(var.map_additional_aws_accounts)) +} + +data "template_file" "configmap_auth" { + count = var.enabled && var.apply_config_map_aws_auth ? 1 : 0 + template = file(local.configmap_auth_template_file) + + vars = { + map_worker_roles_yaml = local.map_worker_roles_yaml + map_additional_iam_roles_yaml = local.map_additional_iam_roles_yaml + map_additional_iam_users_yaml = local.map_additional_iam_users_yaml + map_additional_aws_accounts_yaml = local.map_additional_aws_accounts_yaml + } +} + +resource "local_file" "configmap_auth" { + count = var.enabled && var.apply_config_map_aws_auth ? 1 : 0 + content = join("", data.template_file.configmap_auth.*.rendered) + filename = local.configmap_auth_file +} + +resource "null_resource" "apply_configmap_auth" { + count = var.enabled && var.apply_config_map_aws_auth ? 1 : 0 + + triggers = { + cluster_updated = join("", aws_eks_cluster.default.*.id) + worker_roles_updated = local.map_worker_roles_yaml + additional_roles_updated = local.map_additional_iam_roles_yaml + additional_users_updated = local.map_additional_iam_users_yaml + additional_aws_accounts_updated = local.map_additional_aws_accounts_yaml + } + + depends_on = [aws_eks_cluster.default, local_file.configmap_auth] + + provisioner "local-exec" { + command = <` | no | -| allowed_security_groups | List of Security Group IDs to be allowed to connect to the EKS cluster | list | `` | no | -| attributes | Additional attributes (e.g. `1`) | list | `` | no | +| allowed_cidr_blocks | List of CIDR blocks to be allowed to connect to the EKS cluster | list(string) | `` | no | +| allowed_security_groups | List of Security Group IDs to be allowed to connect to the EKS cluster | list(string) | `` | no | +| apply_config_map_aws_auth | Whether to generate local files from `kubeconfig` and `config-map-aws-auth` templates and perform `kubectl apply` to apply the ConfigMap to allow worker nodes to join the EKS cluster | bool | `true` | no | +| associate_public_ip_address | Associate a public IP address with an instance in a VPC | bool | `true` | no | +| attributes | Additional attributes (e.g. `1`) | list(string) | `` | no | | delimiter | Delimiter to be used between `name`, `namespace`, `stage`, etc. | string | `-` | no | -| enabled | Whether to create the resources. Set to `false` to prevent the module from creating any resources | string | `true` | no | -| enabled_cluster_log_types | A list of the desired control plane logging to enable. For more information, see https://docs.aws.amazon.com/en_us/eks/latest/userguide/control-plane-logs.html. Possible values [`api`, `audit`, `authenticator`, `controllerManager`, `scheduler`] | list | `` | no | -| endpoint_private_access | Indicates whether or not the Amazon EKS private API server endpoint is enabled. Default to AWS EKS resource and it is false | string | `false` | no | -| endpoint_public_access | Indicates whether or not the Amazon EKS public API server endpoint is enabled. Default to AWS EKS resource and it is true | string | `true` | no | -| environment | Environment, e.g. 'testing', 'UAT' | string | `` | no | -| kubernetes_version | Desired Kubernetes master version. If you do not specify a value, the latest available version is used. | string | `` | no | -| name | Solution name, e.g. 'app' or 'cluster' | string | `app` | no | -| namespace | Namespace, which could be your organization name, e.g. 'eg' or 'cp' | string | - | yes | -| stage | Stage, e.g. 'prod', 'staging', 'dev', or 'test' | string | - | yes | -| subnet_ids | A list of subnet IDs to launch the cluster in | list | - | yes | -| tags | Additional tags (e.g. `map('BusinessUnit`,`XYZ`) | map | `` | no | +| enabled | Whether to create the resources. Set to `false` to prevent the module from creating any resources | bool | `true` | no | +| enabled_cluster_log_types | A list of the desired control plane logging to enable. For more information, see https://docs.aws.amazon.com/en_us/eks/latest/userguide/control-plane-logs.html. Possible values [`api`, `audit`, `authenticator`, `controllerManager`, `scheduler`] | list(string) | `` | no | +| endpoint_private_access | Indicates whether or not the Amazon EKS private API server endpoint is enabled. Default to AWS EKS resource and it is false | bool | `false` | no | +| endpoint_public_access | Indicates whether or not the Amazon EKS public API server endpoint is enabled. Default to AWS EKS resource and it is true | bool | `true` | no | +| kubeconfig_path | The path to `kubeconfig` file | string | `~/.kube/config` | no | +| kubernetes_version | Desired Kubernetes master version. If you do not specify a value, the latest available version is used | string | `1.14` | no | +| map_additional_aws_accounts | Additional AWS account numbers to add to `config-map-aws-auth` ConfigMap | list(string) | `` | no | +| map_additional_iam_roles | Additional IAM roles to add to `config-map-aws-auth` ConfigMap | object | `` | no | +| map_additional_iam_users | Additional IAM users to add to `config-map-aws-auth` ConfigMap | object | `` | no | +| name | Solution name, e.g. 'app' or 'cluster' | string | - | yes | +| namespace | Namespace, which could be your organization name, e.g. 'eg' or 'cp' | string | `` | no | +| region | AWS Region | string | - | yes | +| stage | Stage, e.g. 'prod', 'staging', 'dev', or 'test' | string | `` | no | +| subnet_ids | A list of subnet IDs to launch the cluster in | list(string) | - | yes | +| tags | Additional tags (e.g. `map('BusinessUnit`,`XYZ`) | map(string) | `` | no | | vpc_id | VPC ID for the EKS cluster | string | - | yes | -| workers_security_group_count | Count of the worker Security Groups. Needed to prevent Terraform error `count can't be computed` | string | - | yes | -| workers_security_group_ids | Security Group IDs of the worker nodes | list | - | yes | +| workers_role_arns | List of Role ARNs of the worker nodes | list(string) | - | yes | +| workers_security_group_count | Count of the worker Security Groups. Needed to prevent Terraform error `count can't be computed` | number | - | yes | +| workers_security_group_ids | Security Group IDs of the worker nodes | list(string) | - | yes | ## Outputs | Name | Description | |------|-------------| | eks_cluster_arn | The Amazon Resource Name (ARN) of the cluster | -| eks_cluster_certificate_authority_data | The base64 encoded certificate data required to communicate with the cluster | +| eks_cluster_certificate_authority_data | The Kubernetes cluster certificate authority data | | eks_cluster_endpoint | The endpoint for the Kubernetes API server | | eks_cluster_id | The name of the cluster | | eks_cluster_version | The Kubernetes server version of the cluster | -| kubeconfig | `kubeconfig` configuration to connect to the cluster using `kubectl`. https://www.terraform.io/docs/providers/aws/guides/eks-getting-started.html#configuring-kubectl-for-eks | | security_group_arn | ARN of the EKS cluster Security Group | | security_group_id | ID of the EKS cluster Security Group | | security_group_name | Name of the EKS cluster Security Group | diff --git a/examples/complete/fixtures.us-east-2.tfvars b/examples/complete/fixtures.us-east-2.tfvars new file mode 100644 index 00000000..1df7ec87 --- /dev/null +++ b/examples/complete/fixtures.us-east-2.tfvars @@ -0,0 +1,31 @@ +region = "us-east-2" + +availability_zones = ["us-east-2a", "us-east-2b"] + +namespace = "eg" + +stage = "test" + +name = "eks" + +instance_type = "t2.small" + +health_check_type = "EC2" + +wait_for_capacity_timeout = "10m" + +max_size = 3 + +min_size = 2 + +autoscaling_policies_enabled = true + +cpu_utilization_high_threshold_percent = 80 + +cpu_utilization_low_threshold_percent = 20 + +associate_public_ip_address = true + +kubernetes_version = "1.14" + +kubeconfig_path = "/.kube/config" diff --git a/examples/complete/kubectl.tf b/examples/complete/kubectl.tf deleted file mode 100644 index abedda66..00000000 --- a/examples/complete/kubectl.tf +++ /dev/null @@ -1,41 +0,0 @@ -########################################################################################################################################### -# -# NOTE: To automatically apply the Kubernetes configuration to the cluster (which allows the worker nodes to join the cluster), -# the requirements outlined here must be met: -# https://www.terraform.io/docs/providers/aws/guides/eks-getting-started.html#preparation -# https://www.terraform.io/docs/providers/aws/guides/eks-getting-started.html#configuring-kubectl-for-eks -# https://www.terraform.io/docs/providers/aws/guides/eks-getting-started.html#required-kubernetes-configuration-to-join-worker-nodes -# -# If you want to automatically apply the Kubernetes configuration, set `var.apply_config_map_aws_auth` to "true" -# -########################################################################################################################################### - -locals { - kubeconfig_filename = "${path.module}/kubeconfig${var.delimiter}${module.eks_cluster.eks_cluster_id}.yaml" - config_map_aws_auth_filename = "${path.module}/config-map-aws-auth${var.delimiter}${module.eks_cluster.eks_cluster_id}.yaml" -} - -resource "local_file" "kubeconfig" { - count = "${var.enabled == "true" && var.apply_config_map_aws_auth == "true" ? 1 : 0}" - content = "${module.eks_cluster.kubeconfig}" - filename = "${local.kubeconfig_filename}" -} - -resource "local_file" "config_map_aws_auth" { - count = "${var.enabled == "true" && var.apply_config_map_aws_auth == "true" ? 1 : 0}" - content = "${module.eks_workers.config_map_aws_auth}" - filename = "${local.config_map_aws_auth_filename}" -} - -resource "null_resource" "apply_config_map_aws_auth" { - count = "${var.enabled == "true" && var.apply_config_map_aws_auth == "true" ? 1 : 0}" - - provisioner "local-exec" { - command = "kubectl apply -f ${local.config_map_aws_auth_filename} --kubeconfig ${local.kubeconfig_filename}" - } - - triggers { - kubeconfig_rendered = "${module.eks_cluster.kubeconfig}" - config_map_aws_auth_rendered = "${module.eks_workers.config_map_aws_auth}" - } -} diff --git a/examples/complete/main.tf b/examples/complete/main.tf index e52b1791..fb4a0130 100644 --- a/examples/complete/main.tf +++ b/examples/complete/main.tf @@ -1,109 +1,91 @@ provider "aws" { - region = "${var.region}" + region = var.region } -# This `label` is needed to prevent `count can't be computed` errors module "label" { - source = "git::https://github.com/cloudposse/terraform-terraform-label.git?ref=master" - namespace = "${var.namespace}" - name = "${var.name}" - stage = "${var.stage}" - delimiter = "${var.delimiter}" - attributes = "${var.attributes}" - tags = "${var.tags}" - enabled = "${var.enabled}" -} - -# This `label` is needed to prevent `count can't be computed` errors -module "cluster_label" { - source = "git::https://github.com/cloudposse/terraform-terraform-label.git?ref=master" - namespace = "${var.namespace}" - stage = "${var.stage}" - name = "${var.name}" - delimiter = "${var.delimiter}" - attributes = ["${compact(concat(var.attributes, list("cluster")))}"] - tags = "${var.tags}" - enabled = "${var.enabled}" + source = "git::https://github.com/cloudposse/terraform-null-label.git?ref=tags/0.15.0" + namespace = var.namespace + name = var.name + stage = var.stage + delimiter = var.delimiter + attributes = compact(concat(var.attributes, list("cluster"))) + tags = var.tags } locals { # The usage of the specific kubernetes.io/cluster/* resource tags below are required # for EKS and Kubernetes to discover and manage networking resources # https://www.terraform.io/docs/providers/aws/guides/eks-getting-started.html#base-vpc-networking - tags = "${merge(var.tags, map("kubernetes.io/cluster/${module.label.id}", "shared"))}" + tags = merge(module.label.tags, map("kubernetes.io/cluster/${module.label.id}", "shared")) } module "vpc" { - source = "git::https://github.com/cloudposse/terraform-aws-vpc.git?ref=master" - namespace = "${var.namespace}" - stage = "${var.stage}" - name = "${var.name}" - attributes = "${var.attributes}" - tags = "${local.tags}" - cidr_block = "${var.vpc_cidr_block}" + source = "git::https://github.com/cloudposse/terraform-aws-vpc.git?ref=tags/0.8.0" + namespace = var.namespace + stage = var.stage + name = var.name + attributes = var.attributes + cidr_block = "172.16.0.0/16" + tags = local.tags } module "subnets" { - source = "git::https://github.com/cloudposse/terraform-aws-dynamic-subnets.git?ref=master" - availability_zones = ["${var.availability_zones}"] - namespace = "${var.namespace}" - stage = "${var.stage}" - name = "${var.name}" - attributes = "${var.attributes}" - tags = "${local.tags}" - region = "${var.region}" - vpc_id = "${module.vpc.vpc_id}" - igw_id = "${module.vpc.igw_id}" - cidr_block = "${module.vpc.vpc_cidr_block}" - nat_gateway_enabled = "true" + source = "git::https://github.com/cloudposse/terraform-aws-dynamic-subnets.git?ref=tags/0.16.0" + availability_zones = var.availability_zones + namespace = var.namespace + stage = var.stage + name = var.name + attributes = var.attributes + vpc_id = module.vpc.vpc_id + igw_id = module.vpc.igw_id + cidr_block = module.vpc.vpc_cidr_block + nat_gateway_enabled = false + nat_instance_enabled = false + tags = local.tags +} + +module "eks_workers" { + source = "git::https://github.com/cloudposse/terraform-aws-eks-workers.git?ref=tags/0.10.0" + namespace = var.namespace + stage = var.stage + name = var.name + attributes = var.attributes + tags = var.tags + instance_type = var.instance_type + vpc_id = module.vpc.vpc_id + subnet_ids = module.subnets.public_subnet_ids + associate_public_ip_address = var.associate_public_ip_address + health_check_type = var.health_check_type + min_size = var.min_size + max_size = var.max_size + wait_for_capacity_timeout = var.wait_for_capacity_timeout + cluster_name = module.label.id + cluster_endpoint = module.eks_cluster.eks_cluster_endpoint + cluster_certificate_authority_data = module.eks_cluster.eks_cluster_certificate_authority_data + cluster_security_group_id = module.eks_cluster.security_group_id + + # Auto-scaling policies and CloudWatch metric alarms + autoscaling_policies_enabled = var.autoscaling_policies_enabled + cpu_utilization_high_threshold_percent = var.cpu_utilization_high_threshold_percent + cpu_utilization_low_threshold_percent = var.cpu_utilization_low_threshold_percent } module "eks_cluster" { - source = "../../" - namespace = "${var.namespace}" - stage = "${var.stage}" - name = "${var.name}" - attributes = "${var.attributes}" - tags = "${var.tags}" - vpc_id = "${module.vpc.vpc_id}" - subnet_ids = ["${module.subnets.public_subnet_ids}"] - allowed_security_groups = ["${var.allowed_security_groups_cluster}"] + source = "../../" + namespace = var.namespace + stage = var.stage + name = var.name + attributes = var.attributes + tags = var.tags + region = var.region + vpc_id = module.vpc.vpc_id + subnet_ids = module.subnets.public_subnet_ids + kubernetes_version = var.kubernetes_version # `workers_security_group_count` is needed to prevent `count can't be computed` errors - workers_security_group_ids = ["${module.eks_workers.security_group_id}"] + workers_security_group_ids = [module.eks_workers.security_group_id] workers_security_group_count = 1 - allowed_cidr_blocks = ["${var.allowed_cidr_blocks_cluster}"] - enabled = "${var.enabled}" -} - -module "eks_workers" { - source = "git::https://github.com/cloudposse/terraform-aws-eks-workers.git?ref=master" - namespace = "${var.namespace}" - stage = "${var.stage}" - name = "${var.name}" - attributes = "${var.attributes}" - tags = "${var.tags}" - image_id = "${var.image_id}" - eks_worker_ami_name_filter = "${var.eks_worker_ami_name_filter}" - instance_type = "${var.instance_type}" - vpc_id = "${module.vpc.vpc_id}" - subnet_ids = ["${module.subnets.public_subnet_ids}"] - health_check_type = "${var.health_check_type}" - min_size = "${var.min_size}" - max_size = "${var.max_size}" - wait_for_capacity_timeout = "${var.wait_for_capacity_timeout}" - associate_public_ip_address = "${var.associate_public_ip_address}" - cluster_name = "${module.cluster_label.id}" - cluster_endpoint = "${module.eks_cluster.eks_cluster_endpoint}" - cluster_certificate_authority_data = "${module.eks_cluster.eks_cluster_certificate_authority_data}" - cluster_security_group_id = "${module.eks_cluster.security_group_id}" - allowed_security_groups = ["${var.allowed_security_groups_workers}"] - allowed_cidr_blocks = ["${var.allowed_cidr_blocks_workers}"] - enabled = "${var.enabled}" - - # Auto-scaling policies and CloudWatch metric alarms - autoscaling_policies_enabled = "${var.autoscaling_policies_enabled}" - cpu_utilization_high_threshold_percent = "${var.cpu_utilization_high_threshold_percent}" - cpu_utilization_low_threshold_percent = "${var.cpu_utilization_low_threshold_percent}" + workers_role_arns = [module.eks_workers.workers_role_arn] + kubeconfig_path = var.kubeconfig_path } diff --git a/examples/complete/outputs.tf b/examples/complete/outputs.tf index 03a59b59..94d58177 100644 --- a/examples/complete/outputs.tf +++ b/examples/complete/outputs.tf @@ -1,119 +1,129 @@ -output "kubeconfig" { - description = "`kubeconfig` configuration to connect to the cluster using `kubectl`. https://www.terraform.io/docs/providers/aws/guides/eks-getting-started.html#configuring-kubectl-for-eks" - value = "${module.eks_cluster.kubeconfig}" +output "public_subnet_cidrs" { + value = module.subnets.public_subnet_cidrs + description = "Public subnet CIDRs" } -output "config_map_aws_auth" { - description = "Kubernetes ConfigMap configuration to allow the worker nodes to join the EKS cluster. https://www.terraform.io/docs/providers/aws/guides/eks-getting-started.html#required-kubernetes-configuration-to-join-worker-nodes" - value = "${module.eks_workers.config_map_aws_auth}" +output "private_subnet_cidrs" { + value = module.subnets.private_subnet_cidrs + description = "Private subnet CIDRs" +} + +output "vpc_cidr" { + value = module.vpc.vpc_cidr_block + description = "VPC ID" } output "eks_cluster_security_group_id" { description = "ID of the EKS cluster Security Group" - value = "${module.eks_cluster.security_group_id}" + value = module.eks_cluster.security_group_id } output "eks_cluster_security_group_arn" { description = "ARN of the EKS cluster Security Group" - value = "${module.eks_cluster.security_group_arn}" + value = module.eks_cluster.security_group_arn } output "eks_cluster_security_group_name" { description = "Name of the EKS cluster Security Group" - value = "${module.eks_cluster.security_group_name}" + value = module.eks_cluster.security_group_name } output "eks_cluster_id" { description = "The name of the cluster" - value = "${module.eks_cluster.eks_cluster_id}" + value = module.eks_cluster.eks_cluster_id } output "eks_cluster_arn" { description = "The Amazon Resource Name (ARN) of the cluster" - value = "${module.eks_cluster.eks_cluster_arn}" -} - -output "eks_cluster_certificate_authority_data" { - description = "The base64 encoded certificate data required to communicate with the cluster" - value = "${module.eks_cluster.eks_cluster_certificate_authority_data}" + value = module.eks_cluster.eks_cluster_arn } output "eks_cluster_endpoint" { description = "The endpoint for the Kubernetes API server" - value = "${module.eks_cluster.eks_cluster_endpoint}" + value = module.eks_cluster.eks_cluster_endpoint } output "eks_cluster_version" { description = "The Kubernetes server version of the cluster" - value = "${module.eks_cluster.eks_cluster_version}" + value = module.eks_cluster.eks_cluster_version } output "workers_launch_template_id" { description = "ID of the launch template" - value = "${module.eks_workers.launch_template_id}" + value = module.eks_workers.launch_template_id } output "workers_launch_template_arn" { description = "ARN of the launch template" - value = "${module.eks_workers.launch_template_arn}" + value = module.eks_workers.launch_template_arn } output "workers_autoscaling_group_id" { description = "The AutoScaling Group ID" - value = "${module.eks_workers.autoscaling_group_id}" + value = module.eks_workers.autoscaling_group_id } output "workers_autoscaling_group_name" { description = "The AutoScaling Group name" - value = "${module.eks_workers.autoscaling_group_name}" + value = module.eks_workers.autoscaling_group_name } output "workers_autoscaling_group_arn" { description = "ARN of the AutoScaling Group" - value = "${module.eks_workers.autoscaling_group_arn}" + value = module.eks_workers.autoscaling_group_arn } output "workers_autoscaling_group_min_size" { description = "The minimum size of the AutoScaling Group" - value = "${module.eks_workers.autoscaling_group_min_size}" + value = module.eks_workers.autoscaling_group_min_size } output "workers_autoscaling_group_max_size" { description = "The maximum size of the AutoScaling Group" - value = "${module.eks_workers.autoscaling_group_max_size}" + value = module.eks_workers.autoscaling_group_max_size } output "workers_autoscaling_group_desired_capacity" { description = "The number of Amazon EC2 instances that should be running in the group" - value = "${module.eks_workers.autoscaling_group_desired_capacity}" + value = module.eks_workers.autoscaling_group_desired_capacity } output "workers_autoscaling_group_default_cooldown" { description = "Time between a scaling activity and the succeeding scaling activity" - value = "${module.eks_workers.autoscaling_group_default_cooldown}" + value = module.eks_workers.autoscaling_group_default_cooldown } output "workers_autoscaling_group_health_check_grace_period" { description = "Time after instance comes into service before checking health" - value = "${module.eks_workers.autoscaling_group_health_check_grace_period}" + value = module.eks_workers.autoscaling_group_health_check_grace_period } output "workers_autoscaling_group_health_check_type" { description = "`EC2` or `ELB`. Controls how health checking is done" - value = "${module.eks_workers.autoscaling_group_health_check_type}" + value = module.eks_workers.autoscaling_group_health_check_type } output "workers_security_group_id" { description = "ID of the worker nodes Security Group" - value = "${module.eks_workers.security_group_id}" + value = module.eks_workers.security_group_id } output "workers_security_group_arn" { description = "ARN of the worker nodes Security Group" - value = "${module.eks_workers.security_group_arn}" + value = module.eks_workers.security_group_arn } output "workers_security_group_name" { description = "Name of the worker nodes Security Group" - value = "${module.eks_workers.security_group_name}" + value = module.eks_workers.security_group_name +} + +output "workers_role_arn" { + description = "ARN of the worker nodes IAM role" + value = module.eks_workers.workers_role_arn +} + +output "workers_role_name" { + description = "Name of the worker nodes IAM role" + value = module.eks_workers.workers_role_name } diff --git a/examples/complete/variables.tf b/examples/complete/variables.tf index ece9ff32..25eea0ed 100644 --- a/examples/complete/variables.tf +++ b/examples/complete/variables.tf @@ -1,152 +1,128 @@ +variable "region" { + type = string + description = "AWS Region" +} + +variable "availability_zones" { + type = list(string) + description = "List of availability zones" +} + variable "namespace" { - type = "string" - default = "eg" + type = string description = "Namespace, which could be your organization name, e.g. 'eg' or 'cp'" } variable "stage" { - type = "string" - default = "testing" + type = string description = "Stage, e.g. 'prod', 'staging', 'dev' or 'testing'" } variable "name" { - type = "string" - default = "eks" + type = string description = "Solution name, e.g. 'app' or 'cluster'" } variable "delimiter" { - type = "string" + type = string default = "-" description = "Delimiter to be used between `name`, `namespace`, `stage`, etc." } variable "attributes" { - type = "list" + type = list(string) default = [] description = "Additional attributes (e.g. `1`)" } variable "tags" { - type = "map" + type = map(string) default = {} description = "Additional tags (e.g. `map('BusinessUnit`,`XYZ`)" } -variable "enabled" { - type = "string" - description = "Whether to create the resources. Set to `false` to prevent the module from creating any resources" - default = "true" -} - -variable "allowed_security_groups_cluster" { - type = "list" - default = [] - description = "List of Security Group IDs to be allowed to connect to the EKS cluster" -} - -variable "allowed_security_groups_workers" { - type = "list" - default = [] - description = "List of Security Group IDs to be allowed to connect to the worker nodes" -} - -variable "allowed_cidr_blocks_cluster" { - type = "list" - default = [] - description = "List of CIDR blocks to be allowed to connect to the EKS cluster" -} - -variable "allowed_cidr_blocks_workers" { - type = "list" - default = [] - description = "List of CIDR blocks to be allowed to connect to the worker nodes" -} - -variable "region" { - type = "string" - default = "us-east-1" - description = "AWS Region" -} - -variable "vpc_cidr_block" { - type = "string" - default = "172.30.0.0/16" - description = "VPC CIDR block. See https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Subnets.html for more details" +variable "instance_type" { + type = string + description = "Instance type to launch" } -variable "image_id" { - type = "string" +variable "kubernetes_version" { + type = string default = "" - description = "EC2 image ID to launch. If not provided, the module will lookup the most recent EKS AMI. See https://docs.aws.amazon.com/eks/latest/userguide/eks-optimized-ami.html for more details on EKS-optimized images" -} - -variable "eks_worker_ami_name_filter" { - type = "string" - description = "AMI name filter to lookup the most recent EKS AMI if `image_id` is not provided" - default = "amazon-eks-node-v*" -} - -variable "instance_type" { - type = "string" - default = "t2.medium" - description = "Instance type to launch" + description = "Desired Kubernetes master version. If you do not specify a value, the latest available version is used" } variable "health_check_type" { type = "string" description = "Controls how health checking is done. Valid values are `EC2` or `ELB`" - default = "EC2" +} + +variable "associate_public_ip_address" { + type = bool + description = "Associate a public IP address with an instance in a VPC" } variable "max_size" { - default = 3 + type = number description = "The maximum size of the AutoScaling Group" } variable "min_size" { - default = 2 + type = number description = "The minimum size of the AutoScaling Group" } variable "wait_for_capacity_timeout" { - type = "string" + type = string description = "A maximum duration that Terraform should wait for ASG instances to be healthy before timing out. Setting this to '0' causes Terraform to skip all Capacity Waiting behavior" - default = "10m" -} - -variable "associate_public_ip_address" { - description = "Associate a public IP address with the worker nodes in the VPC" - default = true } variable "autoscaling_policies_enabled" { - type = "string" - default = "true" + type = bool description = "Whether to create `aws_autoscaling_policy` and `aws_cloudwatch_metric_alarm` resources to control Auto Scaling" } variable "cpu_utilization_high_threshold_percent" { - type = "string" - default = "80" + type = number description = "Worker nodes AutoScaling Group CPU utilization high threshold percent" } variable "cpu_utilization_low_threshold_percent" { - type = "string" - default = "20" + type = number description = "Worker nodes AutoScaling Group CPU utilization low threshold percent" } -variable "availability_zones" { - type = "list" - default = ["us-east-1a", "us-east-1b", "us-east-1c", "us-east-1d"] - description = "Availability Zones for the cluster" +variable "map_additional_aws_accounts" { + description = "Additional AWS account numbers to add to `config-map-aws-auth` ConfigMap" + type = list(string) + default = [] } -variable "apply_config_map_aws_auth" { - type = "string" - default = "false" - description = "Whether to generate local files from `kubeconfig` and `config_map_aws_auth` and perform `kubectl apply` to apply the ConfigMap to allow the worker nodes to join the EKS cluster" +variable "map_additional_iam_roles" { + description = "Additional IAM roles to add to `config-map-aws-auth` ConfigMap" + + type = list(object({ + rolearn = string + username = string + groups = list(string) + })) + + default = [] +} + +variable "map_additional_iam_users" { + description = "Additional IAM users to add to `config-map-aws-auth` ConfigMap" + + type = list(object({ + userarn = string + username = string + groups = list(string) + })) + + default = [] +} + +variable "kubeconfig_path" { + type = string + description = "The path to `kubeconfig` file" } diff --git a/examples/complete/versions.tf b/examples/complete/versions.tf new file mode 100644 index 00000000..9840ed75 --- /dev/null +++ b/examples/complete/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = "~> 0.12.0" + + required_providers { + aws = "~> 2.0" + template = "~> 2.0" + null = "~> 2.0" + local = "~> 1.3" + } +} diff --git a/kubeconfig.tpl b/kubeconfig.tpl deleted file mode 100644 index fe804e2e..00000000 --- a/kubeconfig.tpl +++ /dev/null @@ -1,28 +0,0 @@ -apiVersion: v1 -kind: Config -preferences: {} - -clusters: -- cluster: - server: ${server} - certificate-authority-data: ${certificate_authority_data} - name: ${cluster_name} - -contexts: -- context: - cluster: ${cluster_name} - user: ${cluster_name} - name: ${cluster_name} - -current-context: ${cluster_name} - -users: -- name: ${cluster_name} - user: - exec: - apiVersion: client.authentication.k8s.io/v1alpha1 - command: aws-iam-authenticator - args: - - "token" - - "-i" - - "${cluster_name}" diff --git a/main.tf b/main.tf index 1c782c4f..801d3011 100644 --- a/main.tf +++ b/main.tf @@ -1,22 +1,22 @@ module "label" { - source = "git::https://github.com/cloudposse/terraform-terraform-label.git?ref=tags/0.2.1" - namespace = "${var.namespace}" - stage = "${var.stage}" - name = "${var.name}" - delimiter = "${var.delimiter}" - attributes = ["${compact(concat(var.attributes, list("cluster")))}"] - tags = "${var.tags}" - enabled = "${var.enabled}" + source = "git::https://github.com/cloudposse/terraform-null-label.git?ref=tags/0.15.0" + namespace = var.namespace + stage = var.stage + name = var.name + delimiter = var.delimiter + attributes = compact(concat(var.attributes, ["cluster"])) + tags = var.tags + enabled = var.enabled } data "aws_iam_policy_document" "assume_role" { - count = "${var.enabled == "true" ? 1 : 0}" + count = var.enabled ? 1 : 0 statement { effect = "Allow" actions = ["sts:AssumeRole"] - principals = { + principals { type = "Service" identifiers = ["eks.amazonaws.com"] } @@ -24,109 +24,92 @@ data "aws_iam_policy_document" "assume_role" { } resource "aws_iam_role" "default" { - count = "${var.enabled == "true" ? 1 : 0}" - name = "${module.label.id}" - assume_role_policy = "${join("", data.aws_iam_policy_document.assume_role.*.json)}" + count = var.enabled ? 1 : 0 + name = module.label.id + assume_role_policy = join("", data.aws_iam_policy_document.assume_role.*.json) + tags = module.label.tags } resource "aws_iam_role_policy_attachment" "amazon_eks_cluster_policy" { - count = "${var.enabled == "true" ? 1 : 0}" + count = var.enabled ? 1 : 0 policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy" - role = "${aws_iam_role.default.name}" + role = join("", aws_iam_role.default.*.name) } resource "aws_iam_role_policy_attachment" "amazon_eks_service_policy" { - count = "${var.enabled == "true" ? 1 : 0}" + count = var.enabled ? 1 : 0 policy_arn = "arn:aws:iam::aws:policy/AmazonEKSServicePolicy" - role = "${join("", aws_iam_role.default.*.name)}" + role = join("", aws_iam_role.default.*.name) } resource "aws_security_group" "default" { - count = "${var.enabled == "true" ? 1 : 0}" - name = "${module.label.id}" + count = var.enabled ? 1 : 0 + name = module.label.id description = "Security Group for EKS cluster" - vpc_id = "${var.vpc_id}" - tags = "${module.label.tags}" + vpc_id = var.vpc_id + tags = module.label.tags } resource "aws_security_group_rule" "egress" { - count = "${var.enabled == "true" ? 1 : 0}" + count = var.enabled ? 1 : 0 description = "Allow all egress traffic" from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] - security_group_id = "${join("", aws_security_group.default.*.id)}" + security_group_id = join("", aws_security_group.default.*.id) type = "egress" } resource "aws_security_group_rule" "ingress_workers" { - count = "${var.enabled == "true" ? var.workers_security_group_count : 0}" + count = var.enabled ? var.workers_security_group_count : 0 description = "Allow the cluster to receive communication from the worker nodes" from_port = 0 to_port = 65535 protocol = "-1" - source_security_group_id = "${element(var.workers_security_group_ids, count.index)}" - security_group_id = "${join("", aws_security_group.default.*.id)}" + source_security_group_id = var.workers_security_group_ids[count.index] + security_group_id = join("", aws_security_group.default.*.id) type = "ingress" } resource "aws_security_group_rule" "ingress_security_groups" { - count = "${var.enabled == "true" ? length(var.allowed_security_groups) : 0}" + count = var.enabled ? length(var.allowed_security_groups) : 0 description = "Allow inbound traffic from existing Security Groups" from_port = 0 to_port = 65535 protocol = "-1" - source_security_group_id = "${element(var.allowed_security_groups, count.index)}" - security_group_id = "${join("", aws_security_group.default.*.id)}" + source_security_group_id = var.allowed_security_groups[count.index] + security_group_id = join("", aws_security_group.default.*.id) type = "ingress" } resource "aws_security_group_rule" "ingress_cidr_blocks" { - count = "${var.enabled == "true" && length(var.allowed_cidr_blocks) > 0 ? 1 : 0}" + count = var.enabled && length(var.allowed_cidr_blocks) > 0 ? 1 : 0 description = "Allow inbound traffic from CIDR blocks" from_port = 0 to_port = 65535 protocol = "-1" - cidr_blocks = ["${var.allowed_cidr_blocks}"] - security_group_id = "${join("", aws_security_group.default.*.id)}" + cidr_blocks = var.allowed_cidr_blocks + security_group_id = join("", aws_security_group.default.*.id) type = "ingress" } resource "aws_eks_cluster" "default" { - count = "${var.enabled == "true" ? 1 : 0}" - name = "${module.label.id}" - role_arn = "${join("", aws_iam_role.default.*.arn)}" - version = "${var.kubernetes_version}" - enabled_cluster_log_types = ["${var.enabled_cluster_log_types}"] + count = var.enabled ? 1 : 0 + name = module.label.id + role_arn = join("", aws_iam_role.default.*.arn) + version = var.kubernetes_version + enabled_cluster_log_types = var.enabled_cluster_log_types vpc_config { - security_group_ids = ["${join("", aws_security_group.default.*.id)}"] - subnet_ids = ["${var.subnet_ids}"] - endpoint_private_access = "${var.endpoint_private_access}" - endpoint_public_access = "${var.endpoint_public_access}" + security_group_ids = [join("", aws_security_group.default.*.id)] + subnet_ids = var.subnet_ids + endpoint_private_access = var.endpoint_private_access + endpoint_public_access = var.endpoint_public_access } depends_on = [ - "aws_iam_role_policy_attachment.amazon_eks_cluster_policy", - "aws_iam_role_policy_attachment.amazon_eks_service_policy", + aws_iam_role_policy_attachment.amazon_eks_cluster_policy, + aws_iam_role_policy_attachment.amazon_eks_service_policy ] } - -locals { - certificate_authority_data_list = "${coalescelist(aws_eks_cluster.default.*.certificate_authority, list(list(map("data", ""))))}" - certificate_authority_data_list_internal = "${local.certificate_authority_data_list[0]}" - certificate_authority_data_map = "${local.certificate_authority_data_list_internal[0]}" - certificate_authority_data = "${local.certificate_authority_data_map["data"]}" -} - -data "template_file" "kubeconfig" { - count = "${var.enabled == "true" ? 1 : 0}" - template = "${file("${path.module}/kubeconfig.tpl")}" - - vars { - server = "${join("", aws_eks_cluster.default.*.endpoint)}" - certificate_authority_data = "${local.certificate_authority_data}" - cluster_name = "${module.label.id}" - } -} diff --git a/outputs.tf b/outputs.tf index 115949ce..9c96652f 100644 --- a/outputs.tf +++ b/outputs.tf @@ -1,44 +1,39 @@ -output "kubeconfig" { - description = "`kubeconfig` configuration to connect to the cluster using `kubectl`. https://www.terraform.io/docs/providers/aws/guides/eks-getting-started.html#configuring-kubectl-for-eks" - value = "${join("", data.template_file.kubeconfig.*.rendered)}" -} - output "security_group_id" { description = "ID of the EKS cluster Security Group" - value = "${join("", aws_security_group.default.*.id)}" + value = join("", aws_security_group.default.*.id) } output "security_group_arn" { description = "ARN of the EKS cluster Security Group" - value = "${join("", aws_security_group.default.*.arn)}" + value = join("", aws_security_group.default.*.arn) } output "security_group_name" { description = "Name of the EKS cluster Security Group" - value = "${join("", aws_security_group.default.*.name)}" + value = join("", aws_security_group.default.*.name) } output "eks_cluster_id" { description = "The name of the cluster" - value = "${join("", aws_eks_cluster.default.*.id)}" + value = join("", aws_eks_cluster.default.*.id) } output "eks_cluster_arn" { description = "The Amazon Resource Name (ARN) of the cluster" - value = "${join("", aws_eks_cluster.default.*.arn)}" -} - -output "eks_cluster_certificate_authority_data" { - description = "The base64 encoded certificate data required to communicate with the cluster" - value = "${local.certificate_authority_data}" + value = join("", aws_eks_cluster.default.*.arn) } output "eks_cluster_endpoint" { description = "The endpoint for the Kubernetes API server" - value = "${join("", aws_eks_cluster.default.*.endpoint)}" + value = join("", aws_eks_cluster.default.*.endpoint) } output "eks_cluster_version" { description = "The Kubernetes server version of the cluster" - value = "${join("", aws_eks_cluster.default.*.version)}" + value = join("", aws_eks_cluster.default.*.version) +} + +output "eks_cluster_certificate_authority_data" { + description = "The Kubernetes cluster certificate authority data" + value = local.certificate_authority_data } diff --git a/test/.gitignore b/test/.gitignore new file mode 100644 index 00000000..442804a2 --- /dev/null +++ b/test/.gitignore @@ -0,0 +1 @@ +.test-harness diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 00000000..17b2fe74 --- /dev/null +++ b/test/Makefile @@ -0,0 +1,43 @@ +TEST_HARNESS ?= https://github.com/cloudposse/test-harness.git +TEST_HARNESS_BRANCH ?= master +TEST_HARNESS_PATH = $(realpath .test-harness) +BATS_ARGS ?= --tap +BATS_LOG ?= test.log + +# Define a macro to run the tests +define RUN_TESTS +@echo "Running tests in $(1)" +@cd $(1) && bats $(BATS_ARGS) $(addsuffix .bats,$(addprefix $(TEST_HARNESS_PATH)/test/terraform/,$(TESTS))) +endef + +default: all + +-include Makefile.* + +## Provision the test-harnesss +.test-harness: + [ -d $@ ] || git clone --depth=1 -b $(TEST_HARNESS_BRANCH) $(TEST_HARNESS) $@ + +## Initialize the tests +init: .test-harness + +## Install all dependencies (OS specific) +deps:: + @exit 0 + +## Clean up the test harness +clean: + [ "$(TEST_HARNESS_PATH)" == "/" ] || rm -rf $(TEST_HARNESS_PATH) + +## Run all tests +all: module examples/complete + +## Run basic sanity checks against the module itself +module: export TESTS ?= installed lint get-modules module-pinning get-plugins provider-pinning validate terraform-docs input-descriptions output-descriptions +module: deps + $(call RUN_TESTS, ../) + +## Run tests against example +examples/complete: export TESTS ?= installed lint get-modules get-plugins validate +examples/complete: deps + $(call RUN_TESTS, ../$@) diff --git a/test/Makefile.alpine b/test/Makefile.alpine new file mode 100644 index 00000000..7925b186 --- /dev/null +++ b/test/Makefile.alpine @@ -0,0 +1,5 @@ +ifneq (,$(wildcard /sbin/apk)) +## Install all dependencies for alpine +deps:: init + @apk add --update terraform-docs@cloudposse json2hcl@cloudposse +endif diff --git a/test/src/.gitignore b/test/src/.gitignore new file mode 100644 index 00000000..31b0219e --- /dev/null +++ b/test/src/.gitignore @@ -0,0 +1,2 @@ +.gopath +vendor/ diff --git a/test/src/Gopkg.lock b/test/src/Gopkg.lock new file mode 100644 index 00000000..87bb6bd6 --- /dev/null +++ b/test/src/Gopkg.lock @@ -0,0 +1,92 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec" + name = "github.com/davecgh/go-spew" + packages = ["spew"] + pruneopts = "UT" + revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73" + version = "v1.1.1" + +[[projects]] + digest = "1:75d6042fc66aebc974cc49b0c6c7cc3b9adb5f8130fbfa0dbec0820d990afa25" + name = "github.com/gruntwork-io/terratest" + packages = [ + "modules/collections", + "modules/customerrors", + "modules/files", + "modules/logger", + "modules/retry", + "modules/shell", + "modules/ssh", + "modules/terraform", + ] + pruneopts = "UT" + revision = "892abb2c35878d0808101bbfe6559e931dc2d354" + version = "v0.16.0" + +[[projects]] + digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe" + name = "github.com/pmezard/go-difflib" + packages = ["difflib"] + pruneopts = "UT" + revision = "792786c7400a136282c1664665ae0a8db921c6c2" + version = "v1.0.0" + +[[projects]] + digest = "1:5da8ce674952566deae4dbc23d07c85caafc6cfa815b0b3e03e41979cedb8750" + name = "github.com/stretchr/testify" + packages = [ + "assert", + "require", + ] + pruneopts = "UT" + revision = "ffdc059bfe9ce6a4e144ba849dbedead332c6053" + version = "v1.3.0" + +[[projects]] + branch = "master" + digest = "1:831470c2758c8b733941144f2803a0ccad0632c5a767415b777ebd296b5f463e" + name = "golang.org/x/crypto" + packages = [ + "curve25519", + "ed25519", + "ed25519/internal/edwards25519", + "internal/chacha20", + "internal/subtle", + "poly1305", + "ssh", + "ssh/agent", + ] + pruneopts = "UT" + revision = "22d7a77e9e5f409e934ed268692e56707cd169e5" + +[[projects]] + branch = "master" + digest = "1:76ee51c3f468493aff39dbacc401e8831fbb765104cbf613b89bef01cf4bad70" + name = "golang.org/x/net" + packages = ["context"] + pruneopts = "UT" + revision = "f3200d17e092c607f615320ecaad13d87ad9a2b3" + +[[projects]] + branch = "master" + digest = "1:181f3fd33e620b958b5ab77da177cf775cdcccd7db82963607875fbd09ae995e" + name = "golang.org/x/sys" + packages = [ + "cpu", + "unix", + ] + pruneopts = "UT" + revision = "9cd6430ef91e39e1a0ec0470cf1321a33ef1b887" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + input-imports = [ + "github.com/gruntwork-io/terratest/modules/terraform", + "github.com/stretchr/testify/assert", + ] + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/test/src/Gopkg.toml b/test/src/Gopkg.toml new file mode 100644 index 00000000..995bac57 --- /dev/null +++ b/test/src/Gopkg.toml @@ -0,0 +1,7 @@ +[[constraint]] + name = "github.com/stretchr/testify" + version = "1.2.2" + +[prune] + go-tests = true + unused-packages = true diff --git a/test/src/Makefile b/test/src/Makefile new file mode 100644 index 00000000..e59dda32 --- /dev/null +++ b/test/src/Makefile @@ -0,0 +1,50 @@ +PACKAGE = terraform-aws-eks-cluster +GOEXE ?= /usr/bin/go +GOPATH = $(CURDIR)/.gopath +GOBIN = $(GOPATH)/bin +BASE = $(GOPATH)/src/$(PACKAGE) +PATH := $(PATH):$(GOBIN) + +export TF_DATA_DIR ?= $(CURDIR)/.terraform +export TF_CLI_ARGS_init ?= -get-plugins=true +export GOPATH + +.PHONY: all +## Default target +all: test + +ifneq (,$(wildcard /sbin/apk)) +## Install go, if not installed +$(GOEXE): + apk add --update go +endif + +ifeq ($(shell uname -s),Linux) +## Install all `dep`, if not installed +$(GOBIN)/dep: + @mkdir -p $(GOBIN) + @curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh +endif + +## Prepare the GOPATH +$(BASE): $(GOEXE) + @mkdir -p $(dir $@) + @ln -sf $(CURDIR) $@ + +## Download vendor dependencies to vendor/ +$(BASE)/vendor: $(BASE) $(GOBIN)/dep + cd $(BASE) && dep ensure + +.PHONY : init +## Initialize tests +init: $(BASE)/vendor + +.PHONY : test +## Run tests +test: init + cd $(BASE) && go test -v -timeout 30m -run TestExamplesComplete + +.PHONY : clean +## Clean up files +clean: + rm -rf .gopath/ vendor/ $(TF_DATA_DIR) diff --git a/test/src/examples_complete_test.go b/test/src/examples_complete_test.go new file mode 100644 index 00000000..66c11a85 --- /dev/null +++ b/test/src/examples_complete_test.go @@ -0,0 +1,122 @@ +package test + +import ( + "fmt" + "sync/atomic" + "testing" + "time" + + "github.com/gruntwork-io/terratest/modules/terraform" + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/cache" + "k8s.io/client-go/tools/clientcmd" +) + +// Test the Terraform module in examples/complete using Terratest. +func TestExamplesComplete(t *testing.T) { + t.Parallel() + + terraformOptions := &terraform.Options{ + // The path to where our Terraform code is located + TerraformDir: "../../examples/complete", + Upgrade: true, + // Variables to pass to our Terraform code using -var-file options + VarFiles: []string{"fixtures.us-east-2.tfvars"}, + } + + // At the end of the test, run `terraform destroy` to clean up any resources that were created + defer terraform.Destroy(t, terraformOptions) + + // This will run `terraform init` and `terraform apply` and fail the test if there are any errors + terraform.InitAndApply(t, terraformOptions) + + // Run `terraform output` to get the value of an output variable + vpcCidr := terraform.Output(t, terraformOptions, "vpc_cidr") + // Verify we're getting back the outputs we expect + assert.Equal(t, "172.16.0.0/16", vpcCidr) + + // Run `terraform output` to get the value of an output variable + privateSubnetCidrs := terraform.OutputList(t, terraformOptions, "private_subnet_cidrs") + // Verify we're getting back the outputs we expect + assert.Equal(t, []string{"172.16.0.0/19", "172.16.32.0/19"}, privateSubnetCidrs) + + // Run `terraform output` to get the value of an output variable + publicSubnetCidrs := terraform.OutputList(t, terraformOptions, "public_subnet_cidrs") + // Verify we're getting back the outputs we expect + assert.Equal(t, []string{"172.16.96.0/19", "172.16.128.0/19"}, publicSubnetCidrs) + + // Run `terraform output` to get the value of an output variable + workersAutoscalingGroupName := terraform.Output(t, terraformOptions, "workers_autoscaling_group_name") + // Verify we're getting back the outputs we expect + assert.Contains(t, workersAutoscalingGroupName, "eg-test-eks") + + // Run `terraform output` to get the value of an output variable + workersLaunchTemplateArn := terraform.Output(t, terraformOptions, "workers_launch_template_arn") + // Verify we're getting back the outputs we expect + assert.Contains(t, workersLaunchTemplateArn, "arn:aws:ec2:us-east-2:126450723953:launch-template") + + // Run `terraform output` to get the value of an output variable + workersSecurityGroupName := terraform.Output(t, terraformOptions, "workers_security_group_name") + // Verify we're getting back the outputs we expect + assert.Equal(t, "eg-test-eks-workers", workersSecurityGroupName) + + // Run `terraform output` to get the value of an output variable + workerRoleName := terraform.Output(t, terraformOptions, "workers_role_name") + // Verify we're getting back the outputs we expect + assert.Equal(t, "eg-test-eks-workers", workerRoleName) + + // Run `terraform output` to get the value of an output variable + eksClusterId := terraform.Output(t, terraformOptions, "eks_cluster_id") + // Verify we're getting back the outputs we expect + assert.Equal(t, "eg-test-eks-cluster", eksClusterId) + + // Run `terraform output` to get the value of an output variable + eksClusterSecurityGroupName := terraform.Output(t, terraformOptions, "eks_cluster_security_group_name") + // Verify we're getting back the outputs we expect + assert.Equal(t, "eg-test-eks-cluster", eksClusterSecurityGroupName) + + // Wait for the worker nodes to join the cluster + // https://github.com/kubernetes/client-go + // https://www.rushtehrani.com/post/using-kubernetes-api + // https://rancher.com/using-kubernetes-api-go-kubecon-2017-session-recap + // https://gianarb.it/blog/kubernetes-shared-informer + fmt.Println("Waiting for worker nodes to join the EKS cluster") + + kubeconfigPath := "/.kube/config" + config, err := clientcmd.BuildConfigFromFlags("", kubeconfigPath) + assert.NoError(t, err) + + clientset, err := kubernetes.NewForConfig(config) + assert.NoError(t, err) + + factory := informers.NewSharedInformerFactory(clientset, 0) + informer := factory.Core().V1().Nodes().Informer() + stopChannel := make(chan struct{}) + var countOfWorkerNodes uint64 = 0 + + informer.AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + node := obj.(*corev1.Node) + fmt.Printf("Worker Node %s has joined the EKS cluster at %s\n", node.Name, node.CreationTimestamp) + atomic.AddUint64(&countOfWorkerNodes, 1) + if countOfWorkerNodes > 1 { + close(stopChannel) + } + }, + }) + + go informer.Run(stopChannel) + + select { + case <-stopChannel: + msg := "All worker nodes have joined the EKS cluster" + fmt.Println(msg) + case <-time.After(5 * time.Minute): + msg := "Not all worker nodes have joined the EKS cluster" + fmt.Println(msg) + assert.Fail(t, msg) + } +} diff --git a/variables.tf b/variables.tf index 016fff0f..a1ca3c20 100644 --- a/variables.tf +++ b/variables.tf @@ -1,100 +1,154 @@ +variable "region" { + type = string + description = "AWS Region" +} + variable "namespace" { - type = "string" + type = string description = "Namespace, which could be your organization name, e.g. 'eg' or 'cp'" + default = "" } variable "stage" { - type = "string" + type = string description = "Stage, e.g. 'prod', 'staging', 'dev', or 'test'" -} - -variable "environment" { - type = "string" default = "" - description = "Environment, e.g. 'testing', 'UAT'" } variable "name" { - type = "string" - default = "app" + type = string description = "Solution name, e.g. 'app' or 'cluster'" } variable "delimiter" { - type = "string" + type = string default = "-" description = "Delimiter to be used between `name`, `namespace`, `stage`, etc." } variable "attributes" { - type = "list" + type = list(string) default = [] description = "Additional attributes (e.g. `1`)" } variable "tags" { - type = "map" + type = map(string) default = {} description = "Additional tags (e.g. `map('BusinessUnit`,`XYZ`)" } variable "enabled" { - type = "string" + type = bool description = "Whether to create the resources. Set to `false` to prevent the module from creating any resources" - default = "true" + default = true } variable "vpc_id" { - type = "string" + type = string description = "VPC ID for the EKS cluster" } variable "subnet_ids" { description = "A list of subnet IDs to launch the cluster in" - type = "list" + type = list(string) +} + +variable "associate_public_ip_address" { + type = bool + description = "Associate a public IP address with an instance in a VPC" + default = true } variable "allowed_security_groups" { - type = "list" + type = list(string) default = [] description = "List of Security Group IDs to be allowed to connect to the EKS cluster" } variable "allowed_cidr_blocks" { - type = "list" + type = list(string) default = [] description = "List of CIDR blocks to be allowed to connect to the EKS cluster" } +variable "workers_role_arns" { + type = list(string) + description = "List of Role ARNs of the worker nodes" +} + variable "workers_security_group_ids" { - type = "list" + type = list(string) description = "Security Group IDs of the worker nodes" } variable "workers_security_group_count" { + type = number description = "Count of the worker Security Groups. Needed to prevent Terraform error `count can't be computed`" } variable "kubernetes_version" { - type = "string" - default = "" - description = "Desired Kubernetes master version. If you do not specify a value, the latest available version is used." + type = string + default = "1.14" + description = "Desired Kubernetes master version. If you do not specify a value, the latest available version is used" } variable "endpoint_private_access" { - type = "string" - default = "false" + type = bool + default = false description = "Indicates whether or not the Amazon EKS private API server endpoint is enabled. Default to AWS EKS resource and it is false" } variable "endpoint_public_access" { - type = "string" - default = "true" + type = bool + default = true description = "Indicates whether or not the Amazon EKS public API server endpoint is enabled. Default to AWS EKS resource and it is true" } variable "enabled_cluster_log_types" { - type = "list" + type = list(string) default = [] description = "A list of the desired control plane logging to enable. For more information, see https://docs.aws.amazon.com/en_us/eks/latest/userguide/control-plane-logs.html. Possible values [`api`, `audit`, `authenticator`, `controllerManager`, `scheduler`]" } + +variable "apply_config_map_aws_auth" { + type = bool + default = true + description = "Whether to generate local files from `kubeconfig` and `config-map-aws-auth` templates and perform `kubectl apply` to apply the ConfigMap to allow worker nodes to join the EKS cluster" +} + +variable "map_additional_aws_accounts" { + description = "Additional AWS account numbers to add to `config-map-aws-auth` ConfigMap" + type = list(string) + default = [] +} + +variable "map_additional_iam_roles" { + description = "Additional IAM roles to add to `config-map-aws-auth` ConfigMap" + + type = list(object({ + rolearn = string + username = string + groups = list(string) + })) + + default = [] +} + +variable "map_additional_iam_users" { + description = "Additional IAM users to add to `config-map-aws-auth` ConfigMap" + + type = list(object({ + userarn = string + username = string + groups = list(string) + })) + + default = [] +} + +variable "kubeconfig_path" { + type = string + default = "~/.kube/config" + description = "The path to `kubeconfig` file" +} diff --git a/versions.tf b/versions.tf new file mode 100644 index 00000000..9840ed75 --- /dev/null +++ b/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = "~> 0.12.0" + + required_providers { + aws = "~> 2.0" + template = "~> 2.0" + null = "~> 2.0" + local = "~> 1.3" + } +}