Skip to content

Configuration

Config Class.

Molecule searches the current directory for molecule.yml files by globbing molecule/*/molecule.yml. The files are instantiated into a list of Molecule molecule.config.Config objects, and each Molecule subcommand operates on this list.

The directory in which the molecule.yml resides is the Scenario's directory. Molecule performs most functions within this directory.

The molecule.config.Config object instantiates Dependency, Driver, Platforms, Provisioner, Verifier_, scenario, and State_ references.

Initialize a new config class and returns None.

Args: molecule_file: A string containing the path to the Molecule file to be parsed. args: An optional dict of options, arguments and commands from the CLI. command_args: An optional dict of options passed to the subcommand from the CLI. ansible_args: An optional tuple of arguments provided to the ansible-playbook command.

Source code in src/molecule/config.py
def __init__(  # type: ignore[no-untyped-def]
    self,  # noqa: ANN101
    molecule_file: str,  # pylint: disable=redefined-outer-name
    args={},  # noqa: ANN001, B006
    command_args={},  # noqa: ANN001, B006
    ansible_args=(),  # noqa: ANN001
) -> None:
    """Initialize a new config class and returns None.

    Args:
        molecule_file: A string containing the path to the Molecule file to be parsed.
        args: An optional dict of options, arguments and commands from the CLI.
        command_args: An optional dict of options passed to the subcommand from the CLI.
        ansible_args: An optional tuple of arguments provided to the `ansible-playbook` command.
    """
    self.molecule_file = molecule_file
    self.args = args
    self.command_args = command_args
    self.ansible_args = ansible_args
    self.config = self._get_config()
    self._action = None
    self._run_uuid = str(uuid4())
    self.project_directory = os.getenv(
        "MOLECULE_PROJECT_DIRECTORY",
        os.getcwd(),  # noqa: PTH109
    )
    self.runtime = app.runtime
    self.scenario_path = Path(molecule_file).parent

ansible_collections_path property

Return collection path variable for current version of Ansible.

Prerun

To help Ansible find used modules and roles, molecule will perform a prerun set of actions. These involve installing dependencies from requirements.yml specified at the project level, installing a standalone role or a collection. The destination is project_dir/.cache and the code itself was reused from ansible-lint, which has to do the same actions. (Note: ansible-lint is not included with molecule.)

This assures that when you include a role inside molecule playbooks, Ansible will be able to find that role and that the include is exactly the same as the one you are expecting to use in production.

If for some reason the prerun action does not suit your needs, you can still disable it by adding prerun: false inside the configuration file.

Keep in mind that you can add this value to the .config/molecule/config.yml file, in your $HOME or at the root of your project, in order to avoid adding it to each scenario.

Role name check

By default, Molecule will check whether the role name follows the name standard. If not, it will raise an error.

If the computed fully qualified role name does not follow current galaxy requirements, you can ignore it by adding role_name_check:1 inside the configuration file.

It is strongly recommended to follow the name standard of namespace and role. A computed fully qualified role name may further contain the dot character.

Variable Substitution

Configuration options may contain environment variables.

For example, suppose the shell contains VERIFIER_NAME=testinfra and the following molecule.yml is supplied.

    verifier:
      - name: ${VERIFIER_NAME}

Molecule will substitute $VERIFIER_NAME with the value of the VERIFIER_NAME environment variable.

Warning

If an environment variable is not set, Molecule substitutes with an empty string.

Both $VARIABLE and ${VARIABLE} syntax are supported. Extended shell-style features, such as ${VARIABLE-default} and ${VARIABLE:-default} are also supported. Even the default as another environment variable is supported like ${VARIABLE-$DEFAULT} or ${VARIABLE:-$DEFAULT}. An empty string is returned when both variables are undefined.

If a literal dollar sign is needed in a configuration, use a double dollar sign ($$).

Molecule will substitute special MOLECULE_ environment variables defined in molecule.yml.

Note

Remember, the MOLECULE_ namespace is reserved for Molecule. Do not prefix your own variables with MOLECULE_.

A file may be placed in the root of the project as .env.yml, and Molecule will read variables when rendering molecule.yml. See command usage.

Construct Interpolator.

Source code in src/molecule/interpolation.py
def __init__(
    self,  # noqa: ANN101
    templater: type["TemplateWithDefaults"],
    mapping: MutableMapping,  # type: ignore[type-arg]
) -> None:
    """Construct Interpolator."""
    self.templater = templater
    self.mapping = mapping

Following are the environment variables available in molecule.yml:

MOLECULE_DEBUG

If debug is turned on or off

MOLECULE_FILE

Path to molecule config file, usually ~/.cache/molecule/<role-name>/<scenario-name>/molecule.yml

MOLECULE_ENV_FILE

Path to molecule environment file, usually <role_path>/.env.yml

MOLECULE_STATE_FILE

The path to molecule state file contains the state of the instances (created, converged, etc.). Usually ~/.cache/molecule/<role-name>/<scenario-name>/state.yml

MOLECULE_INVENTORY_FILE

Path to generated inventory file, usually ~/.cache/molecule/<role-name>/<scenario-name>/inventory/ansible_inventory.yml

MOLECULE_EPHEMERAL_DIRECTORY

Path to generated directory, usually ~/.cache/molecule/<role-name>/<scenario-name>

MOLECULE_SCENARIO_DIRECTORY

Path to scenario directory, usually <role_path>/molecule/<scenario-name>

MOLECULE_PROJECT_DIRECTORY

Path to your project (role) directory, usually <role_path>

MOLECULE_INSTANCE_CONFIG

Path to the instance config file, contains instance name, connection, user, port, etc. (populated from driver). Usually ~/.cache/molecule/<role-name>/<scenario-name>/instance_config.yml

MOLECULE_DEPENDENCY_NAME

Dependency type name, usually 'galaxy'

MOLECULE_DRIVER_NAME

Name of the molecule scenario driver

MOLECULE_PROVISIONER_NAME

Name of the provisioner tool (usually 'ansible')

MOLECULE_REPORT

Name of HTML file where to dump execution report.

MOLECULE_SCENARIO_NAME

Name of the scenario

MOLECULE_VERBOSITY

Determine Ansible verbosity level.

MOLECULE_VERIFIER_NAME

Name of the verifier tool (usually 'ansible')

MOLECULE_VERIFIER_TEST_DIRECTORY

Path of the directory that contains verifier tests, usually <role_path>/<scenario-name>/<verifier-name>

Dependency

Testing roles may rely upon additional dependencies. Molecule handles managing these dependencies by invoking configurable dependency managers.

Ansible Galaxy

Bases: Base

Galaxy is the default dependency manager.

From v6.0.0, the dependencies are installed in the directory that defined in ansible configuration. The default installation directory is DEFAULT_ROLES_PATH(ANSIBLE_HOME). If two versions of the same dependency is required, there is a conflict if the default installation directory is used because both are tried to be installed in one directory.

Additional options can be passed to ansible-galaxy install through the options dict. Any option set in this section will override the defaults.

The role-file and requirements-file search path is <role-name> directory. The default value for role-file is requirements.yml, and the default value for requirements-file is collections.yml.

  1. If they are not defined in options, molecule will find them from the <role-name> directory, e.g. <role-name>/requirements.yml and <role-name>/collections.yml
  2. If they are defined in options, molecule will find them from <role-name>/<the-value-of-role-file> and <role-name>/<the-value-of-requirements-file>.

Note

Molecule will remove any options matching '^[v]+$', and pass -vvv to the underlying ansible-galaxy command when executing molecule --debug.

    dependency:
      name: galaxy
      options:
        ignore-certs: True
        ignore-errors: True
        role-file: requirements.yml
        requirements-file: collections.yml

Use "role-file" if you have roles only. Use the "requirements-file" if you need to install collections. Note that, with Ansible Galaxy's collections support, you can now combine the two lists into a single requirement if your file looks like this

    roles:
      - dep.role1
      - dep.role2
    collections:
      - ns.collection
      - ns2.collection2

If you want to combine them, then just point your role-file and requirements-file to the same path. This is not done by default because older role-file only required a list of roles, while the collections must be under the collections: key within the file and pointing both to the same file by default could break existing code.

The dependency manager can be disabled by setting enabled to False.

    dependency:
      name: galaxy
      enabled: False

Environment variables can be passed to the dependency.

    dependency:
      name: galaxy
      env:
        FOO: bar

Construct AnsibleGalaxy.

Source code in src/molecule/dependency/ansible_galaxy/__init__.py
def __init__(self, config) -> None:  # type: ignore[no-untyped-def]  # noqa: ANN001, ANN101
    """Construct AnsibleGalaxy."""
    super().__init__(config)
    self.invocations = [Roles(config), Collections(config)]

Shell

Bases: Base

Shell is an alternate dependency manager.

It is intended to run a command in situations where Ansible Galaxy_ don't suffice.

The command to execute is required, and is relative to Molecule's project directory when referencing a script not in $PATH.

Note

Unlike the other dependency managers, options are ignored and not passed to shell. Additional flags/subcommands should simply be added to the command.

    dependency:
      name: shell
      command: path/to/command --flag1 subcommand --flag2

The dependency manager can be disabled by setting enabled to False.

    dependency:
      name: shell
      command: path/to/command --flag1 subcommand --flag2
      enabled: False
Environment variables can be passed to the dependency.

    dependency:
      name: shell
      command: path/to/command --flag1 subcommand --flag2
      env:
        FOO: bar

Construct Shell.

Source code in src/molecule/dependency/shell.py
def __init__(self, config) -> None:  # type: ignore[no-untyped-def]  # noqa: ANN001, ANN101
    """Construct Shell."""
    super().__init__(config)
    self._sh_command = None

bake()

Bake a shell command so it's ready to execute.

Source code in src/molecule/dependency/shell.py
def bake(self) -> None:  # noqa: ANN101
    """Bake a ``shell`` command so it's ready to execute."""
    self._sh_command = self.command

Driver

Molecule uses Ansible to manage instances to operate on. Molecule supports any provider Ansible supports. This work is offloaded to the provisioner.

The driver's name is specified in molecule.yml, and can be overridden on the command line. Molecule will remember the last successful driver used, and continue to use the driver for all subsequent subcommands, or until the instances are destroyed by Molecule.

Warning

The verifier must support the Ansible provider for proper Molecule integration.

The driver's python package requires installation.

Delegated

Bases: Driver

The class responsible for managing default instances.

Delegated is the default driver used in Molecule.

Under this driver, it is the developers responsibility to implement the create and destroy playbooks. Managed is the default behavior of all drivers.

    driver:
      name: de

However, the developer must adhere to the instance-config API. The developer's create playbook must provide the following instance-config data, and the developer's destroy playbook must reset the instance-config.

    - address: ssh_endpoint
      identity_file: ssh_identity_file  # mutually exclusive with password
      instance: instance_name
      port: ssh_port_as_string
      user: ssh_user
      shell_type: sh
      password: ssh_password  # mutually exclusive with identity_file
      become_method: valid_ansible_become_method  # optional
      become_pass: password_if_required  # optional

    - address: winrm_endpoint
      instance: instance_name
      connection: 'winrm'
      port: winrm_port_as_string
      user: winrm_user
      password: winrm_password
      shell_type: powershell
      winrm_transport: ntlm/credssp/kerberos
      winrm_cert_pem: <path to the credssp public certificate key>
      winrm_cert_key_pem: <path to the credssp private certificate key>
      winrm_server_cert_validation: validate/ignore

This article covers how to configure and use WinRM with Ansible: https://docs.ansible.com/ansible/latest/user_guide/windows_winrm.html

Molecule can also skip the provisioning/deprovisioning steps. It is the developers responsibility to manage the instances, and properly configure Molecule to connect to said instances.

    driver:
      name: default
      options:
        managed: False
        login_cmd_template: 'docker exec -ti {instance} bash'
        ansible_connection_options:
          ansible_connection: docker
    platforms:
      - name: instance-docker
    $ docker run \
        -d \
        --name instance-docker \
        --hostname instance-docker \
        -it molecule_local/ubuntu:latest sleep infinity & wait

Use Molecule with delegated instances, which are accessible over ssh.

Note

It is the developer's responsibility to configure the ssh config file.

    driver:
      name: default
      options:
        managed: False
        login_cmd_template: 'ssh {instance} -F /tmp/ssh-config'
        ansible_connection_options:
          ansible_connection: ssh
          ansible_ssh_common_args: '-F /path/to/ssh-config'
    platforms:
      - name: instance

Provide the files Molecule will preserve post destroy action.

    driver:
      name: default
      safe_files:
        - foo
And in order to use localhost as molecule's target:

    driver:
      name: default
      options:
        managed: False
        ansible_connection_options:
          ansible_connection: local

Construct Delegated.

Source code in src/molecule/driver/delegated.py
def __init__(self, config=None) -> None:  # type: ignore[no-untyped-def]  # noqa: ANN001, ANN101
    """Construct Delegated."""
    super().__init__(config)
    self._name = "default"

Platforms

Platforms define the instances to be tested, and the groups to which the instances belong.

    platforms:
      - name: instance-1

Multiple instances can be provided.

    platforms:
      - name: instance-1
      - name: instance-2

Mapping instances to groups. These groups will be used by the Provisioner_ for orchestration purposes.

    platforms:
      - name: instance-1
        groups:
          - group1
          - group2

Children allow the creation of groups of groups.

    platforms:
      - name: instance-1
        groups:
          - group1
          - group2
        children:
          - child_group1

Initialize a new platform class and returns None.

Args: config: An instance of a Molecule config. parallelize_platforms: Parallel mode. Default is False. platform_name: One platform to target only, defaults to None.

Source code in src/molecule/platforms.py
def __init__(self, config, parallelize_platforms=False, platform_name=None) -> None:  # type: ignore[no-untyped-def]  # noqa: ANN001, ANN101, FBT002
    """Initialize a new platform class and returns None.

    Args:
        config: An instance of a Molecule config.
        parallelize_platforms: Parallel mode. Default is False.
        platform_name: One platform to target only, defaults to None.
    """
    if platform_name:
        config.config["platforms"] = util._filter_platforms(  # type: ignore[no-untyped-call]  # noqa: SLF001
            config.config,
            platform_name,
        )
    if parallelize_platforms:
        config.config["platforms"] = util._parallelize_platforms(  # type: ignore[no-untyped-call]  # noqa: SLF001
            config.config,
            config._run_uuid,  # noqa: SLF001
        )
    self._config = config

Provisioner

Molecule handles provisioning and converging the role.

Ansible

Bases: Base

Ansible is the default provisioner. No other provisioner will be supported.

Molecule's provisioner manages the instances lifecycle. However, the user must provide the create, destroy, and converge playbooks. Molecule's init subcommand will provide the necessary files for convenience.

Molecule will skip tasks which are tagged with either molecule-notest or notest. With the tag molecule-idempotence-notest tasks are only skipped during the idempotence action step.

Warning

Reserve the create and destroy playbooks for provisioning. Do not attempt to gather facts or perform operations on the provisioned nodes inside these playbooks. Due to the gymnastics necessary to sync state between Ansible and Molecule, it is best to perform these tasks in the prepare or converge playbooks.

It is the developers responsibility to properly map the modules' fact data into the instance_conf_dict fact in the create playbook. This allows Molecule to properly configure Ansible inventory.

Additional options can be passed to ansible-playbook through the options dict. Any option set in this section will override the defaults.

Note

Options do not affect the create and destroy actions.

Note

Molecule will remove any options matching ^[v]+$, and pass -vvv to the underlying ansible-playbook command when executing molecule --debug.

Molecule will silence log output, unless invoked with the --debug flag. However, this results in quite a bit of output. To enable Ansible log output, add the following to the provisioner section of molecule.yml.

    provisioner:
      name: ansible
      log: True

The create/destroy playbooks for Docker and Podman are bundled with Molecule. These playbooks have a clean API from molecule.yml, and are the most commonly used. The bundled playbooks can still be overridden.

The playbook loading order is:

  1. provisioner.playbooks.$driver_name.$action
  2. provisioner.playbooks.$action
  3. bundled_playbook.$driver_name.$action
    provisioner:
      name: ansible
      options:
        vvv: True
      playbooks:
        create: create.yml
        converge: converge.yml
        destroy: destroy.yml

Share playbooks between roles.

    provisioner:
      name: ansible
      playbooks:
        create: ../default/create.yml
        destroy: ../default/destroy.yml
        converge: converge.yml

Multiple driver playbooks. In some situations a developer may choose to test the same role against different backends. Molecule will choose driver specific create/destroy playbooks, if the determined driver has a key in the playbooks section of the provisioner's dict.

Note

If the determined driver has a key in the playbooks dict, Molecule will use this dict to resolve all provisioning playbooks (create/destroy).

    provisioner:
      name: ansible
      playbooks:
        docker:
          create: create.yml
          destroy: destroy.yml
        create: create.yml
        destroy: destroy.yml
        converge: converge.yml

Note

Paths in this section are converted to absolute paths, where the relative parent is the $scenario_directory.

The side effect playbook executes actions which produce side effects to the instances(s). Intended to test HA failover scenarios or the like. It is not enabled by default. Add the following to the provisioner's playbooks section to enable.

    provisioner:
      name: ansible
      playbooks:
        side_effect: side_effect.yml

Note

This feature should be considered experimental.

The prepare playbook executes actions which bring the system to a given state prior to converge. It is executed after create, and only once for the duration of the instances life.

This can be used to bring instances into a particular state, prior to testing.

    provisioner:
      name: ansible
      playbooks:
        prepare: prepare.yml

The cleanup playbook is for cleaning up test infrastructure that may not be present on the instance that will be destroyed. The primary use-case is for "cleaning up" changes that were made outside of Molecule's test environment. For example, remote database connections or user accounts. Intended to be used in conjunction with prepare to modify external resources when required.

The cleanup step is executed directly before every destroy step. Just like the destroy step, it will be run twice. An initial clean before converge and then a clean before the last destroy step. This means that the cleanup playbook must handle failures to cleanup resources which have not been created yet.

Add the following to the provisioner's playbooks section to enable.

    provisioner:
      name: ansible
      playbooks:
        cleanup: cleanup.yml

Note

This feature should be considered experimental.

Environment variables. Molecule does its best to handle common Ansible paths. The defaults are as follows.

::

ANSIBLE_ROLES_PATH:
  $runtime_cache_dir/roles:$ephemeral_directory/roles/:$project_directory/../:~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles
ANSIBLE_LIBRARY:
  $ephemeral_directory/modules/:$project_directory/library/:~/.ansible/plugins/modules:/usr/share/ansible/plugins/modules
ANSIBLE_FILTER_PLUGINS:
  $ephemeral_directory/plugins/filter/:$project_directory/filter/plugins/:~/.ansible/plugins/filter:/usr/share/ansible/plugins/modules

Environment variables can be passed to the provisioner. Variables in this section which match the names above will be appended to the above defaults, and converted to absolute paths, where the relative parent is the $scenario_directory.

Note

Paths in this section are converted to absolute paths, where the relative parent is the $scenario_directory.

    provisioner:
      name: ansible
      env:
        FOO: bar

Modifying ansible.cfg.

    provisioner:
      name: ansible
      config_options:
        defaults:
          fact_caching: jsonfile
        ssh_connection:
          scp_if_ssh: True

Note

The following keys are disallowed to prevent Molecule from improperly functioning. They can be specified through the provisioner's env setting described above, with the exception of the privilege_escalation.

    provisioner:
      name: ansible
      config_options:
        defaults:
          library: /path/to/library
          filter_plugins: /path/to/filter_plugins
        privilege_escalation: {}

Roles which require host/groups to have certain variables set. Molecule uses the same variables defined in a playbook syntax as Ansible.

    provisioner:
      name: ansible
      inventory:
        group_vars:
          all:
            bar: foo
          group1:
            foo: bar
          group2:
            foo: bar
            baz:
              qux: zzyzx
        host_vars:
          group1-01:
            foo: baz

Molecule automatically generates the inventory based on the hosts defined under Platforms_. Using the hosts key allows to add extra hosts to the inventory that are not managed by Molecule.

A typical use case is if you want to access some variables from another host in the inventory (using hostvars) without creating it.

Note

The content of hosts should follow the YAML based inventory syntax: start with the all group and have hosts/vars/children entries.

    provisioner:
      name: ansible
      inventory:
        hosts:
          all:
            hosts:
              extra_host:
                foo: hello

Note

The extra hosts added to the inventory using this key won't be created/destroyed by Molecule. It is the developers responsibility to target the proper hosts in the playbook. Only the hosts defined under Platforms_ should be targeted instead of all.

An alternative to the above is symlinking. Molecule creates symlinks to the specified directory in the inventory directory. This allows ansible to converge utilizing its built in host/group_vars resolution. These two forms of inventory management are mutually exclusive.

Like above, it is possible to pass an additional inventory file (or even dynamic inventory script), using the hosts key. Ansible_ will automatically merge this inventory with the one generated by molecule. This can be useful if you want to define extra hosts that are not managed by Molecule.

Note

Again, it is the developers responsibility to target the proper hosts in the playbook. Only the hosts defined under Platforms_ should be targeted instead of all.

Note

The source directory linking is relative to the scenario's directory.

The only valid keys are hosts, group_vars and host_vars. Molecule's schema validator will enforce this.

    provisioner:
      name: ansible
      inventory:
        links:
          hosts: ../../../inventory/hosts
          group_vars: ../../../inventory/group_vars/
          host_vars: ../../../inventory/host_vars/

Note

You can use either hosts/group_vars/host_vars sections of inventory OR links. If you use both, links will win.

    provisioner:
      name: ansible
      hosts:
        all:
          hosts:
            ignored:
               important: this host is ignored
      inventory:
        links:
          hosts: ../../../inventory/hosts

Override connection options:

    provisioner:
      name: ansible
      connection_options:
        ansible_ssh_user: foo
        ansible_ssh_common_args: -o IdentitiesOnly=no

Override tags:

    provisioner:
    name: ansible
    config_options:
      tags:
        run: tag1,tag2,tag3

A typical use case is if you want to use tags within a scenario. Don't forget to add a tag always in converge.yml as below.

    ---
    - name: Converge
      hosts: all
      tasks:
        - name: "Include acme.my_role_name"
          ansible.builtin.include_role:
            name: "acme.my_role_name"
          tags:
            - always

.. _variables defined in a playbook: https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#defining-variables-in-a-playbook

Add arguments to ansible-playbook when running converge:

    provisioner:
      name: ansible
      ansible_args:
        - --inventory=mygroups.yml
        - --limit=host1,host2

Initialize a new ansible class and returns None.

Args: config: An instance of a Molecule config.

Source code in src/molecule/provisioner/ansible.py
def __init__(self, config) -> None:  # type: ignore[no-untyped-def]  # pylint: disable=useless-parent-delegation  # noqa: ANN001, ANN101
    """Initialize a new ansible class and returns None.

    Args:
        config: An instance of a Molecule config.
    """
    super().__init__(config)

default_config_options: dict[str, Any] property

Provide Default options to construct ansible.cfg and returns a dict.

inventory property

Create an inventory structure and returns a dict.

    ungrouped:
      vars:
        foo: bar
      hosts:
        instance-1:
        instance-2:
      children:
        $child_group_name:
          hosts:
            instance-1:
            instance-2:
    $group_name:
      hosts:
        instance-1:
          ansible_connection: docker
        instance-2:
          ansible_connection: docker

check()

Execute ansible-playbook against the converge playbook with the --check flag.

Source code in src/molecule/provisioner/ansible.py
def check(self):  # type: ignore[no-untyped-def]  # noqa: ANN101, ANN201
    """Execute ``ansible-playbook`` against the converge playbook with the ``--check`` flag."""
    pb = self._get_ansible_playbook(self.playbooks.converge)  # type: ignore[no-untyped-call]
    pb.add_cli_arg("check", True)  # noqa: FBT003
    pb.execute()

cleanup()

Execute ansible-playbook against the cleanup playbook and returns None.

Source code in src/molecule/provisioner/ansible.py
def cleanup(self):  # type: ignore[no-untyped-def]  # noqa: ANN101, ANN201
    """Execute `ansible-playbook` against the cleanup playbook and returns None."""
    pb = self._get_ansible_playbook(self.playbooks.cleanup)  # type: ignore[no-untyped-call]
    pb.execute()

converge(playbook=None, **kwargs)

Execute ansible-playbook against the converge playbook. unless specified otherwise.

Args: playbook: An optional string containing an absolute path to a playbook. kwargs: An optional keyword arguments.

Returns: str: The output from the ansible-playbook command.

Source code in src/molecule/provisioner/ansible.py
def converge(self, playbook=None, **kwargs):  # type: ignore[no-untyped-def]  # noqa: ANN001, ANN003, ANN101, ANN201
    """Execute ``ansible-playbook`` against the converge playbook. unless specified otherwise.

    Args:
        playbook: An optional string containing an absolute path to a playbook.
        kwargs: An optional keyword arguments.

    Returns:
        str: The output from the ``ansible-playbook`` command.
    """
    pb = self._get_ansible_playbook(playbook or self.playbooks.converge, **kwargs)  # type: ignore[no-untyped-call]

    return pb.execute()

create()

Execute ansible-playbook against the create playbook and returns None.

Source code in src/molecule/provisioner/ansible.py
def create(self):  # type: ignore[no-untyped-def]  # noqa: ANN101, ANN201
    """Execute ``ansible-playbook`` against the create playbook and returns None."""
    pb = self._get_ansible_playbook(self.playbooks.create)  # type: ignore[no-untyped-call]
    pb.execute()

destroy()

Execute ansible-playbook against the destroy playbook and returns None.

Source code in src/molecule/provisioner/ansible.py
def destroy(self):  # type: ignore[no-untyped-def]  # noqa: ANN101, ANN201
    """Execute ``ansible-playbook`` against the destroy playbook and returns None."""
    pb = self._get_ansible_playbook(self.playbooks.destroy)  # type: ignore[no-untyped-call]
    pb.execute()

manage_inventory()

Manage inventory for Ansible and returns None.

Source code in src/molecule/provisioner/ansible.py
def manage_inventory(self):  # type: ignore[no-untyped-def]  # noqa: ANN101, ANN201
    """Manage inventory for Ansible and returns None."""
    self._write_inventory()  # type: ignore[no-untyped-call]
    self._remove_vars()  # type: ignore[no-untyped-call]
    if not self.links:
        self._add_or_update_vars()  # type: ignore[no-untyped-call]
    else:
        self._link_or_update_vars()  # type: ignore[no-untyped-call]

prepare()

Execute ansible-playbook against the prepare playbook and returns None.

Source code in src/molecule/provisioner/ansible.py
def prepare(self):  # type: ignore[no-untyped-def]  # noqa: ANN101, ANN201
    """Execute ``ansible-playbook`` against the prepare playbook and returns None."""
    pb = self._get_ansible_playbook(self.playbooks.prepare)  # type: ignore[no-untyped-call]
    pb.execute()

side_effect(action_args=None)

Execute ansible-playbook against the side_effect playbook and returns None.

Source code in src/molecule/provisioner/ansible.py
def side_effect(self, action_args=None):  # type: ignore[no-untyped-def]  # noqa: ANN001, ANN101, ANN201
    """Execute ``ansible-playbook`` against the side_effect playbook and returns None."""
    if action_args:
        playbooks = [
            self._get_ansible_playbook(self._config.provisioner.abs_path(playbook))  # type: ignore[no-untyped-call]
            for playbook in action_args
        ]
    else:
        playbooks = [self._get_ansible_playbook(self.playbooks.side_effect)]  # type: ignore[no-untyped-call]
    for pb in playbooks:
        pb.execute()

syntax()

Execute ansible-playbook against the converge playbook with the -syntax-check flag.

Source code in src/molecule/provisioner/ansible.py
def syntax(self):  # type: ignore[no-untyped-def]  # noqa: ANN101, ANN201
    """Execute `ansible-playbook` against the converge playbook with the -syntax-check flag."""
    pb = self._get_ansible_playbook(self.playbooks.converge)  # type: ignore[no-untyped-call]
    pb.add_cli_arg("syntax-check", True)  # noqa: FBT003
    pb.execute()

verify(action_args=None)

Execute ansible-playbook against the verify playbook and returns None.

Source code in src/molecule/provisioner/ansible.py
def verify(self, action_args=None):  # type: ignore[no-untyped-def]  # noqa: ANN001, ANN101, ANN201
    """Execute ``ansible-playbook`` against the verify playbook and returns None."""
    if action_args:
        playbooks = [self._config.provisioner.abs_path(playbook) for playbook in action_args]
    else:
        playbooks = [self.playbooks.verify]
    if not playbooks:
        LOG.warning("Skipping, verify playbook not configured.")
        return
    for playbook in playbooks:
        # Get ansible playbooks for `verify` instead of `provision`
        pb = self._get_ansible_playbook(playbook, verify=True)  # type: ignore[no-untyped-call]
        pb.execute()

write_config()

Write the provisioner's config file to disk and returns None.

Source code in src/molecule/provisioner/ansible.py
def write_config(self):  # type: ignore[no-untyped-def]  # noqa: ANN101, ANN201
    """Write the provisioner's config file to disk and returns None."""
    template = util.render_template(  # type: ignore[no-untyped-call]
        self._get_config_template(),  # type: ignore[no-untyped-call]
        config_options=self.config_options,
    )
    util.write_file(self.config_file, template)

Scenario

Molecule treats scenarios as a first-class citizens, with a top-level configuration syntax.

A scenario allows Molecule to test a role in a particular way, this is a fundamental change from Molecule v1.

A scenario is a self-contained directory containing everything necessary for testing the role in a particular way. The default scenario is named default, and every role should contain a default scenario.

Unless mentioned explicitly, the scenario name will be the directory name hosting the files.

Any option set in this section will override the defaults.

scenario:
  create_sequence:
    - dependency
    - create
    - prepare
  check_sequence:
    - dependency
    - cleanup
    - destroy
    - create
    - prepare
    - converge
    - check
    - destroy
  converge_sequence:
    - dependency
    - create
    - prepare
    - converge
  destroy_sequence:
    - dependency
    - cleanup
    - destroy
  test_sequence:
    - dependency
    - cleanup
    - destroy
    - syntax
    - create
    - prepare
    - converge
    - idempotence
    - side_effect
    - verify
    - cleanup
    - destroy

Advanced testing

If needed, Molecule can run multiple side effects and tests within a scenario. This allows to perform advanced testing for stateful software under role/playbook management. Actions side_effect and verify can take optional arguments to change the playbook/test they execute.

Example of test sequence with multiple side effects and tests:

test_sequence:
  - converge
  - side_effect reboot.yaml
  - verify after_reboot/
  - side_effect alter_configs.yaml
  - converge
  - verify other_test1.py other_test2.py
  - side_effect
  - verify

side_effect without an argument is executing the usual side_effect configured in provisioner.playbooks section of molecule.yml.

side_effect can have one or more arguments (separated by spaces) which is a playbook (playbooks) to execute. If the argument for side_effect is present, it's executed instead. The path to the playbook is relative to the molecule.yml location. Normal side effect settings (from provisioner.playbooks) are ignored for action with argument.

verify without an argument is executing the usual tests configured in the verifier section of molecule.yml.

If one or more arguments (separated by spaces) are present, each argument is treated as a test name (file or directory) to pass to the verifier (either Ansible or Testinfra). The kind of verifier is set in the verifier section of molecule.yml and is applied to all verify actions in the scenario.

The path to tests is relative to the molecule.yml file location. The additional_files_or_dirs setting for the verifier is ignored if the verify action is provided with an argument.

Multiple side_effect and verify actions can be used to create a combination of playbooks and tests, for example, for end-to-end playbook testing.

Additional converge and idempotence actions can be used multiple times:

test_sequence:
  - converge
  - idempotence
  - side_effect
  - verify
  - converge
  - idempotence
  - side_effect effect2.yml
  - converge
  - idempotence
  - verify test2/
  - side_effect effect3.yml
  - verify test3/
  - idempotence

Verifier

Molecule handles role testing by invoking configurable verifiers.

Ansible

Bases: Verifier

Ansible_ is the default test verifier.

Molecule executes a playbook (verify.yml) located in the role's scenario.directory.

    verifier:
      name: ansible

The testing can be disabled by setting enabled to False.

    verifier:
      name: ansible
      enabled: False

Environment variables can be passed to the verifier.

    verifier:
      name: ansible
      env:
        FOO: bar

Initialize code for all :ref:Verifier classes.

Args: config: An instance of a Molecule config.

Source code in src/molecule/verifier/base.py
def __init__(self, config=None) -> None:  # type: ignore[no-untyped-def]  # noqa: ANN001, ANN101
    """Initialize code for all :ref:`Verifier` classes.

    Args:
        config: An instance of a Molecule config.
    """
    self._config = config

Testinfra

Bases: Verifier

Testinfra_ is no longer the default test verifier since version 3.0.

Additional options can be passed to testinfra through the options dict. Any option set in this section will override the defaults.

Note

Molecule will remove any options matching '^[v]+$', and pass -vvv to the underlying pytest command when executing molecule --debug.

    verifier:
      name: testinfra
      options:
        n: 1

The testing can be disabled by setting enabled to False.

    verifier:
      name: testinfra
      enabled: False

Environment variables can be passed to the verifier.

    verifier:
      name: testinfra
      env:
        FOO: bar

Change path to the test directory.

    verifier:
      name: testinfra
      directory: /foo/bar/

Additional tests from another file or directory relative to the scenario's tests directory (supports regexp).

    verifier:
      name: testinfra
      additional_files_or_dirs:
        - ../path/to/test_1.py
        - ../path/to/test_2.py
        - ../path/to/directory/*
.. _Testinfra: https://testinfra.readthedocs.io

Set up the requirements to execute testinfra and returns None.

Args: config: An instance of a Molecule config.

Source code in src/molecule/verifier/testinfra.py
def __init__(self, config=None) -> None:  # type: ignore[no-untyped-def]  # noqa: ANN001, ANN101
    """Set up the requirements to execute ``testinfra`` and returns None.

    Args:
        config: An instance of a Molecule config.
    """
    super().__init__(config)
    self._testinfra_command = None
    self._tests = []  # type: ignore[var-annotated]

bake()

Bake a testinfra command so it's ready to execute and returns None.

Source code in src/molecule/verifier/testinfra.py
def bake(self):  # type: ignore[no-untyped-def]  # noqa: ANN101, ANN201
    """Bake a ``testinfra`` command so it's ready to execute and returns None."""
    options = self.options
    verbose_flag = util.verbose_flag(options)  # type: ignore[no-untyped-call]
    args = verbose_flag

    self._testinfra_command = [  # type: ignore[assignment]
        "pytest",
        *util.dict2args(options),
        *self._tests,
        *args,
    ]