When you're packaging your bespoke systems as BOSH releases, over and over again, you will first need a Ruby/Java/Golang/Python package for compilation or runtime. The BOSH community is now providing language packs to make it much simpler and faster. This article is a quick look and a "build a BOSH release" walk thru using the Go language pack (you can also see the resulting BOSH release).

The primary bosh command you'll need to learn is bosh vendor-package, and then how/when to use the compile.env and runtime.env scripts that are bundled with each language pack.

Find them all at https://github.com/bosh-packages. At the time of writing, the available language packs are:

  • java - openjdk-8 and openjdk-9
  • golang - golang-1.9 and golang-1.10
  • ruby - ruby-2.4-r3
  • python - python-2.7

Want to help make the world a better place? Pull requests for ruby-2.5 and python-3.6 would be welcomed by me so I can use them.

Note, afaik, the BOSH core team, doesn't call them "language packs". Yet.

I like "language packs", though "language releases" or packages may sound more familiar. - Dmitriy

Probably because there are other "packs" you can use that aren't programming languages: cf-cli and nginx. But I like the phrase "language packs". Your thoughts?

Walk Through

Let's package up a sample Golang web app as a BOSH release.

First, clone the sample app locally and confirm that you know how to compile and run it:

go get github.com/cloudfoundry-community/simple-go-web-app
cd $GOPATH/src/github.com/cloudfoundry-community/simple-go-web-app
export MESSAGE="this is the message to display"
export PORT=8000
go run main.go

Your sample Golang web app will now be running at http://localhost:8000/ and will display your $MESSAGE:

$ curl http://localhost:8000
this is the message to display

Now, let's package and run this via BOSH. First, create a workspace folder and clone the language pack. We're using golang-release to compile our Golang application:

mkdir -p ~/workspace
cd ~/workspace
git clone https://github.com/bosh-packages/golang-release

Create a new BOSH release project, or use an existing one, and vendor the language pack:

gem install bosh-gen
bosh-gen new simple-go-web-app
cd simple-go-web-app-boshrelease

I'm using bosh-gen because a) it makes everything in this article easier; and b) I wrote bosh-gen. This article assumes you've upgraded bosh-gen to v0.98.

Now, the first step of magic for language packs is the bosh vendor-package command:

bosh vendor-package golang-1.10-linux ~/workspace/golang-release

The output will look like:

Adding package 'golang-1.10-linux/48c842421b6f05acf88dc6ec17f7574dade28a86'...
-- Started uploading 'golang-1.10-linux/48c842421b6f05acf88dc6ec17f7574dade28a86' (sha1=e9a18aebf2c407ce17423c22c913b6612cf4b75f)
2018/04/04 09:44:29 Successfully uploaded file to https://s3.amazonaws.com/sample-go-boshrelease/58b5770b-eff5-49f1-5b2d-f834dc80e4d3
-- Finished uploading 'golang-1.10-linux/48c842421b6f05acf88dc6ec17f7574dade28a86' (sha1=e9a18aebf2c407ce17423c22c913b6612cf4b75f)
Added package 'golang-1.10-linux/48c842421b6f05acf88dc6ec17f7574dade28a86'

Your BOSH release will now have a new package:

$ git status
	.final_builds/
	packages/golang-1.10-linux/

Commit it to your repo:

git add .
git commit -m "add golang-1.10-linux langage pack"

Submodule the sample Golang app into the src folder:

git submodule add https://github.com/cloudfoundry-community/simple-go-web-app \
  src/gopath/src/github.com/cloudfoundry-community/simple-go-web-app

Create a simple-go-web-app BOSH package, that compiles itself with our golang-1.10-linux language pack.

bosh-gen package simple-go-web-app \
  -d golang-1.10-linux \
  --src gopath

Update packaging/simple-go-web-app/packaging to load the golang-1.10-linux language pack and to go build our application:

set -eux

cd gopath
source /var/vcap/packages/golang-1.10-linux/bosh/compile.env

go build -v github.com/cloudfoundry-community/simple-go-web-app

mkdir $BOSH_INSTALL_TARGET/bin
cp simple-go-web-app $BOSH_INSTALL_TARGET/bin

The source .../compile.env is the second part of magic for language packs.

It does something different for each language pack, and for each base operating system. For the golang-release on linux, it sets up $GOROOT and also sets $GOPATH to the current directory.

So, in our packaging script above, we first change to the base directory of our $GOPATH and then run source /var/vcap/packages/golang-1.10-linux/bosh/compile.env. We are now free to go build to produce our linux binary simple-go-web-app.

The java language pack sets up $JAVA_HOME and $PATH.

Finally, we copy the binary into the $BOSH_INSTALL_TARGET folder. Anything in this folder is part of the compiled BOSH package.

Next, we need a BOSH job to run our packaged simple-go-web-app binary:

bosh-gen job simple-go-web-app -d simple-go-web-app

This command will override some default files in a pre-created jobs/simple-go-web-app directory. That's fine. Say yes to all prompts.

export BOSH_DEPLOYMENT=simple-go-web-app
bosh create-release --force && \
bosh upload-release --rebase && \
bosh deploy manifests/simple-go-web-app.yml -o manifests/operators/dev.yml

Once the packages have been compiled and the simple-go-web-app instance is running, you can ping your web app on the default job port :8080:

$ curl 10.244.0.2:8080
Hello world

Epilogue - CI Pipeline

To flesh out this example BOSH release, I've also added a CI pipeline, which publishes final releases, updates the base manifest manifests/simple-go-web-app.yml with each version.

Screen-Shot-2018-04-04-at-12.12.11-pm

The CI pipeline, as well as, publishing compiled releases to S3, also creates a use-compiled-releases.yml operator file.

To try it out:

bosh -d simple-go-web-app \
  deploy <(curl -L https://raw.githubusercontent.com/cloudfoundry-community/simple-go-web-app-boshrelease/master/manifests/simple-go-web-app.yml) \
  -o <(curl -L https://raw.githubusercontent.com/cloudfoundry-community/simple-go-web-app-boshrelease/master/manifests/operators/use-compiled-releases.yml)