Technoblogic(.io)

Shameful Self Promotion

Puppet-s3 & Continuous Integration With Puppet, Jenkins and AWS

The (puppet-s3)[https://www.github.com/malnick/puppet-s3] module.

Usage:

1
2
3
4
5
6
7
8
9
s3 { '/path/to/file/on/my/local/filesystem':
    # Required paramters:
    ensure              => present,
    source              => '/bucket/path/to/object',
    access_key_id       => 'mysecret',
    secret_access_key   => 'anothersecret',
    # Optional parameters:
    region              => 'us-west-1', # Defaults to us-east-1
}

Overview

Puppet s3 is my response to the need for secure downloads from S3 via Puppet. At SRC:CLR we use IAM authentication for S3 downloads. This piece of Puppet allows us to build out our Jenkins continuous integration hook between the build and deployment process.

Currently the S3 type is only psudo-idempotent. What does that mean? It means exists()? actually pulls down the specified object to compare to the one on disk via a tempfile. Then, if it returns false, create() pulls it down again.

Yes, this is horribly inefficient.

Yes, performance falls.

Yes, I plan on changing it in the near future.

The Longview

The reason we needed this provider is to enable us to pull S3 objects using our IAM authentication. But that is one small step towards our larger goal of fully integrating our Jenkins build pipeline with our Puppet deployed AWS infrastructure.

To fully realize this integration we needed a way to pull the S3 zip file that is our built application, from S3. Jenkins deposits this zip file for us.

Then, Puppet compares the version number on S3 with the version number on disk. If greater, it pulls the new version.

The Pipeline

The pipeline from git push to puppet agent -t looks like this:

  1. A PR is submitted in Github and human eyes do a final check on the code.
  2. If the human check passes, a manual merge to master is invoked.
  3. A human manually runs a Jenkins build for either QA or Production environments. Lets assume this is a production deployment for this example.
  4. The Jenkins build:
    1. Runs Java tests
    2. If the tests pass, builds a Java Jar.
    3. Places this jar on S3 as: our_app_${version}.zip Let’s assume $version is 3.2.1
    4. Sends a HTTP POST to our Puppet Master with the following JSON:
1
2
3
4
5
{
    'environment':'production',
    'role':'application_backend',
    'version':'3.2.1'
}

A webhook running on our Puppet Master parses the POST and does the following:

  1. Edits the hieradata file at /etc/puppetlabs/puppet/environments/${environment}/roles/${role}.yaml to replace the value of key profiles::sc_services::application_backend::version with the value sent in the POST: 3.2.1. $environment and $role are also inserted in the $path above to make a fully qualified path.
  2. Commits this yaml data file locally and pushes it back up to our Puppet control repo.
  3. It then invokes MCO as:
1
su - peadmin -c 'mco puppet runonce -F role=${role}

Where $role is our role sent in the POST