Skip to content

Molecule Execution

This document aims to describe how molecule execution goes from commandline to playbook execution. It assumes you have a basic understanding of Molecule scenarios and playbooks.

Molecule CLI

The basic execution starts with shell.py. Molecule uses Click for CLI options, and the base arguments and individual subcommands are registered here.

Subcommands

Each Molecule subcommand is represented by a file in command/. These files contain two important chunks of code: the command class, and a function that defines the commandline arguments for the subcommand. Because Molecule uses Click, the function is decorated with click decorators to define arguments and options. These are all centrally defined in click_cfg.py, so that options can be applied consistently across commands.

The function's job is to take the options passed in from Click and arrange them into a standard format for use by Molecule. These structures are MoleculeArgs, CommandArgs, and ansible_args, for options relating to Molecule run, command execution, and Ansible invocation, respectively. CommandArgs and MoleculeArgs are both nested TypedDicts defined in types.py, while ansible_args is a simple tuple of strings. Those three are passed to command/base.py:execute_cmdline_scenarios() along with the scenario include and exclude lists to actually run the specified scenarios.

Base Command Module

command/base.py has a lot going on, but most of it concerns taking the arguments passed in from the commandline and package them into Scenario objects for execution.

Config Object

The Config object holds the computed configuration for the running molecule scenario, generated from the root Molecule config, the scenario Molecule config, commandline options, and environment variables. This object lives in the Scenario object to provide guidance for scenario execution.

Scenario Object

The Scenario object contains the Config object for the scenario in question, as well as helper properties for things like subcommand sequences which may vary between scenarios. Multiple Scenario objects are contained in a Scenarios object which guides the total runtime of the Molecule execution

Scenarios Object

The Scenarios object contains a sequence of Scenario objects as well as some methods to produce a deterministic order of playbooks to run to execute them all.

The base command module's execute_cmdline_scenarios() function thus generates config objects based on scenario names passed in, creates a Scenarios object with those configs (which internally creates the Scenario objects for individual processing), and then runs those scenarios by passing each Scenario to the execute() method of the relevant subcommand command class.

Back to Subcommands

Each subcommand's command class (inheriting from command.base.Base) has an execute() method that takes the Scenario object and its Config and passes the relevant details for this command to the provisioner, which finally runs the playbook. At this point, all details have been fleshed out: the scenarios to run, the sequence of actions to take on each scenario, all the configuration, and so each command's execute() method is called on each scenario in turn.

Provisioner

The provisioner has a number of methods covering each subcommand, which call the underlying AnsiblePlaybook.execute() method for the appropriate playbook for the scenario. Like Scenarios, these are also wrapped in an AnsiblePlaybooks object, but here instead of a sequence of contained objects, it acts more like a map, ensuring that the correct playbook can easily be called for each action.

It is at this step when the call to ansible-playbook or ansible-navigator is finally run, and results from that are saved to the Scenario that requested it, and then back to the Scenarios object that held the Scenario, and then finally those results can be presented back to the user at the end of execute_cmdline_scenarios() if the user has requested it, at which point the execution is finally complete.