mirror of
https://github.com/haidaraM/ansible-playbook-grapher
synced 2024-09-20 06:11:52 +00:00
feat: Add support for hiding empty plays and plays without roles (#177)
This commit is contained in:
parent
9155fa8065
commit
21d54107ac
16 changed files with 345 additions and 54 deletions
|
@ -26,7 +26,7 @@ JavaScript:
|
|||
the files for the others nodes. The cursor will be at the task exact position in the file.
|
||||
Lastly, you can provide your own protocol formats
|
||||
with `--open-protocol-handler custom --open-protocol-custom-formats '{}'`. See the help
|
||||
and [an example.](https://github.com/haidaraM/ansible-playbook-grapher/blob/12cee0fbd59ffbb706731460e301f0b886515357/ansibleplaybookgrapher/graphbuilder.py#L33-L42).
|
||||
and [an example.](https://github.com/haidaraM/ansible-playbook-grapher/blob/12cee0fbd59ffbb706731460e301f0b886515357/ansibleplaybookgrapher/graphbuilder.py#L33-L42)
|
||||
- Filer tasks based on tags
|
||||
- Export the dot file used to generate the graph with Graphviz.
|
||||
|
||||
|
|
|
@ -76,6 +76,8 @@ class PlaybookGrapherCLI(CLI):
|
|||
output_filename=self.options.output_filename,
|
||||
view=self.options.view,
|
||||
save_dot_file=self.options.save_dot_file,
|
||||
hide_empty_plays=self.options.hide_empty_plays,
|
||||
hide_plays_without_roles=self.options.hide_plays_without_roles,
|
||||
)
|
||||
|
||||
return output_path
|
||||
|
@ -91,6 +93,8 @@ class PlaybookGrapherCLI(CLI):
|
|||
view=self.options.view,
|
||||
directive=self.options.renderer_mermaid_directive,
|
||||
orientation=self.options.renderer_mermaid_orientation,
|
||||
hide_empty_plays=self.options.hide_empty_plays,
|
||||
hide_plays_without_roles=self.options.hide_plays_without_roles,
|
||||
)
|
||||
return output_path
|
||||
|
||||
|
@ -114,7 +118,7 @@ class PlaybookGrapherCLI(CLI):
|
|||
dest="include_role_tasks",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Include the tasks of the role in the graph.",
|
||||
help="Include the tasks of the roles in the graph. Applied when parsing the playbooks.",
|
||||
)
|
||||
|
||||
self.parser.add_argument(
|
||||
|
@ -205,6 +209,21 @@ class PlaybookGrapherCLI(CLI):
|
|||
version=f"{__prog__} {__version__} (with ansible {ansible_version})",
|
||||
)
|
||||
|
||||
self.parser.add_argument(
|
||||
"--hide-plays-without-roles",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Hide the plays that end up with no roles in the graph (after applying the tags filter). "
|
||||
"Only roles at the play level and include_role as tasks are considered (no import_role).",
|
||||
)
|
||||
|
||||
self.parser.add_argument(
|
||||
"--hide-empty-plays",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Hide the plays that end up with no tasks in the graph (after applying the tags filter).",
|
||||
)
|
||||
|
||||
self.parser.add_argument(
|
||||
"playbook_filenames",
|
||||
help="Playbook(s) to graph",
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
import os
|
||||
from collections import defaultdict
|
||||
from typing import Dict, List, Set, Type, Tuple, Optional
|
||||
from typing import Dict, List, Set, Tuple, Optional
|
||||
|
||||
from ansibleplaybookgrapher.utils import generate_id, get_play_colors
|
||||
|
||||
|
@ -79,7 +79,7 @@ class Node:
|
|||
if self.raw_object and self.raw_object.get_ds():
|
||||
self.path, self.line, self.column = self.raw_object.get_ds().ansible_pos
|
||||
|
||||
def get_first_parent_matching_type(self, node_type: Type) -> Type:
|
||||
def get_first_parent_matching_type(self, node_type: type) -> type:
|
||||
"""
|
||||
Get the first parent of this node matching the given type
|
||||
:param node_type: The type of the parent to get
|
||||
|
@ -164,7 +164,7 @@ class CompositeNode(Node):
|
|||
node.index = self._node_counter + 1
|
||||
self._node_counter += 1
|
||||
|
||||
def get_node(self, target_composition: str) -> List:
|
||||
def get_nodes(self, target_composition: str) -> List:
|
||||
"""
|
||||
Get a node from the compositions
|
||||
:param target_composition:
|
||||
|
@ -221,6 +221,33 @@ class CompositeNode(Node):
|
|||
node._get_all_links(links)
|
||||
links[self].append(node)
|
||||
|
||||
def is_empty(self) -> bool:
|
||||
"""
|
||||
Returns true if the composite node is empty, false otherwise
|
||||
:return:
|
||||
"""
|
||||
for _, nodes in self._compositions.items():
|
||||
if len(nodes) > 0:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def has_node_type(self, node_type: type) -> bool:
|
||||
"""
|
||||
Returns true if the composite node has at least one node of the given type, false otherwise
|
||||
:param node_type: The type of the node
|
||||
:return:
|
||||
"""
|
||||
for _, nodes in self._compositions.items():
|
||||
for node in nodes:
|
||||
if isinstance(node, node_type):
|
||||
return True
|
||||
|
||||
if isinstance(node, CompositeNode):
|
||||
return node.has_node_type(node_type)
|
||||
|
||||
return False
|
||||
|
||||
|
||||
class CompositeTasksNode(CompositeNode):
|
||||
"""
|
||||
|
@ -261,7 +288,7 @@ class CompositeTasksNode(CompositeNode):
|
|||
The tasks attached to this block
|
||||
:return:
|
||||
"""
|
||||
return self.get_node("tasks")
|
||||
return self.get_nodes("tasks")
|
||||
|
||||
|
||||
class PlaybookNode(CompositeNode):
|
||||
|
@ -296,13 +323,24 @@ class PlaybookNode(CompositeNode):
|
|||
self.line = 1
|
||||
self.column = 1
|
||||
|
||||
@property
|
||||
def plays(self) -> List["PlayNode"]:
|
||||
def plays(
|
||||
self, exclude_empty: bool = False, exclude_without_roles: bool = False
|
||||
) -> List["PlayNode"]:
|
||||
"""
|
||||
Return the list of plays
|
||||
:param exclude_empty: Whether to exclude the empty plays from the result or not
|
||||
:param exclude_without_roles: Whether to exclude the plays that do not have roles
|
||||
:return:
|
||||
"""
|
||||
return self.get_node("plays")
|
||||
plays = self.get_nodes("plays")
|
||||
|
||||
if exclude_empty:
|
||||
plays = [play for play in plays if not play.is_empty()]
|
||||
|
||||
if exclude_without_roles:
|
||||
plays = [play for play in plays if play.has_node_type(RoleNode)]
|
||||
|
||||
return plays
|
||||
|
||||
def roles_usage(self) -> Dict["RoleNode", Set["PlayNode"]]:
|
||||
"""
|
||||
|
@ -364,19 +402,23 @@ class PlayNode(CompositeNode):
|
|||
|
||||
@property
|
||||
def roles(self) -> List["RoleNode"]:
|
||||
return self.get_node("roles")
|
||||
"""
|
||||
Return the roles of the plays. Tasks using "include_role" are NOT returned.
|
||||
:return:
|
||||
"""
|
||||
return self.get_nodes("roles")
|
||||
|
||||
@property
|
||||
def pre_tasks(self) -> List["Node"]:
|
||||
return self.get_node("pre_tasks")
|
||||
return self.get_nodes("pre_tasks")
|
||||
|
||||
@property
|
||||
def post_tasks(self) -> List["Node"]:
|
||||
return self.get_node("post_tasks")
|
||||
return self.get_nodes("post_tasks")
|
||||
|
||||
@property
|
||||
def tasks(self) -> List["Node"]:
|
||||
return self.get_node("tasks")
|
||||
return self.get_nodes("tasks")
|
||||
|
||||
|
||||
class BlockNode(CompositeTasksNode):
|
||||
|
|
|
@ -353,6 +353,16 @@ class PlaybookParser(BaseParser):
|
|||
# See :func:`~ansible.playbook.included_file.IncludedFile.process_include_results` from line 155
|
||||
display.v(f"An 'include_role' found: '{task_or_block.get_name()}'")
|
||||
|
||||
if not task_or_block.evaluate_tags(
|
||||
only_tags=self.tags,
|
||||
skip_tags=self.skip_tags,
|
||||
all_vars=task_vars,
|
||||
):
|
||||
display.vv(
|
||||
f"The include_role '{task_or_block.get_name()}' is skipped due to the tags."
|
||||
)
|
||||
continue # Go to the next task
|
||||
|
||||
# Here we are using the role name instead of the task name to keep the same behavior as a
|
||||
# traditional role
|
||||
if self.group_roles_by_name:
|
||||
|
|
|
@ -57,16 +57,20 @@ class Renderer(ABC):
|
|||
open_protocol_custom_formats: Dict[str, str],
|
||||
output_filename: str,
|
||||
view: bool,
|
||||
hide_empty_plays: bool = False,
|
||||
hide_plays_without_roles: bool = False,
|
||||
**kwargs,
|
||||
) -> str:
|
||||
"""
|
||||
Render the playbooks to a file.
|
||||
:param open_protocol_handler: The protocol handler name to use
|
||||
:param open_protocol_custom_formats: The custom formats to use when the protocol handler is set to custom
|
||||
:param output_filename: without any extension
|
||||
:param output_filename: The output filename without any extension
|
||||
:param view: Whether to open the rendered file in the default viewer
|
||||
:param hide_empty_plays: Whether to hide empty plays or not when rendering the graph
|
||||
:param hide_plays_without_roles: Whether to hide plays without any roles or not
|
||||
:param kwargs:
|
||||
:return: The filename of the rendered file
|
||||
:return: The path of the rendered file
|
||||
"""
|
||||
pass
|
||||
|
||||
|
@ -128,9 +132,16 @@ class PlaybookBuilder(ABC):
|
|||
)
|
||||
|
||||
@abstractmethod
|
||||
def build_playbook(self, **kwargs) -> str:
|
||||
def build_playbook(
|
||||
self,
|
||||
hide_empty_plays: bool = False,
|
||||
hide_plays_without_roles: bool = False,
|
||||
**kwargs,
|
||||
) -> str:
|
||||
"""
|
||||
Build the whole playbook
|
||||
:param hide_empty_plays: Whether to hide empty plays or not
|
||||
:param hide_plays_without_roles:
|
||||
:param kwargs:
|
||||
:return: The rendered playbook as a string
|
||||
"""
|
||||
|
|
|
@ -53,10 +53,18 @@ class GraphvizRenderer(Renderer):
|
|||
open_protocol_custom_formats: Dict[str, str],
|
||||
output_filename: str,
|
||||
view: bool,
|
||||
hide_empty_plays: bool = False,
|
||||
hide_plays_without_roles=False,
|
||||
**kwargs,
|
||||
) -> str:
|
||||
"""
|
||||
:return: The filename where the playbooks where rendered
|
||||
:param open_protocol_handler: The protocol handler name to use
|
||||
:param open_protocol_custom_formats: The custom formats to use when the protocol handler is set to custom
|
||||
:param output_filename: The output filename without any extension
|
||||
:param view: Whether to open the rendered file in the default viewer
|
||||
:param hide_empty_plays: Whether to hide empty plays or not when rendering the graph
|
||||
:param hide_plays_without_roles: Whether to hide plays without any roles or not
|
||||
:return: The path of the rendered file
|
||||
"""
|
||||
save_dot_file = kwargs.get("save_dot_file", False)
|
||||
|
||||
|
@ -76,7 +84,10 @@ class GraphvizRenderer(Renderer):
|
|||
roles_built=roles_built,
|
||||
digraph=digraph,
|
||||
)
|
||||
builder.build_playbook()
|
||||
builder.build_playbook(
|
||||
hide_empty_plays=hide_empty_plays,
|
||||
hide_plays_without_roles=hide_plays_without_roles,
|
||||
)
|
||||
roles_built.update(builder.roles_built)
|
||||
|
||||
display.display("Rendering the graph...")
|
||||
|
@ -274,9 +285,13 @@ class GraphvizPlaybookBuilder(PlaybookBuilder):
|
|||
digraph=role_subgraph,
|
||||
)
|
||||
|
||||
def build_playbook(self, **kwargs) -> str:
|
||||
def build_playbook(
|
||||
self, hide_empty_plays: bool = False, hide_plays_without_roles=False, **kwargs
|
||||
) -> str:
|
||||
"""
|
||||
Convert the PlaybookNode to the graphviz dot format
|
||||
:param hide_empty_plays: Whether to hide empty plays or not when rendering the graph
|
||||
:param hide_plays_without_roles: Whether to hide plays without any roles or not
|
||||
:return: The text representation of the graphviz dot format for the playbook
|
||||
"""
|
||||
display.vvv(f"Converting the graph to the dot format for graphviz")
|
||||
|
@ -289,7 +304,10 @@ class GraphvizPlaybookBuilder(PlaybookBuilder):
|
|||
URL=self.get_node_url(self.playbook_node, "file"),
|
||||
)
|
||||
|
||||
for play in self.playbook_node.plays:
|
||||
for play in self.playbook_node.plays(
|
||||
exclude_empty=hide_empty_plays,
|
||||
exclude_without_roles=hide_plays_without_roles,
|
||||
):
|
||||
with self.digraph.subgraph(name=play.name) as play_subgraph:
|
||||
self.build_play(play, digraph=play_subgraph, **kwargs)
|
||||
|
||||
|
|
|
@ -49,26 +49,32 @@ class MermaidFlowChartRenderer(Renderer):
|
|||
open_protocol_custom_formats: Dict[str, str],
|
||||
output_filename: str,
|
||||
view: bool,
|
||||
hide_empty_plays: bool = False,
|
||||
hide_plays_without_roles: bool = False,
|
||||
directive: str = DEFAULT_DIRECTIVE,
|
||||
orientation: str = DEFAULT_ORIENTATION,
|
||||
**kwargs,
|
||||
) -> str:
|
||||
"""
|
||||
|
||||
:param open_protocol_handler:
|
||||
:param open_protocol_custom_formats:
|
||||
:param output_filename: without any extension
|
||||
:param view:
|
||||
:param open_protocol_handler: Not supported for the moment
|
||||
:param open_protocol_custom_formats: Not supported for the moment
|
||||
:param output_filename: The output filename without any extension
|
||||
:param view: Not supported for the moment
|
||||
:param hide_empty_plays: Whether to hide empty plays or not when rendering the graph
|
||||
:param hide_plays_without_roles: Whether to hide plays without any roles or not
|
||||
:param directive: Mermaid directive
|
||||
:param orientation: Mermaid graph orientation
|
||||
:param kwargs:
|
||||
:return:
|
||||
"""
|
||||
# TODO: Add support for protocol handler
|
||||
# TODO: Add support for hover
|
||||
|
||||
mermaid_code = "---\n"
|
||||
mermaid_code += "title: Ansible Playbook Grapher\n"
|
||||
mermaid_code += "---\n"
|
||||
|
||||
directive = kwargs.get("directive", DEFAULT_DIRECTIVE)
|
||||
orientation = kwargs.get("orientation", DEFAULT_ORIENTATION)
|
||||
|
||||
display.vvv(f"Using '{directive}' as directive for the mermaid chart")
|
||||
mermaid_code += f"{directive}\n"
|
||||
|
||||
|
@ -90,7 +96,10 @@ class MermaidFlowChartRenderer(Renderer):
|
|||
link_order=link_order,
|
||||
)
|
||||
|
||||
mermaid_code += playbook_builder.build_playbook()
|
||||
mermaid_code += playbook_builder.build_playbook(
|
||||
hide_empty_plays=hide_empty_plays,
|
||||
hide_plays_without_roles=hide_plays_without_roles,
|
||||
)
|
||||
link_order = playbook_builder.link_order
|
||||
roles_built.update(playbook_builder.roles_built)
|
||||
|
||||
|
@ -130,15 +139,24 @@ class MermaidFlowChartPlaybookBuilder(PlaybookBuilder):
|
|||
roles_usage,
|
||||
roles_built,
|
||||
)
|
||||
|
||||
self.mermaid_code = ""
|
||||
# Used as an identifier for the links
|
||||
self.link_order = link_order
|
||||
# The current depth level of the nodes. Used for indentation
|
||||
self._identation_level = 1
|
||||
self._indentation_level = 1
|
||||
|
||||
def build_playbook(self, **kwargs) -> str:
|
||||
def build_playbook(
|
||||
self,
|
||||
hide_empty_plays: bool = False,
|
||||
hide_plays_without_roles=False,
|
||||
**kwargs: bool,
|
||||
) -> str:
|
||||
"""
|
||||
Build the playbook
|
||||
:param hide_plays_without_roles: Whether to hide plays without any roles or not
|
||||
:param hide_empty_plays: Whether to hide empty plays or not
|
||||
:param hide_plays_without_roles: Whether to hide plays without any roles or not
|
||||
:param kwargs:
|
||||
:return:
|
||||
"""
|
||||
|
@ -150,10 +168,13 @@ class MermaidFlowChartPlaybookBuilder(PlaybookBuilder):
|
|||
self.add_comment(f"Start of the playbook '{self.playbook_node.name}'")
|
||||
self.add_text(f'{self.playbook_node.id}("{self.playbook_node.name}")')
|
||||
|
||||
self._identation_level += 1
|
||||
for play_node in self.playbook_node.plays:
|
||||
self._indentation_level += 1
|
||||
for play_node in self.playbook_node.plays(
|
||||
exclude_empty=hide_empty_plays,
|
||||
exclude_without_roles=hide_plays_without_roles,
|
||||
):
|
||||
self.build_play(play_node)
|
||||
self._identation_level -= 1
|
||||
self._indentation_level -= 1
|
||||
|
||||
self.add_comment(f"End of the playbook '{self.playbook_node.name}'\n")
|
||||
|
||||
|
@ -182,9 +203,9 @@ class MermaidFlowChartPlaybookBuilder(PlaybookBuilder):
|
|||
)
|
||||
|
||||
# traverse the play
|
||||
self._identation_level += 1
|
||||
self._indentation_level += 1
|
||||
self.traverse_play(play_node)
|
||||
self._identation_level -= 1
|
||||
self._indentation_level -= 1
|
||||
|
||||
self.add_comment(f"End of the play '{play_node.name}'")
|
||||
|
||||
|
@ -249,14 +270,14 @@ class MermaidFlowChartPlaybookBuilder(PlaybookBuilder):
|
|||
)
|
||||
|
||||
# Role tasks
|
||||
self._identation_level += 1
|
||||
self._indentation_level += 1
|
||||
for role_task in role_node.tasks:
|
||||
self.build_node(
|
||||
node=role_task,
|
||||
color=node_color,
|
||||
fontcolor=fontcolor,
|
||||
)
|
||||
self._identation_level -= 1
|
||||
self._indentation_level -= 1
|
||||
|
||||
self.add_comment(f"End of the role '{role_node.name}'")
|
||||
|
||||
|
@ -287,14 +308,14 @@ class MermaidFlowChartPlaybookBuilder(PlaybookBuilder):
|
|||
|
||||
self.add_text(f'subgraph subgraph_{block_node.id}["{block_node.name} "]')
|
||||
|
||||
self._identation_level += 1
|
||||
self._indentation_level += 1
|
||||
for task in block_node.tasks:
|
||||
self.build_node(
|
||||
node=task,
|
||||
color=color,
|
||||
fontcolor=fontcolor,
|
||||
)
|
||||
self._identation_level -= 1
|
||||
self._indentation_level -= 1
|
||||
|
||||
self.add_text("end") # End of the subgraph
|
||||
self.add_comment(f"End of the block '{block_node.name}'")
|
||||
|
@ -347,4 +368,4 @@ class MermaidFlowChartPlaybookBuilder(PlaybookBuilder):
|
|||
Return the current indentation level as tabulations
|
||||
:return:
|
||||
"""
|
||||
return "\t" * self._identation_level
|
||||
return "\t" * self._indentation_level
|
||||
|
|
|
@ -21,7 +21,7 @@ from operator import methodcaller
|
|||
from typing import Tuple, List, Dict, Any, Set
|
||||
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.common.text.converters import to_text
|
||||
from ansible.parsing.dataloader import DataLoader
|
||||
from ansible.playbook.role_include import IncludeRole
|
||||
from ansible.playbook.task import Task
|
||||
|
|
2
tests/fixtures/import_role.yml
vendored
2
tests/fixtures/import_role.yml
vendored
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
- hosts: all
|
||||
tags:
|
||||
- play2
|
||||
- play1
|
||||
roles:
|
||||
- role: fake_role
|
||||
tags:
|
||||
|
|
2
tests/fixtures/include_role.yml
vendored
2
tests/fixtures/include_role.yml
vendored
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
- hosts: all
|
||||
tags:
|
||||
- play2
|
||||
- play1
|
||||
roles:
|
||||
- fake_role
|
||||
- display_some_facts
|
||||
|
|
22
tests/fixtures/play-hiding.yml
vendored
Normal file
22
tests/fixtures/play-hiding.yml
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
- hosts: host1
|
||||
tags:
|
||||
- play1
|
||||
roles:
|
||||
- role: with-dependencies
|
||||
|
||||
|
||||
- hosts: host2
|
||||
tags:
|
||||
- play2
|
||||
tasks:
|
||||
- name: debug
|
||||
debug: msg="Post task 1"
|
||||
- name: Include role
|
||||
include_role:
|
||||
name: fake_role
|
||||
|
||||
|
||||
- hosts: host3 # This should not be displayed when --hide-empty-plays is set
|
||||
tags:
|
||||
- play3
|
2
tests/fixtures/roles_dependencies.yml
vendored
2
tests/fixtures/roles_dependencies.yml
vendored
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
- hosts: all
|
||||
tags:
|
||||
- play2
|
||||
- play1
|
||||
roles:
|
||||
- role: with-dependencies
|
||||
|
||||
|
|
|
@ -66,3 +66,36 @@ def test_get_all_tasks_nodes():
|
|||
all_tasks = play.get_all_tasks()
|
||||
assert len(all_tasks) == 4, "There should be 4 tasks in all"
|
||||
assert [task_1, task_2, task_3, task_4] == all_tasks
|
||||
|
||||
|
||||
def test_empty_play():
|
||||
"""
|
||||
Testing the emptiness of a play
|
||||
:return:
|
||||
"""
|
||||
|
||||
play = PlayNode("play")
|
||||
assert play.is_empty(), "The play should empty"
|
||||
|
||||
play.add_node("roles", RoleNode("my_role_1"))
|
||||
assert not play.is_empty(), "The play should not be empty"
|
||||
|
||||
|
||||
def test_has_node_type():
|
||||
"""
|
||||
Testing the method has_node_type
|
||||
:return:
|
||||
"""
|
||||
play = PlayNode("play")
|
||||
block = BlockNode("block 1")
|
||||
role = RoleNode("my_role")
|
||||
role.add_node("tasks", TaskNode("task 1"))
|
||||
|
||||
block.add_node("tasks", role)
|
||||
play.add_node("tasks", block)
|
||||
|
||||
assert play.has_node_type(BlockNode), "The play should have BlockNode"
|
||||
assert play.has_node_type(RoleNode), "The play should have a RoleNode"
|
||||
assert play.has_node_type(TaskNode), "The play should have a TaskNode"
|
||||
|
||||
assert not role.has_node_type(BlockNode), "The role doesn't have a BlockNode"
|
||||
|
|
|
@ -541,3 +541,118 @@ def test_group_roles_by_name(
|
|||
post_tasks_number=post_tasks_number,
|
||||
blocks_number=1,
|
||||
)
|
||||
|
||||
|
||||
def test_hiding_plays(request):
|
||||
"""
|
||||
Test hiding_plays with the flag --hide-empty-plays.
|
||||
|
||||
This case is about hiding plays with 0 zero task (no filtering)
|
||||
:param request:
|
||||
:return:
|
||||
"""
|
||||
svg_path, playbook_paths = run_grapher(
|
||||
["play-hiding.yml"],
|
||||
output_filename=request.node.name,
|
||||
additional_args=["--hide-empty-plays"],
|
||||
)
|
||||
|
||||
_common_tests(
|
||||
svg_path=svg_path,
|
||||
playbook_paths=playbook_paths,
|
||||
plays_number=2,
|
||||
roles_number=2,
|
||||
tasks_number=1,
|
||||
)
|
||||
|
||||
|
||||
def test_hiding_empty_plays_with_tags_filter(request):
|
||||
"""
|
||||
Test hiding plays with the flag --hide-empty-plays.
|
||||
|
||||
This case is about hiding plays when filtering with tags
|
||||
:param request:
|
||||
:return:
|
||||
"""
|
||||
svg_path, playbook_paths = run_grapher(
|
||||
["play-hiding.yml"],
|
||||
output_filename=request.node.name,
|
||||
additional_args=["--hide-empty-plays", "--tags", "play1"],
|
||||
)
|
||||
|
||||
_common_tests(
|
||||
svg_path=svg_path, playbook_paths=playbook_paths, plays_number=1, roles_number=1
|
||||
)
|
||||
|
||||
|
||||
def test_hiding_empty_plays_with_tags_filter_all(request):
|
||||
"""
|
||||
Test hiding plays with the flag --hide-empty-plays.
|
||||
|
||||
This case is about hiding ALL the plays when filtering with tags
|
||||
:param request:
|
||||
:return:
|
||||
"""
|
||||
svg_path, playbook_paths = run_grapher(
|
||||
["play-hiding.yml"],
|
||||
output_filename=request.node.name,
|
||||
additional_args=[
|
||||
"--hide-empty-plays",
|
||||
"--tags",
|
||||
"fake-tag-that-does-not-exist",
|
||||
],
|
||||
)
|
||||
|
||||
_common_tests(svg_path=svg_path, playbook_paths=playbook_paths)
|
||||
|
||||
|
||||
def test_hiding_plays_without_roles(request):
|
||||
"""
|
||||
Test hiding plays with the flag --hide-plays-without-roles
|
||||
|
||||
:param request:
|
||||
:return:
|
||||
"""
|
||||
svg_path, playbook_paths = run_grapher(
|
||||
["play-hiding.yml"],
|
||||
output_filename=request.node.name,
|
||||
additional_args=[
|
||||
"--hide-plays-without-roles",
|
||||
],
|
||||
)
|
||||
|
||||
_common_tests(
|
||||
svg_path=svg_path,
|
||||
playbook_paths=playbook_paths,
|
||||
plays_number=2,
|
||||
roles_number=2,
|
||||
tasks_number=1,
|
||||
)
|
||||
|
||||
|
||||
def test_hiding_plays_without_roles_with_tags_filtering(request):
|
||||
"""
|
||||
Test hiding plays with the flag --hide-plays-without-roles
|
||||
|
||||
Also apply some tags filter
|
||||
:param request:
|
||||
:return:
|
||||
"""
|
||||
svg_path, playbook_paths = run_grapher(
|
||||
["play-hiding.yml"],
|
||||
output_filename=request.node.name,
|
||||
additional_args=[
|
||||
"--hide-plays-without-roles",
|
||||
"--tags",
|
||||
"play1",
|
||||
"--include-role-tasks",
|
||||
],
|
||||
)
|
||||
|
||||
_common_tests(
|
||||
svg_path=svg_path,
|
||||
playbook_paths=playbook_paths,
|
||||
plays_number=1,
|
||||
roles_number=1,
|
||||
tasks_number=5,
|
||||
)
|
||||
|
|
|
@ -50,7 +50,7 @@ def run_grapher(
|
|||
|
||||
def _common_tests(mermaid_path: str, playbook_paths: List[str], **kwargs):
|
||||
"""
|
||||
|
||||
Some common tests for mermaid renderer
|
||||
:param mermaid_path:
|
||||
:param playbook_paths:
|
||||
:param kwargs:
|
||||
|
|
|
@ -48,7 +48,7 @@ def test_example_parsing(grapher_cli: PlaybookGrapherCLI, display: Display):
|
|||
"""
|
||||
parser = PlaybookParser(grapher_cli.options.playbook_filenames[0])
|
||||
playbook_node = parser.parse()
|
||||
assert len(playbook_node.plays) == 1
|
||||
assert len(playbook_node.plays()) == 1
|
||||
assert playbook_node.path == os.path.join(FIXTURES_PATH, "example.yml")
|
||||
assert playbook_node.line == 1
|
||||
assert playbook_node.column == 1
|
||||
|
@ -56,7 +56,7 @@ def test_example_parsing(grapher_cli: PlaybookGrapherCLI, display: Display):
|
|||
playbook_node.index is None
|
||||
), "The index of the playbook should be None (it has no parent)"
|
||||
|
||||
play_node = playbook_node.plays[0]
|
||||
play_node = playbook_node.plays()[0]
|
||||
assert play_node.path == os.path.join(FIXTURES_PATH, "example.yml")
|
||||
assert play_node.line == 2
|
||||
assert play_node.index == 1
|
||||
|
@ -90,8 +90,8 @@ def test_with_roles_parsing(grapher_cli: PlaybookGrapherCLI):
|
|||
"""
|
||||
parser = PlaybookParser(grapher_cli.options.playbook_filenames[0])
|
||||
playbook_node = parser.parse()
|
||||
assert len(playbook_node.plays) == 1
|
||||
play_node = playbook_node.plays[0]
|
||||
assert len(playbook_node.plays()) == 1
|
||||
play_node = playbook_node.plays()[0]
|
||||
assert play_node.index == 1
|
||||
|
||||
assert len(play_node.roles) == 2
|
||||
|
@ -127,8 +127,8 @@ def test_include_role_parsing(grapher_cli: PlaybookGrapherCLI, capsys):
|
|||
grapher_cli.options.playbook_filenames[0], include_role_tasks=True
|
||||
)
|
||||
playbook_node = parser.parse()
|
||||
assert len(playbook_node.plays) == 1
|
||||
play_node = playbook_node.plays[0]
|
||||
assert len(playbook_node.plays()) == 1
|
||||
play_node = playbook_node.plays()[0]
|
||||
tasks = play_node.tasks
|
||||
assert len(tasks) == 6
|
||||
|
||||
|
@ -194,9 +194,9 @@ def test_block_parsing(grapher_cli: PlaybookGrapherCLI):
|
|||
grapher_cli.options.playbook_filenames[0], include_role_tasks=True
|
||||
)
|
||||
playbook_node = parser.parse()
|
||||
assert len(playbook_node.plays) == 1
|
||||
assert len(playbook_node.plays()) == 1
|
||||
|
||||
play_node = playbook_node.plays[0]
|
||||
play_node = playbook_node.plays()[0]
|
||||
pre_tasks = play_node.pre_tasks
|
||||
tasks = play_node.tasks
|
||||
post_tasks = play_node.post_tasks
|
||||
|
@ -360,7 +360,7 @@ def test_roles_dependencies(grapher_cli: PlaybookGrapherCLI):
|
|||
grapher_cli.options.playbook_filenames[0], include_role_tasks=True
|
||||
)
|
||||
playbook_node = parser.parse()
|
||||
roles = playbook_node.plays[0].roles
|
||||
roles = playbook_node.plays()[0].roles
|
||||
assert len(roles) == 1, "Only one explicit role is called inside the playbook"
|
||||
role_with_dependencies = roles[0]
|
||||
tasks = role_with_dependencies.tasks
|
||||
|
|
Loading…
Reference in a new issue