We noted in the previous post that there's a trade-off between granting end users control over their downstream projects and the ability to push incremental changes made to the upstream projects; the more control end users have, the harder it is to push updates to many projects.
One way to control how people modify downstream Config as Code projects is to rely on Git based workflows that protect the default branch from direct modification. This requires your team to make all changes in feature branches, and adds checks to the pull request opened against the default branch.
In this post, we look at a practical example of an automated check so our Config as Code project always meets some minimum requirements.
Parsing the OCL language
The Octopus Configuration Language (OCL) is a subset of Hashicorp's HCL language. Octopus automatically converts a Config as Code project to OCL when it persists it in Git.
By parsing the OCL of a feature branch and validating that it meets some minimum requirements, you can build an automated workflow allowing end users to edit a Config as Code project but only allow the default branch to be updated if those changes satisfy your checks.
The sample OCL validation project
Start by creating the
package.json file, which defines your Node.js project. This file lists the OCL parser as a dependency, and also includes the Jest test framework:
The validation logic is defined in the
check.js file. In this example, you define a function called
checkPr that requires the deployment process to have at least one step, for the first step to be called
Manual Intervention, and for it to be of type
Octopus.Manual. The script then defines supporting functions to assist with extracting values from the parsed OCL:
To verify that this code works as expected, you define unit tests in a file called
The test cases reference an example OCL file that passes the required checks:
A second test case references an OCL file with a correctly named step but that is of the wrong type:
And the remaining test references an OCL file that includes a step with the incorrect name:
To test the validation script, ensure you have Node.js installed and then run the following commands:
npm install npm run test
Automating pull request validation
Now you have an automated script to validate your deployment process, the next step is executing this script as part of a pull request.
To make the script available to Git based CI platforms, it's easiest to check the
check.js files into the Config as Code Git repository.
As an example, the following GitHub Actions workflow downloads the script dependencies and executes the
check.js file in response to a pull request being opened or updated:
name: OCL PR Validation on: pull_request: types: [opened, reopened, synchronize] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 with: fetch-depth: '0' - uses: actions/setup-node@v3 - name: Get Dependencies run: npm install shell: bash - name: Validate OCL run: node check.js .octopus/project shell: bash
This workflow, in conjunction with protected branches and branch protection rules, ensures that pull requests are blocked unless the OCL validation script passes successfully.
Most commercial Git platforms operate in much the same way, by executing the Node.js script in response to a pull/merge request and using the result to block or allow the merge to continue.
The reference implementation uses Gitea to host Config as Code projects. Gitea does not provide an execution environment to run scripts like our OCL validator, but it does let you set status checks on pull requests and trigger external services via webhooks when a PR is created. So the reference implementation has a small proxy server that responds to Gitea webhooks and triggers an Octopus runbook. The runbook then runs the OCL validation script and passes the result back to the Gitea pull request via the Gitea REST API:
By parsing and validating the OCL backing a Config as Code enabled project during a pull request, DevOps teams can proactively ensure Octopus projects meet very granular requirements before merging changes into the default branch. This lets DevOps teams enforce requirements like ensuring certain steps are always defined, configuring steps with particular values, or making sure steps appear in a specific order.
This post describes a simple OCL validation script written for Node.js including a number of useful supporting functions for extracting data from the parsed file. Although the example script only implemented a few simple rules, it's easy to extend this example to define any requirements that DevOps teams may wish to enforce with their deployment processes.
We're currently refining our approach to these enterprise patterns, so if you have any suggestions or feedback about the approach described here, please leave a comment on this GitHub issue.