Spotlight - Cirrus CLI (with Podman)

Cirrus CLI is a tool for running containerized tasks reproducibly in any environment. Most commonly, Cirrus tasks are used as part of continuous integration workflows but can also be used as part of local development process as a hermetic replacement of helper scripts/Makefiles.

Spotlight - Cirrus CLI (with Podman)
Cirrus CLI is a tool for running containerized tasks reproducibly in any environment. Most commonly, Cirrus tasks are used as part of continuous integration workflows but can also be used as part of local development process as a hermetic replacement of helper scripts/Makefiles.

This is a statement from the makers of Cirrus CLI. You will write some YAML (yep, again) and Cirrus will do the hard work for you. Sounds pretty terrific.

Let's see how it works, how to install and (buzzwords incoming) how to test a small Ansible Playbook in a Podman Container with Cirrus CLI.

Cirrus CLI at a glance

Cirrus CLI allows you to write tasks, that can run on your local machine in the same way they are running on Cirrus CI. This allows to test your code on your local machine, before pushing it. There is no need to set up a local Jenkins or connect to a remote server that may be unavailable. Instead, you can run your compiling, testing and sanity checks in containers locally.

Cirrus supports Docker and Podman to execute the tasks and is very configurable. It can also replace other tooling like makefiles.

Installation

The installation of Cirrus CLI is pretty easy. Since I am on Fedora and Podman is the default container engine, I will stick to this setup. But, Cirrus supports Docker, too. You can also run it on Ubuntu, Debian, Arch Linux or whatever you like. You just need to ensure that you have Docker or Podman installed.

Hint
The guide is tested on Fedora 34 with Podman 3.4.0 and Cirrus CLI 0.66.5.

Podman

In most Fedora installations, you will find Podman already installed. Nevertheless, if it is missing you can install Podman with the below commands.

# Install Podman
$ sudo dnf install podman

Yup, that's it. Done.

Cirrus CLI

Installing Cirrus CLI is very easy, too. You just need to download the binary and copy it to a proper location. According to conventions, the Linux FHS and my personal experience, you should use one of two locations for this.

# Download cirrus
$ wget https://github.com/cirruslabs/cirrus-cli/releases/download/v0.66.5/cirrus-linux-amd64

# Make available for current user only
$ cp cirrus-linux-amd64 ~/.local/bin/cirrus

# Make available for all users of the system
$ sudo cp cirrus-linux-amd64 /usr/local/bin/cirrus

Afterwards, you can check if everything is working with the below command.

# Check cirrus version
$ cirrus --version

Now that this is out of the way. Let's actually do something.

First project

Playing with a test tool requires at least a bit of code. I opted for a very simple Ansible Playbook and a bunch of tests, I want to perform against it. Nothing fancy or special. Just some "first steps".

Example Code (Ansible)

The Ansible example code is a single playbook. You just need to create a directory and a playbook.yml with the below content. There is only one task, which should update the operating system.

---
# playbook.yml

- hosts: "localhost"
  connection: "local"
  
  tasks:
  
    - name: "Update OS"
      ansible.builtin.package:
        name: "*"
        state: "latest"
      become: true
...
playbook.yml

That's already it. But, having valid code does not bring up issues or break our tests... OK, let's introduce some bugs to the playbook.

---
# playbook.yml

- hosts: "localhost"
  connection: "local"

 tasks:

    - name: "Update OS"
      ansible.builtin.packages:
       name: "*"
        state: "latest"
      become: true
...
playbook.yml

Can you already spot all problems? We will find out in a couple of minutes.

Writing the test

I will not dig into too many details for testing Ansible Playbooks for now. But, there are a couple of tests, that are quite common and almost mandatory in my development workflow. Cirrus CLI makes it also very easy to test things.

Let's start with some linting. Linting is something that can be done by many code editors and IDEs already. Linting YAML in general is a good idea. There is so much that can go wrong. So, how does this look like in Cirrus CLI?

---
# .cirrus.yml

yamllint_task:
  name: "Lint Yaml"
  container:
    image: "docker.io/library/python:latest"
  install_script: "pip install yamllint"
  lint_script: "yamllint playbook.yml"
...
.cirrus.yml

The above file may need a bit more explanation. But let's run it first and see the result. Just run the below command.

# Run cirrus
$ cirrus run
❌ 'Lint Yaml' Task 11s
   ✅ Preparing execution environment... 1.8s
   ✅ image pull 2.6s
   ✅ 'install' script 5.9s
   ❌ 'lint' script 1.0s
      yamllint playbook.yml
      playbook.yml
        7:2       error    syntax error: expected <block end>, but found '<block mapping start>' (syntax)
        11:8      error    wrong indentation: expected 9 but found 7  (indentation)
        12:9      error    wrong indentation: expected 7 but found 8  (indentation)
      
      
Error: build failed: task Lint Yaml (0) failed
2021/10/27 01:14:06 build failed: task Lint Yaml (0) failed

Pretty awesome. We are getting our errors displayed. But what happened here? Let's go back to the Cirrus CLI configuration file.

We can see a line:

yamllint_task:

This is how you trigger a new task in cirrus. Every task can be prefixed with something. So, yamllint_task or ansible_task or task are all valid options for the first line.

  name: "Lint Yaml"

The name should be clear. It's just a name.

  container:
    image: "docker.io/library/python:latest"

These 2 lines are more interesting. This way, you will tell Cirrus to download an image. This can be a simple one as in our example or your own image. If the image is already present at the host, it will not be downloaded.

  install_script: "pip install yamllint"
  lint_script: "yamllint playbook.yml"

The script statement tell Cirrus to run a command in the container. Similar to the task statement from above, you can prefix script statements to make them unique. In our example, we want to install yamllint and run yamllint.

That's already it. The first test is done. Now you can fix the code or add more tests.

Adding tests

Adding more tests is pretty easy. You can just make a new task and write your tests similar to our example. Cirrus does not care how many tasks you want to have or how many scripts you need. But, for many test cases, you can find examples.

Examples - Cirrus CI
Cirrus CI is a modern Continuous Integration system built for the era of cloud computing. Cirrus CI supports Linux, Windows, macOS and FreeBSD environments as well as various cloud computing services like Kubernetes, Google Cloud, AWS and Azure.

Furthermore, since the syntax is exactly the same as for Cirrus' hosted service, you can find examples in many Open Source repositories, that may have solved your tests already for you.

Cirrus has a very nice documentation and there are also some articles on the web, that are worth a read.

GitHub - cirruslabs/cirrus-cli: CLI for executing Cirrus tasks locally
CLI for executing Cirrus tasks locally. Contribute to cirruslabs/cirrus-cli development by creating an account on GitHub.
Quick Start - Cirrus CI
Cirrus CI is a modern Continuous Integration system built for the era of cloud computing. Cirrus CI supports Linux, Windows, macOS and FreeBSD environments as well as various cloud computing services like Kubernetes, Google Cloud, AWS and Azure.
Examples - Cirrus CI
Cirrus CI is a modern Continuous Integration system built for the era of cloud computing. Cirrus CI supports Linux, Windows, macOS and FreeBSD environments as well as various cloud computing services like Kubernetes, Google Cloud, AWS and Azure.
How Cirrus CLI uses Podman to achieve rootless builds
Use both Cirrus CLI and Podman to better manage and secure container environments for rootless builds.
Using Cirrus CLI instead of Makefiles for generating gRPC
Cirrus CLI is a tool for running containerized tasks reproducibly in any environment. Most commonly, Cirrus tasks are used as part of continuous integration workflows but Cirrus tasks can also be…

Conclusion

Cirrus CLI brings the power of Cirrus CI to your local machine. You can also use Cirrus CLI on other providers to achieve the same results on Travis CI or GitHub Actions. This enables you to write tests, that are vendor independent.