Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Select and exclude multiple scenarios #4388

Merged
merged 18 commits into from
Feb 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 51 additions & 15 deletions src/molecule/command/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,11 @@ def _setup(self) -> None:


def execute_cmdline_scenarios(
scenario_name: str | None,
scenario_names: list[str] | None,
args: MoleculeArgs,
command_args: CommandArgs,
ansible_args: tuple[str, ...] = (),
excludes: list[str] | None = None,
) -> None:
"""Execute scenario sequences based on parsed command-line arguments.

Expand All @@ -113,28 +114,33 @@ def execute_cmdline_scenarios(
to generate the scenario(s) configuration.

Args:
scenario_name: Name of scenario to run, or ``None`` to run all.
scenario_names: Name of scenarios to run, or ``None`` to run all.
args: ``args`` dict from ``click`` command context
command_args: dict of command arguments, including the target
ansible_args: Optional tuple of arguments to pass to the `ansible-playbook` command
excludes: Name of scenarios to not run.

Raises:
SystemExit: If scenario exits prematurely.
"""
glob_str = MOLECULE_GLOB
if scenario_name:
glob_str = glob_str.replace("*", scenario_name)
scenarios = molecule.scenarios.Scenarios(
get_configs(args, command_args, ansible_args, glob_str),
scenario_name,
)
if excludes is None:
excludes = []

configs: list[config.Config] = []
if scenario_names is None:
configs = [
config
for config in get_configs(args, command_args, ansible_args, MOLECULE_GLOB)
if config.scenario.name not in excludes
]
else:
# filter out excludes
scenario_names = [name for name in scenario_names if name not in excludes]
for scenario_name in scenario_names:
glob_str = MOLECULE_GLOB.replace("*", scenario_name)
configs.extend(get_configs(args, command_args, ansible_args, glob_str))

if scenario_name and scenarios:
LOG.info(
"%s scenario test matrix: %s",
scenario_name,
", ".join(scenarios.sequence(scenario_name)),
)
scenarios = _generate_scenarios(scenario_names, configs)

for scenario in scenarios:
if scenario.config.config["prerun"]:
Expand Down Expand Up @@ -171,6 +177,36 @@ def execute_cmdline_scenarios(
raise


def _generate_scenarios(
scenario_names: list[str] | None,
configs: list[config.Config],
) -> molecule.scenarios.Scenarios:
"""Generate Scenarios object from names and configs.

Args:
scenario_names: Names of scenarios to include.
configs: List of Config objects to consider.

Returns:
Combined Scenarios object.
"""
scenarios = molecule.scenarios.Scenarios(
configs,
scenario_names,
)

if scenario_names is not None:
for scenario_name in scenario_names:
if scenario_name != "*" and scenarios:
LOG.info(
"%s scenario test matrix: %s",
scenario_name,
", ".join(scenarios.sequence(scenario_name)),
)

return scenarios


def execute_subcommand(
current_config: config.Config,
subcommand_and_args: str,
Expand Down
28 changes: 24 additions & 4 deletions src/molecule/command/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,21 @@ def execute(self, action_args: list[str] | None = None) -> None: # noqa: ARG002
@click.option(
"--scenario-name",
"-s",
default=base.MOLECULE_DEFAULT_SCENARIO_NAME,
help=f"Name of the scenario to target. ({base.MOLECULE_DEFAULT_SCENARIO_NAME})",
multiple=True,
default=[base.MOLECULE_DEFAULT_SCENARIO_NAME],
help=f"Name of the scenario to target. May be specified multiple times. ({base.MOLECULE_DEFAULT_SCENARIO_NAME})",
)
@click.option(
"--all/--no-all",
"__all",
default=False,
help="Check all scenarios. Default is False.",
)
@click.option(
"--exclude",
"-e",
multiple=True,
help="Name of the scenario to exclude from running. May be specified multiple times.",
)
@click.option(
"--parallel/--no-parallel",
Expand All @@ -67,7 +80,9 @@ def execute(self, action_args: list[str] | None = None) -> None: # noqa: ARG002
)
def check( # pragma: no cover
ctx: click.Context,
scenario_name: str,
scenario_name: list[str] | None,
exclude: list[str],
__all: bool, # noqa: FBT001
*,
parallel: bool,
) -> None:
Expand All @@ -76,13 +91,18 @@ def check( # pragma: no cover
Args:
ctx: Click context object holding commandline arguments.
scenario_name: Name of the scenario to target.
exclude: Name of the scenarios to avoid targeting.
__all: Whether molecule should target scenario_name or all scenarios.
parallel: Whether the scenario(s) should be run in parallel.
"""
args: MoleculeArgs = ctx.obj.get("args")
subcommand = base._get_subcommand(__name__) # noqa: SLF001
command_args: CommandArgs = {"parallel": parallel, "subcommand": subcommand}

if __all:
scenario_name = None

if parallel:
util.validate_parallel_cmd_args(command_args)

base.execute_cmdline_scenarios(scenario_name, args, command_args)
base.execute_cmdline_scenarios(scenario_name, args, command_args, excludes=exclude)
28 changes: 24 additions & 4 deletions src/molecule/command/cleanup.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,27 @@ def execute(self, action_args: list[str] | None = None) -> None: # noqa: ARG002
@click.option(
"--scenario-name",
"-s",
default=base.MOLECULE_DEFAULT_SCENARIO_NAME,
help=f"Name of the scenario to target. ({base.MOLECULE_DEFAULT_SCENARIO_NAME})",
multiple=True,
default=[base.MOLECULE_DEFAULT_SCENARIO_NAME],
help=f"Name of the scenario to target. May be specified multiple times. ({base.MOLECULE_DEFAULT_SCENARIO_NAME})",
)
@click.option(
"--all/--no-all",
"__all",
default=False,
help="Cleanup all scenarios. Default is False.",
)
@click.option(
"--exclude",
"-e",
multiple=True,
help="Name of the scenario to exclude from running. May be specified multiple times.",
)
def cleanup(
ctx: click.Context,
scenario_name: str = "default",
scenario_name: list[str] | None,
exclude: list[str],
__all: bool, # noqa: FBT001
) -> None: # pragma: no cover
"""Use the provisioner to cleanup any changes.

Expand All @@ -73,9 +88,14 @@ def cleanup(
Args:
ctx: Click context object holding commandline arguments.
scenario_name: Name of the scenario to target.
exclude: Name of the scenarios to avoid targeting.
__all: Whether molecule should target scenario_name or all scenarios.
"""
args: MoleculeArgs = ctx.obj.get("args")
subcommand = base._get_subcommand(__name__) # noqa: SLF001
command_args: CommandArgs = {"subcommand": subcommand}

base.execute_cmdline_scenarios(scenario_name, args, command_args)
if __all:
scenario_name = None

base.execute_cmdline_scenarios(scenario_name, args, command_args, excludes=exclude)
28 changes: 24 additions & 4 deletions src/molecule/command/converge.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,24 +55,44 @@ def execute(self, action_args: list[str] | None = None) -> None: # noqa: ARG002
@click.option(
"--scenario-name",
"-s",
default=base.MOLECULE_DEFAULT_SCENARIO_NAME,
help=f"Name of the scenario to target. ({base.MOLECULE_DEFAULT_SCENARIO_NAME})",
multiple=True,
default=[base.MOLECULE_DEFAULT_SCENARIO_NAME],
help=f"Name of the scenario to target. May be specified multiple times. ({base.MOLECULE_DEFAULT_SCENARIO_NAME})",
)
@click.option(
"--all/--no-all",
"__all",
default=False,
help="Converge all scenarios. Default is False.",
)
@click.option(
"--exclude",
"-e",
multiple=True,
help="Name of the scenario to exclude from running. May be specified multiple times.",
)
@click.argument("ansible_args", nargs=-1, type=click.UNPROCESSED)
def converge(
ctx: click.Context,
scenario_name: str,
scenario_name: list[str] | None,
exclude: list[str],
__all: bool, # noqa: FBT001
ansible_args: tuple[str],
) -> None: # pragma: no cover
"""Use the provisioner to configure instances (dependency, create, prepare converge).

Args:
ctx: Click context object holding commandline arguments.
scenario_name: Name of the scenario to target.
exclude: Name of the scenarios to avoid targeting.
__all: Whether molecule should target scenario_name or all scenarios.
ansible_args: Arguments to forward to Ansible.
"""
args: MoleculeArgs = ctx.obj.get("args")
subcommand = base._get_subcommand(__name__) # noqa: SLF001
command_args: CommandArgs = {"subcommand": subcommand}

base.execute_cmdline_scenarios(scenario_name, args, command_args, ansible_args)
if __all:
scenario_name = None

base.execute_cmdline_scenarios(scenario_name, args, command_args, ansible_args, exclude)
28 changes: 24 additions & 4 deletions src/molecule/command/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,29 +65,49 @@ def execute(self, action_args: list[str] | None = None) -> None: # noqa: ARG002
@click.option(
"--scenario-name",
"-s",
default=base.MOLECULE_DEFAULT_SCENARIO_NAME,
help=f"Name of the scenario to target. ({base.MOLECULE_DEFAULT_SCENARIO_NAME})",
multiple=True,
default=[base.MOLECULE_DEFAULT_SCENARIO_NAME],
help=f"Name of the scenario to target. May be specified multiple times. ({base.MOLECULE_DEFAULT_SCENARIO_NAME})",
)
@click.option(
"--driver-name",
"-d",
type=click.Choice([str(s) for s in drivers()]),
help=f"Name of driver to use. ({DEFAULT_DRIVER})",
)
@click.option(
"--all/--no-all",
"__all",
default=False,
help="Start all scenarios. Default is False.",
)
@click.option(
"--exclude",
"-e",
multiple=True,
help="Name of the scenario to exclude from running. May be specified multiple times.",
)
def create(
ctx: click.Context,
scenario_name: str,
scenario_name: list[str] | None,
exclude: list[str],
driver_name: str,
__all: bool, # noqa: FBT001
) -> None: # pragma: no cover
"""Use the provisioner to start the instances.

Args:
ctx: Click context object holding commandline arguments.
scenario_name: Name of the scenario to target.
exclude: Name of the scenarios to avoid targeting.
driver_name: Name of the Molecule driver to use.
__all: Whether molecule should target scenario_name or all scenarios.
"""
args: MoleculeArgs = ctx.obj.get("args")
subcommand = base._get_subcommand(__name__) # noqa: SLF001
command_args: CommandArgs = {"subcommand": subcommand, "driver_name": driver_name}

base.execute_cmdline_scenarios(scenario_name, args, command_args)
if __all:
scenario_name = None

base.execute_cmdline_scenarios(scenario_name, args, command_args, excludes=exclude)
28 changes: 24 additions & 4 deletions src/molecule/command/dependency.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,21 +54,41 @@ def execute(self, action_args: list[str] | None = None) -> None: # noqa: ARG002
@click.option(
"--scenario-name",
"-s",
default=base.MOLECULE_DEFAULT_SCENARIO_NAME,
help=f"Name of the scenario to target. ({base.MOLECULE_DEFAULT_SCENARIO_NAME})",
multiple=True,
default=[base.MOLECULE_DEFAULT_SCENARIO_NAME],
help=f"Name of the scenario to target. May be specified multiple times. ({base.MOLECULE_DEFAULT_SCENARIO_NAME})",
)
@click.option(
"--all/--no-all",
"__all",
default=False,
help="Target all scenarios. Default is False.",
)
@click.option(
"--exclude",
"-e",
multiple=True,
help="Name of the scenario to exclude from running. May be specified multiple times.",
)
def dependency(
ctx: click.Context,
scenario_name: str,
scenario_name: list[str] | None,
exclude: list[str],
__all: bool, # noqa: FBT001
) -> None: # pragma: no cover
"""Manage the role's dependencies.

Args:
ctx: Click context object holding commandline arguments.
scenario_name: Name of the scenario to target.
exclude: Name of the scenarios to avoid targeting.
__all: Whether molecule should target scenario_name or all scenarios.
"""
args: MoleculeArgs = ctx.obj.get("args")
subcommand = base._get_subcommand(__name__) # noqa: SLF001
command_args: CommandArgs = {"subcommand": subcommand}

base.execute_cmdline_scenarios(scenario_name, args, command_args)
if __all:
scenario_name = None

base.execute_cmdline_scenarios(scenario_name, args, command_args, excludes=exclude)
17 changes: 13 additions & 4 deletions src/molecule/command/destroy.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,9 @@ def execute(self, action_args: list[str] | None = None) -> None: # noqa: ARG002
@click.option(
"--scenario-name",
"-s",
default=base.MOLECULE_DEFAULT_SCENARIO_NAME,
help=f"Name of the scenario to target. ({base.MOLECULE_DEFAULT_SCENARIO_NAME})",
multiple=True,
default=[base.MOLECULE_DEFAULT_SCENARIO_NAME],
help=f"Name of the scenario to target. May be specified multiple times. ({base.MOLECULE_DEFAULT_SCENARIO_NAME})",
)
@click.option(
"--driver-name",
Expand All @@ -80,14 +81,21 @@ def execute(self, action_args: list[str] | None = None) -> None: # noqa: ARG002
default=MOLECULE_PARALLEL,
help="Destroy all scenarios. Default is False.",
)
@click.option(
"--exclude",
"-e",
multiple=True,
help="Name of the scenario to exclude from running. May be specified multiple times.",
)
@click.option(
"--parallel/--no-parallel",
default=False,
help="Enable or disable parallel mode. Default is disabled.",
)
def destroy(
ctx: click.Context,
scenario_name: str | None,
scenario_name: list[str] | None,
exclude: list[str],
driver_name: str,
__all: bool, # noqa: FBT001
parallel: bool, # noqa: FBT001
Expand All @@ -97,6 +105,7 @@ def destroy(
Args:
ctx: Click context object holding commandline arguments.
scenario_name: Name of the scenario to target.
exclude: Name of the scenarios to avoid targeting.
driver_name: Molecule driver to use.
__all: Whether molecule should target scenario_name or all scenarios.
parallel: Whether the scenario(s) should be run in parallel mode.
Expand All @@ -115,4 +124,4 @@ def destroy(
if parallel:
util.validate_parallel_cmd_args(command_args)

base.execute_cmdline_scenarios(scenario_name, args, command_args)
base.execute_cmdline_scenarios(scenario_name, args, command_args, excludes=exclude)
Loading