1
0

loader.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. # -*- coding: utf-8 -*-
  2. """
  3. tasks.loader
  4. ~~~~~~~~~~~~
  5. Salt loader checks
  6. """
  7. import ast
  8. import pathlib
  9. from invoke import task # pylint: disable=3rd-party-module-not-gated
  10. from tasks import utils
  11. CODE_DIR = pathlib.Path(__file__).resolve().parent.parent
  12. SALT_CODE_DIR = CODE_DIR / "salt"
  13. @task(iterable=["files"], positional=["files"])
  14. def check_virtual(ctx, files):
  15. """
  16. Check Salt loader modules for a defined `__virtualname__` attribute and `__virtual__` function.
  17. This is meant to replace:
  18. https://github.com/saltstack/salt/blob/27ae8260983b11fe6e32a18e777d550be9fe1dc2/tests/unit/test_virtualname.py
  19. """
  20. # CD into Salt's repo root directory
  21. ctx.cd(CODE_DIR)
  22. # Unfortunately invoke does not support nargs.
  23. # We migth have been passed --files="foo.py bar.py"
  24. # Turn that into a list of paths
  25. _files = []
  26. for path in files:
  27. if not path:
  28. continue
  29. _files.extend(path.split())
  30. if not _files:
  31. _files = SALT_CODE_DIR.rglob("*.py")
  32. else:
  33. _files = [pathlib.Path(fname) for fname in _files]
  34. _files = [path.resolve() for path in _files]
  35. salt_loaders = (
  36. CODE_DIR / "salt" / "modules",
  37. CODE_DIR / "salt" / "metaproxy",
  38. CODE_DIR / "salt" / "matchers",
  39. CODE_DIR / "salt" / "engines",
  40. CODE_DIR / "salt" / "proxy",
  41. CODE_DIR / "salt" / "returners",
  42. CODE_DIR / "salt" / "utils",
  43. CODE_DIR / "salt" / "pillar",
  44. CODE_DIR / "salt" / "tops",
  45. CODE_DIR / "salt" / "wheel",
  46. CODE_DIR / "salt" / "output",
  47. CODE_DIR / "salt" / "serializers",
  48. CODE_DIR / "salt" / "tokens",
  49. CODE_DIR / "salt" / "auth",
  50. CODE_DIR / "salt" / "fileserver",
  51. CODE_DIR / "salt" / "roster",
  52. CODE_DIR / "salt" / "thorium",
  53. CODE_DIR / "salt" / "states",
  54. CODE_DIR / "salt" / "beacons",
  55. CODE_DIR / "salt" / "log" / "handlers",
  56. CODE_DIR / "salt" / "client" / "ssh",
  57. CODE_DIR / "salt" / "renderers",
  58. CODE_DIR / "salt" / "grains",
  59. CODE_DIR / "salt" / "runners",
  60. CODE_DIR / "salt" / "queues",
  61. CODE_DIR / "salt" / "sdb",
  62. CODE_DIR / "salt" / "spm" / "pkgdb",
  63. CODE_DIR / "salt" / "spm" / "pkgfiles",
  64. CODE_DIR / "salt" / "cloud" / "clouds",
  65. CODE_DIR / "salt" / "netapi",
  66. CODE_DIR / "salt" / "executors",
  67. CODE_DIR / "salt" / "cache",
  68. )
  69. # This is just internal task checking
  70. for loader in salt_loaders:
  71. if not pathlib.Path(loader).is_dir():
  72. utils.error("The {} path is not a directory", loader)
  73. errors = 0
  74. exitcode = 0
  75. for path in _files:
  76. strpath = str(path)
  77. if strpath.endswith("__init__.py"):
  78. continue
  79. for loader in salt_loaders:
  80. try:
  81. path.relative_to(loader)
  82. break
  83. except ValueError:
  84. # Path doesn't start with the loader path, carry on
  85. continue
  86. module = ast.parse(path.read_text(), filename=strpath)
  87. found_virtual_func = False
  88. for funcdef in [
  89. node for node in module.body if isinstance(node, ast.FunctionDef)
  90. ]:
  91. if funcdef.name == "__virtual__":
  92. found_virtual_func = True
  93. break
  94. if not found_virtual_func:
  95. # If the module does not define a __virtual__() function, we don't require a __virtualname__ attribute
  96. continue
  97. found_virtualname_attr = False
  98. for node in module.body:
  99. if isinstance(node, ast.Assign):
  100. if not found_virtualname_attr:
  101. for target in node.targets:
  102. if not isinstance(target, ast.Name):
  103. continue
  104. if target.id == "__virtualname__":
  105. found_virtualname_attr = True
  106. if node.value.s not in path.name:
  107. errors += 1
  108. exitcode = 1
  109. utils.error(
  110. 'The value of the __virtualname__ attribute, "{}" is not part of {}',
  111. node.value.s,
  112. path.name,
  113. )
  114. if found_virtualname_attr:
  115. break
  116. if not found_virtualname_attr:
  117. errors += 1
  118. exitcode = 1
  119. utils.error(
  120. "The salt loader module {} defines a __virtual__() function but does not define a "
  121. "__virtualname__ attribute",
  122. path.relative_to(CODE_DIR),
  123. )
  124. if exitcode:
  125. utils.error("Found {} errors", errors)
  126. utils.exit_invoke(exitcode)