Automating Testing for Generated HTML Content using Gradle
January 13th, 2017
Validate generated HTML content in your continuous integration (CI) pipeline using Gradle.
This blog post was originally posted at http://rancher.com/managing-container-clusters-terraform-rancher.
Infrastructure as code is a practice of codifying and automating the deployment and management of infrastructure with tooling. This allows for testing, reviewing, approving, and deploying infrastructure changes with the same processes and tools as application code.
Terraform from Hashicorp is a tool for abstracting service and provider APIs into declarative configuration files. It then tracks the state of the infrastructure and converges it to match the specified configuration.
Terraform ships with built-in support for a variety of cloud providers (AWS, CenturyLink Cloud, Google Cloud, Microsoft Azure, OpenStack, VMware vSphere, etc.) and other services such as BitBucket, GitHub, Fastly, Heroku DNSimple, Rancher, etc. The full list of providers can be found at https://www.terraform.io/docs/providers/index.html
Beginning with version 0.8.0, Terraform is able to manage resources within a Rancher cluster using the built-in provider. The full list of supported resources for Rancher can be found here. Any issues should be reported at Github.
This post assumes that a Rancher server is available for testing against and the latest version of Terraform is installed. If a Rancher server is not available, one can be started locally using the following Docker command:
$ docker run -d -p 8080:8080 rancher/server:v1.3.3
The first step to managing a Rancher server with Terraform is to configure the rancher
provider for Terraform. Create a working directory for this example and place the following code in the rancher.tf
within the directory.
provider "rancher" {
api_url = "http://localhost:8080" //<1>
access_key = "" //<2>
secret_key = ""
}
The only required configuration for the rancher
provider is api_key
(<1>). This should be configured to point to the Rancher server location. Optionally, the API access and secret keys can be configured with the access_key
and secret_key
properties as shown (<2>). If the Rancher server does not have access control configured, then these settings can be omitted. If access control is enabled, then API credentials are required. Credentials can be created by following the instructions here
Rancher is also able to access these properties using the environment variables RANCHER_URL
, RANCHER_ACCESS_KEY
, and RANCHER_SECRET_KEY
respectively. For security purposes, it is advisable to use the environment variables or Terraform variables to provide credentials.
Now that the provider is configured, Terraform can create new resources. Start by creating a new Environment
within Rancher by placing the following code in the demo.tf
file in the working directory.
resource "rancher_environment" "demo" { // <1>
name = "blog-demo" // <2>
description = "Demonstration environment" // <3>
orchestration = "cattle" // <4>
}
First, a new resource is declared with the type of rancher_environment
and a logical name of demo
(<1>). This is used for referencing this resource within the Terraform configuration. The name
property is required for the environment (<2>) and specifies the name of the environment in Rancher. Optionally, a description can be provided (<3>). Finally, the orchestration engine to use for the environment is specified (<4>). This value can be cattle
, kubernetes
, swarm
, or mesos
. If omitted, the value will default to cattle
.
Once specified, Terraform can dry-run and apply the configurations by executing terraform plan
and terraform apply
respectively.
$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.
The Terraform execution plan has been generated and is shown below.
Resources are shown in alphabetical order for quick scanning. Green resources
will be created (or destroyed and then created if an existing resource
exists), yellow resources are being changed in-place, and red resources
will be destroyed. Cyan entries are data sources to be read.
Note: You didn't specify an "-out" parameter to save this plan, so when
"apply" is called, Terraform can't guarantee this is what will execute.
\+ rancher_environment.demo
description: "Demonstration environment"
name: "demo"
orchestration: "cattle"
Plan: 1 to add, 0 to change, 0 to destroy.
$ terraform apply
rancher_environment.demo: Creating...
description: "" => "Demonstration environment"
name: "" => "demo"
orchestration: "" => "cattle"
rancher_environment.demo: Creation complete
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
The state of your infrastructure has been saved to the path
below. This state is required to modify and destroy your
infrastructure, so keep it safe. To inspect the complete state
use the `terraform show` command.
State path: terraform.tfstate
Terraform can create the registration token information for a host to join and environment using the rancher_registration_token
resource. Add the following to the demo.tf
file.
resource "rancher_registration_token" "demo-token" { // <1>
environment_id = "${rancher_environment.demo.id}" // <2>
name = "demo-token" // <3>
description = "Host registration token for Demo environment"
}
The environment is declared by creating a new rancher_registration_token
resource with the logical name of demo-token
(<1>). The token is declared to be created in the demo
environment by populating the environment_id
property with the id
property from the previously created environment (<2>). This uses Terraform’s builtin interpolation and resource dependency system. Finally, the name
is a required property (<3>) and an optional description
can be provided.
Run terraform plan
and terraform apply
to view and apply the changes once again. Note that any IDs in the sample output may be different.
$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.
rancher_environment.demo: Refreshing state... (ID: 1a7)
The Terraform execution plan has been generated and is shown below.
Resources are shown in alphabetical order for quick scanning. Green resources
will be created (or destroyed and then created if an existing resource
exists), yellow resources are being changed in-place, and red resources
will be destroyed. Cyan entries are data sources to be read.
Note: You didn't specify an "-out" parameter to save this plan, so when
"apply" is called, Terraform can't guarantee this is what will execute.
\+ rancher_registration_token.demo-token
command: ""
description: "Host registration token for Demo environment"
environment_id: "1a7"
name: "demo-token"
registration_url: ""
token: ""
Plan: 1 to add, 0 to change, 0 to destroy.
$ terraform apply
rancher_environment.demo: Refreshing state... (ID: 1a7)
rancher_registration_token.demo-token: Creating...
command: "" => ""
description: "" => "Host registration token for Demo environment"
environment_id: "" => "1a7"
name: "" => "demo-token"
registration_url: "" => ""
token: "" => ""
rancher_registration_token.demo-token: Creation complete
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
The state of your infrastructure has been saved to the path
below. This state is required to modify and destroy your
infrastructure, so keep it safe. To inspect the complete state
use the `terraform show` command.
State path: terraform.tfstate
The rancher_registration_token
resource outputs a few useful properties. First it captures the entire command
value. This is the value that is visible in the Rancher UI when adding a Custom Host. It contains the entire command for staring the Rancher agent on a machine with Docker to join this environment.
Additionally, the registration_url
and token
properties are captured. These contain the full URL for the Rancher agent to connect to as specified in the the command
property and the environment specific path token from that URL respectively.
To view the host command, an output
can be configured in Terraform to capture the value and write it to the console. Add the following block to the demo.tf
file.
output "rancher_agent_command" {
value = "${rancher_registration_token.demo-token.command}"
}
Now run a terraform apply
to configure the output
value.
$ terraform apply
rancher_environment.demo: Refreshing state... (ID: 1a7)
rancher_registration_token.demo-token: Refreshing state... (ID: 1c2)
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
rancher_agent_command = sudo docker run -d --privileged -v /var/run/docker.sock:/var/run/docker.sock -v /var/lib/rancher:/var/lib/rancher rancher/agent:v1.1.3 http://localhost:8080/v1/scripts/A17BDBB62228B4B18B4A:1485619200000:undsWydVkrYSHqd2NNU5fVDdx7s
The rancher_registration_token
outputs can be used to configure the launch parameters for other resources. For example, if Amazon Web Services is being used to create an Autoscaling Group of instances to join the cluster, the registration command can be added to the user data for like so.
resource "aws_launch_configuration" "demo-hosts" {
name = "demo_hosts"
image_id = "ami-6edd3078" // <1>
instance_type = "t2.micro"
root_block_device {
volume_type = "gp2"
volume_size = 8
delete_on_termination = true
}
user_data = < permissions: "0755"
content: |
#!/bin/bash
DOCKER_VERSION=$1
apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
mkdir -p /etc/apt/sources.list.d/
echo "deb https://apt.dockerproject.org/repo ubuntu-xenial main" > /etc/apt/sources.list.d/docker.list
apt-get update
apt-get install -y linux-image-extra-virtual linux-image-extra-$(uname -r) docker-engine=$DOCKER_VERSION
- path: /opt/rancher/rancher.sh // <3>
permissions: "0755"
content: |
#!/bin/sh
set -e
umask 077
${rancher_registration_token.demo-token.command}
runcmd:
- [ cloud-init-per, once, docker, /opt/docker/install.sh, "1.13.0-0~ubuntu-xenial" ]
- [ cloud-init-per, once, rancher, /opt/rancher/rancher.sh ]
EOF
}
This block creates a launch configuration for an Ubuntu Xenial image in the us-east-1
region in AWS (<1>). It adds boot time scripts to install Docker (<2>), and to start the Rancher agent using the command string from the registration token. Notice that the command property is interpolated into the user data as part of a script file that is written out (<3>). This block is a example and the same pattern can be used for various operating systems or cloud providers.
Terraform can additionally create and manage stacks within a Rancher environment. The entire docker-compose.yml
and rancher-compose.yml
can be specified for the stack, or the Rancher catalog can be used to specify the entry and answers to create a stack. Add the following block to the demo.tf
file to create a new stack from the community catalog entry for the Ghost blogging platform.
resource "rancher_stack" "ghost" { // <1>
environment_id = "${rancher_environment.demo.id}" // <2>
name = "ghost" // <3>
description = "Ghost demo stack"
catalog_id = "community:ghost:0" // <4>
scope = "user"
start_on_create = true
environment { // <5>
public_port = "80"
}
}
The block adds a new rancher_stack
resource with the logical name of ghost
(<1>) within the demo
environment previously created (<2>). It specifies the name and description (optional) for the stack (<3>). It then configures the stack to be created from the entry named ghost
in the community
catalog using the version 0
of the template (<4>). These values can be discovered by utilizing the “View in API” feature of Rancher. Finally, it configures the answers to the questions defined in the catalog template using the environment
block (<5>). Run terraform apply
to create the stack in Rancher.
$ terraform apply
rancher_environment.demo: Refreshing state... (ID: 1a7)
rancher_registration_token.demo-token: Refreshing state... (ID: 1c2)
rancher_stack.ghost: Creating...
catalog_id: "" => "community:ghost:0"
description: "" => "Ghost demo stack"
environment.%: "" => "1"
environment.public_port: "" => "80"
environment_id: "" => "1a7"
name: "" => "ghost"
rendered_docker_compose: "" => ""
rendered_rancher_compose: "" => ""
scope: "" => "user"
start_on_create: "" => "true"
rancher_stack.ghost: Creation complete
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
The state of your infrastructure has been saved to the path
below. This state is required to modify and destroy your
infrastructure, so keep it safe. To inspect the complete state
use the `terraform show` command.
State path: terraform.tfstate
Outputs:
rancher_agent_command = sudo docker run -d --privileged -v /var/run/docker.sock:/var/run/docker.sock -v /var/lib/rancher:/var/lib/rancher rancher/agent:v1.1.3 http://localhost:8080/v1/scripts/A17BDBB62228B4B18B4A:1485622800000:E0L4yrjYajfxbg7mKSNxLZGup0
Open your the Rancher dashboard, switch the demo
environment and see that the ghost
stack is created and active:
[caption id=“attachment_16315” align=“aligncenter” width=“1024”] Stack created by Terraform in Rancher[/caption]
Terraform can be used to codify infrastructure across many platforms including Rancher. Using a tool like Terraform allows for easily repeating configuration and setup of infrastructure. It also allows for incorporating infrastructure changes into standard development practices and processes such as versioning, reviews, and promotions.
The full list of supported resources for Rancher can be found here. Any issues with the Rancher provider can be reported on Github.
Validate generated HTML content in your continuous integration (CI) pipeline using Gradle.
Apache Cassandra, a scalable and high-availability platform, is a good choice for high volume event management applications, such as large deployments of sensors. Applications include telematics data for large fleets, smart meter telemetry in…
What I thought of HashiConf 2016 as an attendee that brought my brain as a sponge to soak up DevOps wisdom.
Insert bio here