Skip to content

Commit

Permalink
feat: Capture blacklisted specs inside archive (#3664)
Browse files Browse the repository at this point in the history
* Capture the name of blacklisted specs, and dump them to a file in the
  archive.
* Added parser and spec to parse the blacklisted specs and use in rules.

Signed-off-by: Ryan Blakley <[email protected]>
  • Loading branch information
ryan-blakley authored Feb 6, 2023
1 parent 045878f commit da20d33
Show file tree
Hide file tree
Showing 11 changed files with 135 additions and 4 deletions.
3 changes: 3 additions & 0 deletions docs/shared_parsers_catalog/blacklisted.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.. automodule:: insights.parsers.blacklisted
:members:
:show-inheritance:
11 changes: 11 additions & 0 deletions insights/client/data_collector.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from subprocess import Popen, PIPE, STDOUT
from tempfile import NamedTemporaryFile

from insights.core.blacklist import BLACKLISTED_SPECS
from insights.util import mangle
from ..contrib.soscleaner import SOSCleaner
from .utilities import _expand_paths, get_version_info, systemd_notify_init_thread, get_tags
Expand Down Expand Up @@ -132,6 +133,10 @@ def _write_blacklist_report(self, blacklist_report):
self.archive.add_metadata_to_archive(
json.dumps(blacklist_report), '/blacklist_report')

if BLACKLISTED_SPECS:
self.archive.add_metadata_to_archive(
json.dumps({"specs": BLACKLISTED_SPECS}), '/blacklisted_specs.txt')

def _write_egg_release(self):
logger.debug("Writing egg release to archive...")
egg_release = ''
Expand Down Expand Up @@ -327,11 +332,13 @@ def run_collection(self, conf, rm_conf, branch_info, blacklist_report):
'insights_commands', mangle.mangle_command(c['command']))
if c['command'] in rm_commands or c.get('symbolic_name') in rm_commands:
logger.warn("WARNING: Skipping command %s", c['command'])
BLACKLISTED_SPECS.append(c['symbolic_name'])
elif self.mountpoint == "/" or c.get("image"):
cmd_specs = self._parse_command_spec(c, conf['pre_commands'])
for s in cmd_specs:
if s['command'] in rm_commands:
logger.warn("WARNING: Skipping command %s", s['command'])
BLACKLISTED_SPECS.append(s['symbolic_name'])
continue
cmd_spec = InsightsCommand(self.config, s, self.mountpoint)
self.archive.add_to_archive(cmd_spec)
Expand All @@ -343,12 +350,14 @@ def run_collection(self, conf, rm_conf, branch_info, blacklist_report):
for f in conf['files']:
if f['file'] in rm_files or f.get('symbolic_name') in rm_files:
logger.warn("WARNING: Skipping file %s", f['file'])
BLACKLISTED_SPECS.append(f['symbolic_name'])
else:
file_specs = self._parse_file_spec(f)
for s in file_specs:
# filter files post-wildcard parsing
if s['file'] in rm_conf.get('files', []):
logger.warn("WARNING: Skipping file %s", s['file'])
BLACKLISTED_SPECS.append(s['symbolic_name'])
else:
file_spec = InsightsFile(s, self.mountpoint)
self.archive.add_to_archive(file_spec)
Expand All @@ -361,11 +370,13 @@ def run_collection(self, conf, rm_conf, branch_info, blacklist_report):
if g.get('symbolic_name') in rm_files:
# ignore glob via symbolic name
logger.warn("WARNING: Skipping file %s", g['glob'])
BLACKLISTED_SPECS.append(g['symbolic_name'])
else:
glob_specs = self._parse_glob_spec(g)
for g in glob_specs:
if g['file'] in rm_files:
logger.warn("WARNING: Skipping file %s", g['file'])
BLACKLISTED_SPECS.append(g['symbolic_name'])
else:
glob_spec = InsightsFile(g, self.mountpoint)
self.archive.add_to_archive(glob_spec)
Expand Down
43 changes: 43 additions & 0 deletions insights/collect.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"""
from __future__ import print_function
import argparse
import json
import logging
import os
import sys
Expand All @@ -19,6 +20,7 @@

from insights import apply_configs, apply_default_enabled, get_pool
from insights.core import blacklist, dr, filters
from insights.core.blacklist import BLACKLISTED_SPECS
from insights.core.exceptions import CalledProcessError
from insights.core.serde import Hydration
from insights.util import fs
Expand Down Expand Up @@ -402,6 +404,7 @@ def collect(manifest=default_manifest, tmp_path=None, compress=False, rm_conf=No
log.warning('WARNING: Unknown component in blacklist: %s' % component)
else:
dr.set_enabled(component, enabled=False)
BLACKLISTED_SPECS.append(component.split('.')[-1])
log.warning('WARNING: Skipping component: %s', component)

to_persist = get_to_persist(client.get("persist", set()))
Expand Down Expand Up @@ -438,6 +441,11 @@ def collect(manifest=default_manifest, tmp_path=None, compress=False, rm_conf=No
broker.add_observer(h.make_persister(to_persist))
dr.run_all(broker=broker, pool=pool)

if BLACKLISTED_SPECS:
_write_out_blacklisted_specs(output_path)
# Delete the list so the specs aren't written again by the client.
del BLACKLISTED_SPECS[:]

collect_errors = _parse_broker_exceptions(broker, EXCEPTIONS_TO_REPORT)

if compress:
Expand Down Expand Up @@ -473,6 +481,41 @@ def _parse_broker_exceptions(broker, exceptions_to_report):
return errors


def _write_out_blacklisted_specs(output_path):
"""
Write out the blacklisted specs to blacklisted_specs.txt, and create
a meta-data file for this file. That way it can be loaded when the
archive is processed.
Args:
output_path (str): Path of the output directory.
"""
if os.path.exists(os.path.join(output_path, "meta_data")):
output_path_root = os.path.join(output_path, "data")
else:
output_path_root = output_path

with open(os.path.join(output_path_root, "blacklisted_specs.txt"), "w") as of:
json.dump({"specs": BLACKLISTED_SPECS}, of)

doc = {
"name": "insights.specs.Specs.blacklisted_specs",
"exec_time": 0.0,
"errors": [],
"results": {
"type": "insights.core.spec_factory.DatasourceProvider",
"object": {
"relative_path": "blacklisted_specs.txt"
}
},
"ser_time": 0.0
}

meta_path = os.path.join(os.path.join(output_path, "meta_data"), "insights.specs.Specs.blacklisted_specs")
with open(meta_path, "w") as of:
json.dump(doc, of)


def main():
# Remove command line args so that they are not parsed by any called modules
# The main fxn is only invoked as a cli, if calling from another cli then
Expand Down
1 change: 1 addition & 0 deletions insights/core/blacklist.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import re


BLACKLISTED_SPECS = []
_FILE_FILTERS = set()
_COMMAND_FILTERS = set()
_PATTERN_FILTERS = set()
Expand Down
7 changes: 6 additions & 1 deletion insights/core/dr.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ def add(a, b):

from insights.contrib import importlib
from insights.contrib.toposort import toposort_flatten
from insights.core.exceptions import MissingRequirements, ParseException, SkipComponent
from insights.core.blacklist import BLACKLISTED_SPECS
from insights.core.exceptions import BlacklistedSpec, MissingRequirements, ParseException, SkipComponent
from insights.util import defaults, enum, KeyPassingDefaultDict

log = logging.getLogger(__name__)
Expand Down Expand Up @@ -1038,6 +1039,10 @@ def run_components(ordered_components, components, broker):
log.info("Trying %s" % get_name(component))
result = DELEGATES[component].process(broker)
broker[component] = result
except BlacklistedSpec as bs:
for x in get_registry_points(component):
BLACKLISTED_SPECS.append(str(x).split('.')[-1])
broker.add_exception(component, bs, traceback.format_exc())
except MissingRequirements as mr:
if log.isEnabledFor(logging.DEBUG):
name = get_name(component)
Expand Down
7 changes: 7 additions & 0 deletions insights/core/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
from insights.util import deprecated


class BlacklistedSpec(Exception):
"""
Exception to be thrown when a blacklisted spec is found.
"""
pass


class CalledProcessError(Exception):
"""
Raised if call fails.
Expand Down
6 changes: 3 additions & 3 deletions insights/core/spec_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from insights.core import blacklist, dr
from insights.core.context import ExecutionContext, FSRoots, HostContext
from insights.core.exceptions import ContentException, SkipComponent
from insights.core.exceptions import BlacklistedSpec, ContentException, SkipComponent
from insights.core.filters import _add_filter, get_filters
from insights.core.plugins import component, datasource, is_datasource
from insights.core.serde import deserializer, serializer
Expand Down Expand Up @@ -178,7 +178,7 @@ def __init__(self, relative_path, root="/", ds=None, ctx=None):
def validate(self):
if not blacklist.allow_file("/" + self.relative_path):
log.warning("WARNING: Skipping file %s", "/" + self.relative_path)
raise SkipComponent()
raise BlacklistedSpec()

if not os.path.exists(self.path):
raise ContentException("%s does not exist." % self.path)
Expand Down Expand Up @@ -333,7 +333,7 @@ def _misc_settings(self):
def validate(self):
if not blacklist.allow_command(self.cmd):
log.warning("WARNING: Skipping command %s", self.cmd)
raise SkipComponent()
raise BlacklistedSpec()

cmd = shlex.split(self.cmd)[0]
if not which(cmd, env=self._env):
Expand Down
30 changes: 30 additions & 0 deletions insights/parsers/blacklisted.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"""
BlacklistedSpecs - File ``blacklisted_specs.txt``
=================================================
"""
from insights.core import JSONParser
from insights.core.plugins import parser
from insights.specs import Specs


@parser(Specs.blacklisted_specs)
class BlacklistedSpecs(JSONParser):
"""
Parses the blacklisted_specs.txt file generated on archive creation.
Typical output::
"{"specs": ["insights.specs.default.DefaultSpecs.dmesg", "insights.specs.default.DefaultSpecs.fstab"]}"
Attributes:
specs (list): List of blacklisted specs.
Examples:
>>> type(specs)
<class 'insights.parsers.blacklisted.BlacklistedSpecs'>
>>> result = ['insights.specs.default.DefaultSpecs.dmesg', 'insights.specs.default.DefaultSpecs.fstab']
>>> specs.specs == result
True
"""
@property
def specs(self):
return self.data['specs']
1 change: 1 addition & 0 deletions insights/specs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class Specs(SpecSet):
azure_instance_type = RegistryPoint()
bdi_read_ahead_kb = RegistryPoint(multi_output=True)
bios_uuid = RegistryPoint()
blacklisted_specs = RegistryPoint()
blkid = RegistryPoint()
bond = RegistryPoint(multi_output=True)
bond_dynamic_lb = RegistryPoint(multi_output=True)
Expand Down
1 change: 1 addition & 0 deletions insights/specs/insights_archive.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class InsightsArchiveSpecs(Specs):
azure_instance_type = simple_file("insights_commands/python_-m_insights.tools.cat_--no-header_azure_instance_type")
azure_instance_plan = simple_file("insights_commands/python_-m_insights.tools.cat_--no-header_azure_instance_plan")
bios_uuid = simple_file("insights_commands/dmidecode_-s_system-uuid")
blacklisted_specs = simple_file("blacklisted_specs.txt")
blkid = simple_file("insights_commands/blkid_-c_.dev.null")
brctl_show = simple_file("insights_commands/brctl_show")
ceph_df_detail = first_file(["insights_commands/ceph_df_detail_-f_json-pretty", "insights_commands/ceph_df_detail_-f_json"])
Expand Down
29 changes: 29 additions & 0 deletions insights/tests/parsers/test_blacklist.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import doctest
import pytest

from insights.core.exceptions import SkipComponent
from insights.parsers import blacklisted
from insights.parsers.blacklisted import BlacklistedSpecs
from insights.tests import context_wrap


SPECS = '{"specs": ["insights.specs.default.DefaultSpecs.dmesg", "insights.specs.default.DefaultSpecs.fstab"]}'


def test_blacklisted_doc_examples():
env = {
"specs": BlacklistedSpecs(context_wrap(SPECS)),
}
failed, total = doctest.testmod(blacklisted, globs=env)
assert failed == 0


def test_skip():
with pytest.raises(SkipComponent) as ex:
BlacklistedSpecs(context_wrap(""))
assert "Empty output." in str(ex)


def test_blacklist_specs():
bs = BlacklistedSpecs(context_wrap(SPECS))
assert bs.specs[0] == "insights.specs.default.DefaultSpecs.dmesg"

0 comments on commit da20d33

Please sign in to comment.