Deploy Custom Spinnaker Builds

The Bill of Materials (BOM)

Spinnaker is composed of many microservices, each of which has their own version. When Spinanker is built, the microservices are tested together to ensure that they interoperate correctly, and then their versions are recorded in a BOM. The BOM also includes information on what commits for each service were built, what repositories they can be downloaded from, and where additional configuration lives. All together, a BOM describes a specific release of Spinnaker.

A BOM has the following structure:

version:                  # the version this corresponds to
timestamp:                # when this version was assembled
services:
  ${SUBCOMPONENT}:        # for each subcomponent
    version:              # the subcomponent version
    commit:               # the commit the version corresponds to
    artifactSources:      # (optional) overrides for where the artifacts are stored
      debianRepository:   # (optional) debian repository storing the built deb
      dockerRegistry:     # (optional) docker registry storing the build image
      gitPrefix:          # (optional) git repository this is stored in
dependencies:
  ${DEPENDENCY}:          # for each required 3rd party service
    version:              # dependency version
artifactSources:
  debianRepository:       # debian repository storing all built debs
  dockerRegistry:         # docker registry storing all built images
  gitPrefix:              # git org all repos are in

For example, here is the BOM for Spinnaker 1.10.1:

You can capture this yourself by running hal version bom 1.10.1 -q -o yaml

version: 1.10.1
timestamp: '2018-10-24 15:31:30'
services:
  echo:
    version: 2.1.0-20181003100130
    commit: d20259cd47acd432670dcbddd8191eabbdbf7a4e
  clouddriver:
    version: 4.0.1-20181024113115
    commit: ad521d622193bd36b90f7c8b3fb453044222cd72
  deck:
    version: 2.5.1-20181018042808
    commit: 9a096f216be8bf36da14f2f5faa15e94e0a407c5
  fiat:
    version: 1.1.0-20181012042808
    commit: b1fd0b386534dd170f61c845ee8cda9a5865de82
  front50:
    version: 0.13.0-20181005212906
    commit: 62da4dc86c663230d897dc8da5ffbbb2c7793bbe
  gate:
    version: 1.2.0-20181016042808
    commit: 0b204b7b4e36819b2f469dd3850dc89b45a50bf8
  igor:
    version: 0.13.0-20181003100130
    commit: a4fd89756144d4b0722dc43ee679b9ae51a75171
  kayenta:
    version: 0.4.0-20180928152808
    commit: 788433f454505e7848d185868ed78d73ac0ef4cd
  orca:
    version: 1.1.0-20181003100130
    commit: bde9d946c68b8305e7ecd48c045a52eaa9b63cbc
  rosco:
    version: 0.8.0-20181003100130
    commit: 2f1a4f856b04971fe0fa04c7d402ee8f03827f61
  defaultArtifact: {}
  monitoring-third-party:
    version: 0.9.0-20180913172809
    commit: 1559f0a03c2c1d88bf07a164e1c9c21a7c5e6af4
  monitoring-daemon:
    version: 0.9.0-20180913172809
    commit: 1559f0a03c2c1d88bf07a164e1c9c21a7c5e6af4
dependencies:
  redis:
    version: 2:2.8.4-2
  consul:
    version: 0.7.5
  vault:
    version: 0.7.0
artifactSources:
  debianRepository: https://dl.bintray.com/spinnaker-releases/debians
  dockerRegistry: gcr.io/spinnaker-marketplace
  gitPrefix: https://github.com/spinnaker

Along with a service’s version, we record canonical configuration for each service at each release. This allows us to ensure that changes in the required configuration of a service are captured with a release, and don’t require user intervention when installing Spinnaker with Halyard (or any other tooling that takes advantage of this configuration). This canonical configuration is captured from each service’s ./halconfig directory in each repository during a build. Here are Clouddriver’s latest default configuration files, as an example.

BOMs can be stored in a number of places, but regardless of the location or storage solution, the required configuration files all share the same directory/naming conventions relative to a ${CONFIG_INPUT_ROOT}.

The conventions are as follows:

${CONFIG_INPUT_ROOT}/
├── boms/
│   └── ${VERSION}.yml            # for each top-level spinnaker version
└── ${SUBCOMPONENT}
    ├── ${DEFAULT_PROFILE}        # when a given version isn't found
    └── ${SUBCOMPONENT_VERSION}/  # for each subcomponent version
        └── ${PROFILE}            # for each profile at this version

Take for example the public gs://halconfig bucket, where all releases are published, looks like (with a lot of omissions):

gs://halconfig/
├── boms/
│   ├── 1.10.1.yml
│   ├── 1.10.0.yml
│   ├── 1.9.5.yml
│   ├── 1.9.4.yml
│   ├── master-latest-unvalidated.yml
│   └── ...
├── clouddriver/
│   ├── clouddriver.yml
│   ├── 4.0.1-20181024113115/
│   │   ├── clouddriver.yml
│   │   ├── clouddriver-caching.yml
│   │   └── clouddriver-ro.yml
│   ├── 4.0.0-20181005152808/
│   │   ├── clouddriver.yml
│   │   ├── clouddriver-caching.yml
│   │   └── clouddriver-ro.yml
│   └── ...
├── deck/
│   ├── 2.5.1-20181018042808/
│   │   └── settings.js
│   ├── 2.5.1-20181018042808/
│   │   └── settings.js
│   └── ...
└── ...

BOMS and Configuration in GCS

By default, all BOMs are stored in a publicly readable GCS bucket, gs://halconfig that Halyard reads from when doing a deployment. This bucket location can be changed by setting the spinnaker.config.input.bucket property in /opt/spinnaker/config/halyard-local.yml. If you supply a private bucket, Halyard will use your application default credentials to authenticate.

Given any GCS bucket, gs://${BUCKET}, the ${CONFIG_INPUT_ROOT} is gs://${BUCKET}.

As a result, a BOM at version ${VERSION} can be found at gs://${BUCKET}/boms/${VERSION}.yml, and a configuration file ${PROFILE} for subcomponent ${SUBCOMPONENT} at version ${SUBCOMPONENT_VERSION} can be found at gs://${BUCKET}/${SUBCOMPONENT}/${SUBCOMPONENT_VERSION}/${PROFILE}.

Disabling GCS reads

You can also completely disable reads from GCS by setting spinnaker.config.input.gcs.enabled: false in /opt/spinanker/config/halyard-local.yml. Be sure to restart the Halyard daemon for this configuration to take effect: hal shutdown && hal.

BOMs and Configuration on your Filesystem

BOMs can also be read from your filesystem. To indicate to Halyard that you want to read a local BOM, prefix the version you want to deploy (${VERSION}) with local:, e.g.

hal config version edit --version local:${VERSION}

In this case, the ${CONFIG_INPUT_ROOT} is ${HALCONFIG_DIR}/.boms. As a result, the BOM will be found under ${HALCONFIG_DIR}/.boms/boms/${VERSION}.yml.

${HALCONFIG_DIR} is typically ~/.hal

At this point, the configuration files for each service will by default be read from GCS, unless you modify the BOM to indicate that they should be sourced locally. This is done by prefixing the subcompent version with local: as well. For example, in ~/.hal/.boms/boms/1.10.1.yml:

version: 1.10.1
timestamp: '2018-10-24 15:31:30'
services:
  echo:
    version: local:2.1.0-20181003100130               # 'local:' here
    commit: d20259cd47acd432670dcbddd8191eabbdbf7a4e
...

Indicates to Halyard to look in ~/.hal/.boms/echo/2.1.0-20181003100130/echo.yml for Echo’s default echo.yml, and then in ~/.hal/.boms/echo/echo.yml as a backup.