Simplifying our forklift pipeline definitions

Motivation

One of the things I dislike about our current test setup is the fact that we create a new file for every version we test in forklift. This makes creating new pipelines tedious (esp reviewing PRs that add them) and adds a bit of drift where “new stuff” gets added to the newly created pipe, but does not get backported to older ones. And I’d like to change that :wink:

Closes: forklift#806

Userstories

  • As a pipeline author/maintainer I want to
    • add new pipelines without writing much boilerplate ansible
    • adjust existing pipelines easily
    • review only the relevant parts of a newly added pipeline
  • As a developer I want to
    • have a single command to run a pipeline on my workstation that corresponds 100% to what Jenkins runs nightly

The developer one is already implemented today and is only listed here so that we don’t break that experience.

Proposal

Instead of having each pipeline in a separate file, I’d like to introduce a “generic” pipeline for each topic (Foreman, Katello, Katello Upgrade, etc). These pipelines will be version-agnostic in their definition and will load a version-specific file using Ansbile’s vars_files directive. This will allow us to only submit new variable files when we add new pipelines and adjustments to the main pipeline file will benefit all versions instantly.

The invocation of the pipeline will slightly change from ansible-playbook pipeplines/pipeline_<type>_<version>.yml to ansible-playbook pipeplines/pipeline_<type>.yml -e version=<version> and thus remain the “one command rules them all” requirement.

Example

Please have a look at the rework-pipelines branch of my forklift fork for an example implementation of my idea.

1 Like

Big :+1: to DRYing it up. It has always annoyed me. In the past I’ve done a PoC to generate these files but also realized there should be an ansible-native way of doing it.

I’ve also been wondering if we could reuse our config/versions.yaml since it has the version info already. Perhaps we could restructure it as versions/x.y.yaml and make them ansible vars files. We can change the box loader we already have.

Another point I’m wondering about is the installer arguments we define in the pipeline. I’d like to reuse them in the upgrade scenario as well.

Yeah, splitting up config/versions.yaml into config/versions/X.Y.yaml and reformatting these as Ansible vars files is probably a good idea!

Not sure what you mean but the installer arguments. foreman_installer_options_internal_use_only? These are just two lines for the server itself: --disable-system-checks and the password. We only set a huge set on the proxy, and that’s not in the upgrade test (yet).

We also could adjust inventories/vagrant.py to read versions.yaml for us.

I’m not sure how that would look like. How do you know which version to include for a box?

It’d need some hacky hostname parsing, probably. So maybe not the best idea. Just dumping my brain here :slight_smile:

I think this is a fine idea for reducing boilerplate. How will this method handle customization of a pipeline if it needs to occur?

Take this example:

The final testing step in pipeline_katello.yml looks like this:

- hosts: pipeline-katello-{{ katello_version }}-centos7
  become: true
  vars:
    bats_tests:
      - fb-verify-packages.bats
      - fb-test-katello.bats
      - fb-content-katello.bats
      - fb-proxy.bats
      - fb-destroy-organization.bats
      - fb-finish.bats
  vars_files:
    - vars_katello_{{ katello_version }}.yml
  roles:
    - foreman_testing

As vars_files overwrites vars, you could just add bats_tests: <whatever> to vars_katello_<specialversion>.yml and the pipeline for that version would execute a different set of tests.

Does this answer your question?

That answers part of my question. Let’s take a real world example that I know of today. In 3.9 we introduced a foreman_client_repositories role that must be used with 3.9+ but not any version before that. Therefore, the pipeline for 3.9 will look different than 3.8 as well as upgrade pipelines given it requires both role and variable differences. How will this handle that scenario?

For f_client_repositories, I have used a when in the roles block:

    - role: foreman_client_repositories
      when: katello_version in ['3.9', 'nightly']

Another possibility would be to set a foreman_client_repositories_required boolean in the vars file and use it here.

Or make foreman_client_repositories a noop when a Foreman version < 1.20 (or nightly) is passed.

1 Like

I think that is a good way to handle this. Even if we have to add versions to the when blocks it’s still a huge reduction in boiler plate. +1 from me for this.

I think I lean more towards the “make role a NOOP based on version” solution, to be honest. (using https://docs.ansible.com/ansible/2.5/user_guide/playbooks_tests.html#version-comparison)

1 Like

I have now opened PRs against forklift and foreman-infra to track the changes I want to make for this.