News

My campaign to produce Shakespeare's Sonnets: A Graphic Novel Adaptation needs your help! Please sign up at https://www.patreon.com/fisherking for access to exclusive content and the opportunity to be a part of the magic!

I'm also producing a podcast discussing the sonnets, available on
industrial curiosity, itunes, spotify, stitcher, tunein and youtube!
For those who prefer reading to listening, the first 25 sonnets have been compiled into a book that is available now on Amazon and the Google Play store.

Friday, 6 August 2021

Bamboo YAML Specs Tips and Tricks (For Fun and Profit)

A cute panda eating bamboo
[UPDATED 2021.09.25 WITH DEPLOYMENT PLAN LEARNINGS]

Introduction

“In for a penny, in for a pound” — that’s our team’s current approach regarding Atlassian’s products. As part of our efforts to code as much of our infrastructure as possible, we’ve recently begun migrating old build plans to Bamboo Specs and, as non-Java devs who don’t want more headaches than are absolutely necessary, we’ve chosen to work with the Bamboo Specs YAML offering.

YAML specs are pretty great. Migrations are assisted by the fact that manually-created plans will show you their YAML translations, so in most cases a simple copy/paste is all you need. I mean, aside from migrating to linked repositories and ensuring they’re configured correctly…

Having said that, Bamboo’s YAML specs are an incomplete product with undocumented critical features, and fail to support a surprising number of standard use cases that one would expect software engineers building software for other software engineers to appreciate the value of. 
They’re still pretty great in spite of that, but overcoming the limitations of incomplete specs definitions and deployment plans is not exactly intuitive. This article attempts to cover some of the missing pieces and suggest some workarounds that we’ve found useful.


Configuring a linked repository for Bamboo Specs

Bamboo Specs require the configuration of a linked repository. Head to settings -> linked repositories (this will require admin permissions) to create or update a linked repository configuration. The two most important steps to be taken here are as follows:
  1. Determine which branch of your repository will be used by Bamboo to read the specs file. Only one branch can be considered the source of truth, my personal recommendation is to make it the development branch.

  2. Two sets of permissions need to be configured correctly in order for Bamboo Specs to be able to do their job:

    a. First, the Bamboo project must give permissions to the linked repository to create and modify the build and deployment plans. Head to the project you want your linked repository to operate in, go to Project Settings, then Bamboo Specs repositories, and add the linked repository.

    b. Under the Bamboo Specs tab of the linked repository, enable both Access all projects and Access all repositories. I’m pretty confident that this requirement is not a security best-practice, but in my experience Bamboo Specs won’t work without them.

Testing Bamboo Specs

When experimenting with changes to the specs, I’ve found it useful to do the following:
  1. Create a testing branch (a feature / bugfix branch).

  2. Change the build and deployment plans’ names and keys and push them to origin so that your changes won’t overwrite the existing plans.

  3. Set the testing branch to be the linked repository’s branch in the General tab and proceed with your changes.

  4. Once you’re happy with the changes and they’ve been reviewed: set the linked repository’s branch back to the development branch, then revert the plan names and keys in the testing branch to the existing plans’.

  5. Delete the test plans manually.

Unexpected Limitations of Bamboo Specs

Plan dependencies

The most glaring omission in the YAML specs is the inability to set up plan dependencies. While we were initially upset by this, we quickly realized that at the end of the day plan dependencies are a nice hack that we shouldn’t really have been relying on in the first place. Bamboo appears to encourage the download of artifacts from matching branches on other build plans, but this can quickly break down into chaos with dependency versioning and management. I warmly recommend using Bamboo artifacts for build debugging and deployment plans exclusively, and proper package repositories for storing and retrieving versioned build artifacts.

When exporting Bamboo Specs from existing plans, build plans and deployment plans are not considered strongly related so you will need to gather and combine the specs from both into a single file. To do this, copy in the deployment plan specs beneath the build plan specs, retaining the --- as separators.
NOTE: the ordering of the sections is important! The build plan definition must be followed by the build plan permissions, then the deployment plan definition(s), with each deployment plan definition followed by a section for the deployment plan's permissions. See the outline at the end of the article for clarity.
An interesting omission is the inability to include unset plan variables. This actually makes sense, as manually maintained plans need some way to ensure that they’re all using the same variable names, but with Bamboo Specs it’s really on you to be consistent and it’s obviously much* easier to search through a single file than it is to hunt for variables across different plan branches via the Bamboo interface.

* infinitely easier

Deployment Plans — sharing build variables and tooling

The principal idea behind a deployment plan is to separate deployment from the build process. Bamboo implements deployment plans as distinct entities with entirely different environments, with the intention that your only interaction with the related build plan is to download your artifacts from it.

For us, this proved problematic as we require shared environment variables and tooling to deploy our builds. To work around this, we required the following mechanisms:
  1. Environment variable injection. Early in our build plan tasks, we prepare an environment variable file in the following format and include values like build versions and git branch names.

    version=1.0.2
    git_branch=feature/example


    WARNING: variable values MUST NOT be surrounded by quotations, as this leads to unpredictable behaviour.


    It is recommended to use the inject namespace for injections. When the scope of the injection is RESULT, the variables will be available to all subsequent build tasks as well as the attached deployment plan. In Bamboo Specs they’ll be available in the form ${bamboo.inject.git_branch} and in inline scripts as $bamboo_inject_git_branch (on Unix agents) or %bamboo_inject_git_branch% (on Windows agents).

    One of my favourite uses of this technique is the ability to name releases automatically based on the build version, with the following line:
    release-naming: ${bamboo.inject.version}

  2. As deployment plans are not really designed to use git directly we have found that we sometimes require non-build folders to be available for deployment, such as documentation. In these cases, we simply zip the desired folders and make them available as artifacts as well.

  3. Running the deployment in a docker container. I find it disconcerting that such an extremely useful feature is undocumented! Deployment plan environments can be configured to run in a docker container just like a build plan, which provides us with all requisite tooling and context.

    DevEnvironment:
      docker:
        image: golang:1.16.6-buster
        docker-run-arguments:
        - --net=host
      tasks:

One plan branch, one deployment plan

Ironically, while deployment plans are supposed to operate independently from build plans, they only really function well when linked to specific build branches. If your intention is to build once, then deploy the build artifacts to multiple stages, you're out of luck!

To enable a deployment plan to use a build's injected variables, the deployment plan must be attached to a specific build plan branch. In our case, we have three git branches of interest (development, release testing and master), so we have to create three individual deployment plans per repository and ensure that the build plan branch keys are immutable.

Bearing in mind that plan branch keys are autoincremented and uneditable, for this to be guaranteed the build plan branch configuration needs to be as follows:

branches:
  create: manually
  delete: never

Putting it all together

The general outline of our YAML specs files is therefore as follows:

---

version: 2

# build plan definition

plan:

  project-key: PROJECTKEY

  key: PLANKEY

  name: Product Name

  ...

branches:

  create: manually

  delete: never

---

version: 2

# build plan permissions

plan:

  key: PROJECTKEY-PLANKEY

plan-permissions:

  ...

---

version: 2

# development deployment plan definition

deployment:

  # NOTE: deployment plan names must be unique

  name: Product Name (Development)

  # NOTE: I recommend setting the repository's default branch to the development, if it's not then follow the Release Testing example below

  source-plan: PROJECTKEY-PLANKEY

release-naming: ${bamboo.inject.version}

...

---

version: 2

# development deployment plan permissions

deployment:

  name: Product Name (Development)

deployment-permissions:

  ...

---

version: 2

# release testing deployment plan definition

deployment:

  name: Product Name (Release Testing)

  # NOTE: plan branch keys autoincrement, branch must be created first

  source-plan: PROJECTKEY-PLANKEY0

release-naming: ${bamboo.inject.version}

...

---

version: 2

# release testing deployment plan permissions

deployment:

  name: Product Name (Release Testing)

deployment-permissions:

  ...

These are the tips and tricks that have helped us overcome our biggest migration challenges so far, I hope they can help others as well. If you have any others that come to mind, or improvements over the above, please let me know in the comments!
...

Originally published at https://therightstuff.medium.com.

No comments:

Post a Comment