Now that we have defined our Fastly service using Terraform locally; we want to be able to reliably reproduce the infrastructure configuration in a continuous integration environment and deploy our changes to the service every time we check our config into version control such as GitHub. To achieve this we are going to use the open-source tool Travis CI.
Travis CI provides a hosted build environment and default set of steps to test, build and deploy project artefacts. You can customize any step in this process in a .travis.yml
yaml file in the root of your repository. Travis uses the file to learn about your project and how you want your builds to be executed.
-
Sign in to Travis CI with your GitHub account, accepting the GitHub access permissions confirmation.
-
Once you’re signed in, Travis will synchronize your repositories from GitHub, go to your profile page and enable Travis CI for your workshop repository
github.com/<USERNAME>/altitude-ci-cd-workshop
by toggling the associated switch.
Note: you may need to get Travis to sync with GitHub again.
Once Travis is configured and authorised for your repository we can add a .travis.yml
file. This declares the actions we'd like Travis perform in the CI environment whenever we change our configuration.
Edit the file named .travis.yml
within the root directory of our project using your text editor of choice.
A build on Travis CI is made up of three steps, all of which can be customized using the the yaml declaration format inside our .travis.yml
file:
install:
install any dependencies requiredscript:
run the build scriptdeploy:
deploy the artefacts
Note: There are actually more than 3 steps including some optional ones. The complete build lifecycle and hooks and information on how to customize them further can be found on the within the Travis docs.
The before_install
step tells Travis to install the Terraform binary and place on the $PATH
so it is avaliable to use within our build environment. The script
and deploy
steps should be familiar, it's the terraform plan
commands that we ran in the first exercise.
Make an empty commit and push to travis.
$ git commit --allow-empty -m "Force travis run"
$ git push origin master
Navigate back to the Travis CI dashboard. After a few moments, you should see a build start and eventually fail. You should have an error like this:
Errors:
* 2 error(s) occurred:
* Required variable not set: fastly_name
* Required variable not set: fastly_api_token
Locally, we were using terraform.tfvars
to store our sensitive variables. That is not checked into git, so Travis CI does not have access to those values. We need to provide those to Travis, securely.
For obvious security reasons its a very bad idea to store any sensitive data such as our Fastly API token in plain-text within our source code. Especially on an open-source project like this. Fortunately, Travis provides a way to encrypt and store secrets in the yaml file using public/private key signing.
Terraform reads any variables from the environment prefixed with TF_VAR_<variable name>
, such as TF_VAR_fastly_api_token
. We can leverage this feature on Travis, since Travis supports securely storing environment variables.
You can find your API key and service name in terraform.tfvars
.
Using the Travis binary (installed during setup) run the following command to add your token and service id to the Travis file:
$ travis encrypt TF_VAR_fastly_api_token=<YOUR_TOKEN> --add
Run the same command to generate the value for your service id:
$ travis encrypt TF_VAR_fastly_name=<YOUR_NAME> --add
Your .travis.yml
file should now have an env declaration at the bottom of the file and should look something like this:
env:
global:
secure:
# ...
Note: Travis parses the YAML file, so our comments actually get removed :(.
To get Travis to trigger our newly defined build with environment variables within CI, we need to add the changed files in our working directory and push them to GitHub. Travis will observe the change to our repository and automatically start a build to run our install
and script
commands defined in the .travis.yml
in the CI environment.
$ git add -A
$ git commit -m 'Testing Travis CI'
$ git push origin master
Wait for Travis CI to run a build on your fork of the altitude-ci-cd-workshop
repository. You can view the output and build status by navigating to: https://travis-ci.org/<USERNAME>/altitude-ci-cd-workshop
. Click on the branches tab if the build has not started yet. Depending on the time of day this may take some time.
You should have see the output of our terraform plan
in the job log tab:
Now that we have Travis continuously integrating our changes whenever we push changes to our terraform configuration to GitHub, we can use the deploy:
step inside .travis.yml
to actually apply our changes to our Fastly service in production.
Review the following declaration the bottom of your .travis.yml
file:
deploy:
- provider: script
skip_cleanup: true
script: terraform apply ./terraform
on:
branch: master
The script:
declaration inside the deploy block is the command that Travis will execute when running the deploy
step. Here we have simply ran our terraform apply
command as per the last exercise:
script: terraform apply ./terraform
As we only want to deploy the changes to our service once we are satisfied with them (such as tests passing in the script
step, peer reviewed by a colleague etc.) via a pull request into the master branch. We can tell Travis to only deploy the changes on a merge into the master
branch via the on:
declaration of the deploy block. I.e. only perform "this" action on "this" branch.
on:
branch: master
In order to test our pipeline, we need to make a change.
First, create a new branch. In a real production scenario, you would likely seek peer review using a Pull Request workflow, so let's mirror that here.
$ git checkout -b changes
Next, let's add a second domain to our main.tf. After the first domain stanza, add a second domain
stanza which a "-2" suffix.
# terraform/main.tf
resource "fastly_service_v1" "my-fastly-service" {
name = "${var.fastly_name}"
# ...
domain {
name = "${var.fastly_name}-2.fastly-altitude-2017.com" # <--
}
# ...
Add and commit these changes:
$ git add -A
$ git commit -m 'Testing Travis CI deployment'
$ git push origin changes
Raise a new pull request for the branch changes in GitHub and merge it:
- Navigate to the repository on GitHub and click "Compare & pull request"
- Fill out the PR form title and description. Note: ensure we are merging into the base master of our forked repo and not the upstream master
- Click the "Merge pull request" button
If you jump back over to travis-ci.org you should now be able to watch the build of the master
branch with our newly added deployment step creating our Fastly service.
You should see the apply
output in the build log.
If your configuration was successfully applied via Travis CI you should be able to curl
your newly updated service on it's secondary domain.
$ curl -v http://<YOUR SERVICE DOMAIN>.global.prod.fastly.net/index.html
HTTP/1.1 200 OK
Cache-Control: public, max-age=3600
Content-Type: text/html
Content-Length: 11651
Connection: keep-alive
X-Served-By: cache-lcy1136-LCY
X-Cache: HIT
X-Cache-Hits: 1
If you don't believe this you can even login to the Fastly web interface and look at your configuration.