Skip to content

Commit

Permalink
Adding new module, documentation, and complete example (#1)
Browse files Browse the repository at this point in the history
* Initial commit

* Updating README

* Updating README

* Updating documentation

* Fixing small documentation thing

* Renaming k8s_namespace

* Updating a few things

* Some minor formatting tweaks

* A few more tweaks

* Updating examples

* Adding first draft of service acount

* A couple small tweaks

* Breaking out k8s resource values into TF vars

* Updating vars

* Renaming fixtures in complete example

* Updating variable description

* Fixing a formatting thing

* Fixing formatting & added missing variable

* Fixing a couple more things

* Update README.yaml

Co-authored-by: Erik Osterman <erik@cloudposse.com>

* Update README.yaml

Co-authored-by: Erik Osterman <erik@cloudposse.com>

* Update README.yaml

Co-authored-by: Erik Osterman <erik@cloudposse.com>

* Update variables.tf

Co-authored-by: Erik Osterman <erik@cloudposse.com>

* Update variables.tf

Co-authored-by: Erik Osterman <erik@cloudposse.com>

* Update README.yaml

Co-authored-by: Erik Osterman <erik@cloudposse.com>

* Update main.tf

Co-authored-by: Erik Osterman <erik@cloudposse.com>

* Making a bunch of updates

* Fixing a formatting thing

* Update README.md

Co-authored-by: Erik Osterman <erik@cloudposse.com>

* Update README.md

Co-authored-by: Erik Osterman <erik@cloudposse.com>

* Update README.yaml

Co-authored-by: Erik Osterman <erik@cloudposse.com>

* Updating README

* A few more updates

* Adding support for agent CLI arguments

* Cleaning up namespace/service_account stuff and adding outputs

* Lots of updates

* Updating README

* Fixing namespace output

* Fixing HEREDOC

Co-authored-by: Erik Osterman <erik@cloudposse.com>

* Fixing HEREDOC

Co-authored-by: Erik Osterman <erik@cloudposse.com>

* Fixing more HEREDOC

* Renaming tfc_extra_envs var to agent_envs

* Fixing a few things found during testing

Co-authored-by: Dan Meyers <danjbh@users.noreply.github.com>
Co-authored-by: Erik Osterman <erik@cloudposse.com>
  • Loading branch information
3 people committed Oct 13, 2020
1 parent 5209623 commit 6d60cb6
Show file tree
Hide file tree
Showing 13 changed files with 411 additions and 126 deletions.
133 changes: 84 additions & 49 deletions README.md

Large diffs are not rendered by default.

61 changes: 37 additions & 24 deletions README.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#

# Name of this project
name: terraform-example-module
name: terraform-kubernetes-tfc-cloud-agent

# Logo for this project
#logo: docs/logo.png
Expand All @@ -20,13 +20,13 @@ copyrights:
year: "2020"

# Canonical GitHub repo
github_repo: cloudposse/terraform-example-module
github_repo: cloudposse/terraform-kubernetes-tfc-cloud-agent

# Badges to display
badges:
- name: "Latest Release"
image: "https://img.shields.io/github/release/cloudposse/terraform-example-module.svg"
url: "https://github.com/cloudposse/terraform-example-module/releases/latest"
image: "https://img.shields.io/github/release/cloudposse/terraform-kubernetes-tfc-cloud-agent.svg"
url: "https://github.com/cloudposse/terraform-kubernetes-tfc-cloud-agent/releases/latest"
- name: "Slack Community"
image: "https://slack.cloudposse.com/badge.svg"
url: "https://slack.cloudposse.com"
Expand All @@ -48,41 +48,54 @@ references:
- name: "Terraform Module Requirements"
description: "HashiCorp's guidance on all the requirements for publishing a module. Meeting the requirements for publishing a module is extremely easy."
url: "https://www.terraform.io/docs/registry/modules/publish.html#requirements"
- name: "Terraform `random_integer` Resource"
description: "The resource random_integer generates random values from a given range, described by the min and max attributes of a given resource."
url: "https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/integer"
- name: "Terraform Version Pinning"
description: "The required_version setting can be used to constrain which versions of the Terraform CLI can be used with your configuration"
url: "https://www.terraform.io/docs/configuration/terraform.html#specifying-a-required-terraform-version"
- name: "Terraform Cloud Agents"
description: "Terraform Cloud Agents are a solution to allow Terraform Cloud to communicate with isolated, private, or on-premises infrastructure."
url: "https://www.terraform.io/docs/cloud/workspaces/agent.html"
- name: "Announcing HashiCorp Terraform Cloud Business Tier"
description: "This new tier includes enterprise features for advanced security, compliance and governance, the ability to execute multiple runs concurrently, and flexible support options."
url: "https://www.hashicorp.com/blog/announcing-hashicorp-terraform-cloud-business"
- name: "Announcing Terraform Cloud Business Tier with Armon Dadgar"
description: "Watch HashiCorp co-founder and CTO Armon Dadgar announce the latest addition to Terraform Cloud: the new Business tier."
url: "https://www.hashicorp.com/resources/announcing-terraform-cloud-business-tier-with-armon-dadgar"

# Short description of this project
description: |-
This is `terraform-example-module` project provides all the scaffolding for a typical well-built Cloud Posse module. It's a template repository you can
use when creating new repositories.
# Introduction to the project
#introduction: |-
# This is an introduction.
This project installs the Terraform Cloud Agent on an existing Kubernetes cluster. You must provide your own Kubernetes provider configuration in your project!
NOTE: Requires [Terraform Cloud Business or Terraform Enterprise](https://www.hashicorp.com/products/terraform/pricing) subscription.
# How to use this module. Should be an easy example to copy and paste.
usage: |-
Here's how to invoke this example module in your projects
```hcl
module "example" {
source = "https://github.com/cloudposse/terraform-example-module.git?ref=master"
example = "Hello world!"
provider "kubernetes" {
# Context to choose from the config file, if needed.
config_context = "example-context"
version = "~> 1.12"
}
module "tfc_agent" {
source = "https://github.com/cloudposse/terraform-kubernetes-tfc-cloud-agent.git?ref=master"
# Your agent token generated in Terraform Cloud
token = var.tfc_agent_token
namespace = var.namespace
stage = var.stage
environment = var.environment
name = var.name
# You can specify a namespace other than "default"
kubernetes_namespace = "tfc-agent"
}
```
# Example usage
examples: |-
Here is an example of using this module:
- [`examples/complete`](https://github.com/cloudposse/terraform-example-module/) - complete example of using this module
# How to get started quickly
#quickstart: |-
# Here's how to get started...
- [`examples/complete`](https://github.com/cloudposse/terraform-kubernetes-tfc-cloud-agent/) - complete example of using this module
# Other files to include in this README from the project folder
include:
Expand All @@ -91,5 +104,5 @@ include:

# Contributors to this project
contributors:
- name: "Erik Osterman"
github: "osterman"
- name: "Dan Meyers"
github: "danjbh"
30 changes: 23 additions & 7 deletions docs/terraform.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,56 @@
| Name | Version |
|------|---------|
| terraform | >= 0.12.0, < 0.14.0 |
| local | ~> 1.2 |
| random | ~> 2.2 |
| kubernetes | >= 1.12.0 |

## Providers

| Name | Version |
|------|---------|
| random | ~> 2.2 |
| kubernetes | >= 1.12.0 |

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| additional\_tag\_map | Additional tags for appending to tags\_as\_list\_of\_maps. Not added to `tags`. | `map(string)` | `{}` | no |
| agent\_cli\_args | Extra command line arguments to pass to tfc-agent | `list` | `[]` | no |
| agent\_image | Name and tag of Terraform Cloud Agent docker image | `string` | `"hashicorp/tfc-agent:latest"` | no |
| attributes | Additional attributes (e.g. `1`) | `list(string)` | `[]` | no |
| context | Single object for setting entire context at once.<br>See description of individual variables for details.<br>Leave string and numeric variables as `null` to use default value.<br>Individual variable settings (non-null) override settings in context object,<br>except for attributes, tags, and additional\_tag\_map, which are merged. | <pre>object({<br> enabled = bool<br> namespace = string<br> environment = string<br> stage = string<br> name = string<br> delimiter = string<br> attributes = list(string)<br> tags = map(string)<br> additional_tag_map = map(string)<br> regex_replace_chars = string<br> label_order = list(string)<br> id_length_limit = number<br> })</pre> | <pre>{<br> "additional_tag_map": {},<br> "attributes": [],<br> "delimiter": null,<br> "enabled": true,<br> "environment": null,<br> "id_length_limit": null,<br> "label_order": [],<br> "name": null,<br> "namespace": null,<br> "regex_replace_chars": null,<br> "stage": null,<br> "tags": {}<br>}</pre> | no |
| delimiter | Delimiter to be used between `namespace`, `environment`, `stage`, `name` and `attributes`.<br>Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. | `string` | `null` | no |
| deployment\_annotations | Annotations to add to the Kubernetes deployment | `map` | `{}` | no |
| deployment\_name | Override the deployment name in Kubernetes | `string` | `null` | no |
| enabled | Set to false to prevent the module from creating any resources | `bool` | `null` | no |
| environment | Environment, e.g. 'uw2', 'us-west-2', OR 'prod', 'staging', 'dev', 'UAT' | `string` | `null` | no |
| example | Example variable | `string` | `"hello world"` | no |
| id\_length\_limit | Limit `id` to this many characters.<br>Set to `0` for unlimited length.<br>Set to `null` for default, which is `0`.<br>Does not affect `id_full`. | `number` | `null` | no |
| kubernetes\_namespace | Kubernetes namespace override | `string` | `null` | no |
| label\_order | The naming order of the id output and Name tag.<br>Defaults to ["namespace", "environment", "stage", "name", "attributes"].<br>You can omit any of the 5 elements, but at least one must be present. | `list(string)` | `null` | no |
| name | Solution name, e.g. 'app' or 'jenkins' | `string` | `null` | no |
| namespace | Namespace, which could be your organization name or abbreviation, e.g. 'eg' or 'cp' | `string` | `null` | no |
| namespace\_creation\_enabled | Enable this if the Kubernetes namespace does not already exist | `bool` | `false` | no |
| regex\_replace\_chars | Regex to replace chars with empty string in `namespace`, `environment`, `stage` and `name`.<br>If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. | `string` | `null` | no |
| replicas | Number of replicas in the Kubernetes deployment | `number` | `1` | no |
| resource\_limits\_cpu | Kubernetes deployment resource hard CPU limit | `string` | `"1"` | no |
| resource\_limits\_memory | Kubernetes deployment resource hard memory limit | `string` | `"512Mi"` | no |
| resource\_requests\_cpu | Kubernetes deployment resource CPU requests | `string` | `"250m"` | no |
| resource\_requests\_memory | Kubernetes deployment resource memory requests | `string` | `"50Mi"` | no |
| service\_account\_annotations | Annotations to add to the Kubernetes service account | `map` | `{}` | no |
| stage | Stage, e.g. 'prod', 'staging', 'dev', OR 'source', 'build', 'test', 'deploy', 'release' | `string` | `null` | no |
| tags | Additional tags (e.g. `map('BusinessUnit','XYZ')` | `map(string)` | `{}` | no |
| tfc\_address | The HTTP or HTTPS address of the Terraform Cloud API. | `string` | `"https://app.terraform.io"` | no |
| tfc\_agent\_data\_dir | The path to a directory to store all agent-related data, including<br> Terraform configurations, cached Terraform release archives, etc. It is<br> important to ensure that the given directory is backed by plentiful<br> storage. | `string` | `"~/.tfc-agent"` | no |
| tfc\_agent\_disable\_update | Disable automatic core updates. | `bool` | `false` | no |
| tfc\_agent\_log\_level | The log verbosity expressed as a level string. Level options include<br> "trace", "debug", "info", "warn", and "error" | `string` | `"info"` | no |
| tfc\_agent\_single | Enable single mode. This causes the agent to handle at most one job and<br> immediately exit thereafter. Useful for running agents as ephemeral<br> containers, VMs, or other isolated contexts with a higher-level scheduler<br> or process supervisor. | `bool` | `false` | no |
| tfc\_agent\_token | The agent token to use when making requests to the Terraform Cloud API.<br> This token must be obtained from the API or UI. It is recommended to use<br> the environment variable whenever possible for configuring this setting due<br> to the sensitive nature of API tokens. | `string` | `""` | no |
| tfc\_extra\_envs | A map of any extra environment variables to pass to the TFC agent | `map` | `{}` | no |

## Outputs

| Name | Description |
|------|-------------|
| example | Example output |
| id | ID of the created example |
| random | Stable random number for this example |
| namespace | n/a |
| service\_account\_name | n/a |

<!-- markdownlint-restore -->
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
region = "us-east-2"

namespace = "eg"

environment = "ue2"

stage = "test"

name = "example"

14 changes: 10 additions & 4 deletions examples/complete/main.tf
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
module "example" {
source = "../.."

example = var.example
provider "kubernetes" {
version = "~> 1.12"
}

module "tfc_agent" {
source = "../.."
context = module.this.context

tfc_agent_token = var.tfc_agent_token

namespace_creation_enabled = true
kubernetes_namespace = "foo"
}
17 changes: 5 additions & 12 deletions examples/complete/outputs.tf
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
output "id" {
description = "ID of the created example"
value = module.example.id
output "service_account_name" {
value = module.tfc_agent.service_account_name
}

output "example" {
description = "Output \"example\" from example module"
value = module.example.example
}

output "random" {
description = "Output \"random\" from example module"
value = module.example.random
}
output "namespace" {
value = module.tfc_agent.namespace
}
4 changes: 2 additions & 2 deletions examples/complete/variables.tf
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
variable "example" {
variable "tfc_agent_token" {
type = string
description = "The value which will be passed to the example module"
description = "The preconfigured Terraform Cloud Agent token"
}
2 changes: 1 addition & 1 deletion examples/complete/versions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ terraform {
required_version = ">= 0.12.0, < 0.14"

required_providers {
local = "~> 1.2"
kubernetes = "~> 1.12"
}
}
118 changes: 111 additions & 7 deletions main.tf
Original file line number Diff line number Diff line change
@@ -1,13 +1,117 @@
resource "random_integer" "example" {
locals {
service_account_name = coalesce(var.deployment_name, module.this.id, "tfc-agent")
deployment_name = coalesce(var.deployment_name, module.this.id, "tfc-agent")

namespace = coalesce(var.kubernetes_namespace, "default")
}

resource "kubernetes_namespace" "namespace" {
count = var.namespace_creation_enabled ? 1 : 0
metadata {
name = local.namespace
}
}

resource "kubernetes_service_account" "service_account" {
count = module.this.enabled ? 1 : 0

min = 1
max = 50000
keepers = {
example = var.example
metadata {
name = local.service_account_name
namespace = local.namespace
annotations = var.service_account_annotations
}
}

locals {
example = format("%v %v", var.example, join("", random_integer.example[*].result))
resource "kubernetes_secret" "secret" {
metadata {
name = local.deployment_name
namespace = local.namespace
}

data = {
token = var.tfc_agent_token
}
}

resource "kubernetes_deployment" "tfc_cloud_agent" {
count = module.this.enabled ? 1 : 0

metadata {
name = local.deployment_name
namespace = local.namespace
labels = module.this.tags
}
spec {
selector {
match_labels = module.this.tags
}
replicas = var.replicas

template {
metadata {
namespace = local.namespace
labels = module.this.tags
annotations = var.deployment_annotations
}
spec {
service_account_name = local.service_account_name
automount_service_account_token = true
container {
image = var.agent_image
name = "tfc-agent"
args = var.agent_cli_args
env {
name = "TFC_AGENT_TOKEN"
value_from {
secret_key_ref {
key = "token"
name = local.deployment_name
}
}
}
env {
name = "TFC_AGENT_NAME"
value = coalesce(module.this.id, "tfc-agent")
}
env {
name = "TFC_AGENT_LOG_LEVEL"
value = var.tfc_agent_log_level
}
env {
name = "TFC_AGENT_DATA_DIR"
value = var.tfc_agent_data_dir
}
env {
name = "TFC_AGENT_SINGLE"
value = var.tfc_agent_single
}
env {
name = "TFC_AGENT_DISABLE_UPDATE"
value = var.tfc_agent_disable_update
}
env {
name = "TFC_ADDRESS"
value = var.tfc_address
}
dynamic "env" {
for_each = var.agent_envs
content {
name = env.key
value = env.value
}
}
resources {
limits {
cpu = var.resource_limits_cpu
memory = var.resource_limits_memory
}
requests {
cpu = var.resource_requests_cpu
memory = var.resource_requests_memory
}
}
}
}
}
}
}
17 changes: 6 additions & 11 deletions outputs.tf
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
output "id" {
description = "ID of the created example"
value = module.this.enabled ? module.this.id : null
output "service_account_name" {
value = local.service_account_name
description = "Name of the Kubernetes service account"
}

output "example" {
description = "Example output"
value = module.this.enabled ? local.example : null
}

output "random" {
description = "Stable random number for this example"
value = module.this.enabled ? join("", random_integer.example[*].result) : null
output "namespace" {
value = local.namespace
description = "Name of the Kubernetes namespace"
}
2 changes: 1 addition & 1 deletion test/src/examples_complete_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func TestExamplesComplete(t *testing.T) {
TerraformDir: "../../examples/complete",
Upgrade: true,
// Variables to pass to our Terraform code using -var-file options
VarFiles: []string{"fixtures.us-east-2.tfvars"},
VarFiles: []string{"fixtures.tfvars"},
// We always include a random attribute so that parallel tests
// and AWS resources do not interfere with each other
Vars: map[string]interface{}{
Expand Down
Loading

0 comments on commit 6d60cb6

Please sign in to comment.