diff --git a/griffon/commands/queries.py b/griffon/commands/queries.py index 1391624..f2002a5 100644 --- a/griffon/commands/queries.py +++ b/griffon/commands/queries.py @@ -36,7 +36,14 @@ generate_entity_report, generate_license_report, ) -from griffon.output import console, cprint, raw_json_transform +from griffon.output import ( + console, + cprint, + generate_affects, + generate_normalised_results, + generate_result_tree, + raw_json_transform, +) from griffon.services import QueryService, core_queries # , exp logger = logging.getLogger("griffon") @@ -382,18 +389,20 @@ def get_product_contain_component( # generate affects output = raw_json_transform(q, True) - ordered_results = sorted(output["results"], key=lambda d: d["product_stream"]) - affects = [] - product_versions = sorted( - list(set([item["product_version"] for item in ordered_results])) + + exclude_products = [] + if get_config_option(ctx.obj["PROFILE"], "exclude"): + exclude_products = get_config_option(ctx.obj["PROFILE"], "exclude").split("\n") + exclude_components = [] + if get_config_option(ctx.obj["PROFILE"], "exclude_components"): + exclude_components = get_config_option( + ctx.obj["PROFILE"], "exclude_components" + ).split("\n") + normalised_results = generate_normalised_results( + output, exclude_products, exclude_components ) - for pv in product_versions: - names = [item["name"] for item in ordered_results if pv == item["product_version"]] - names = list(set(names)) - for name in names: - affects.append( - {"product_version": pv, "component_name": name, "operation": "add"} - ) + result_tree = generate_result_tree(normalised_results) + affects = generate_affects(ctx, result_tree, exclude_components, "add", format="json") # attempt to import sfm2client module try: diff --git a/griffon/output.py b/griffon/output.py index bb46d21..ac9652c 100644 --- a/griffon/output.py +++ b/griffon/output.py @@ -155,6 +155,115 @@ def text_output_product_summary(ctx, output, format, exclude_products, no_wrap=F ctx.exit() +def generate_normalised_results(output, exclude_products, exclude_components): + normalised_results = list() + if "results" in output: + for item in output["results"]: + for ps in item["product_streams"]: + if ps["product_versions"][0]["name"] not in exclude_products: + if not any([match in item["name"] for match in exclude_components]): + c = { + "product_version": ps["product_versions"][0]["name"], + "product_stream": ps.get("name"), + "namespace": item.get("namespace"), + "name": item.get("name"), + "nvr": item.get("nvr"), + "type": item.get("type"), + "arch": item.get("arch"), + "version": item.get("version"), + "related_url": item.get("related_url"), + "purl": item.get("purl"), + "sources": item.get("sources"), + "upstreams": item.get("upstreams"), + } + if "software_build" in item: + c["build_source_url"] = item["software_build"].get("source") + normalised_results.append(c) + return normalised_results + + +def generate_result_tree(normalised_results): + product_versions = sorted(list(set([item["product_version"] for item in normalised_results]))) + result_tree = {} + for pv in product_versions: + result_tree[pv] = {} + product_streams = sorted( + list( + set( + [ + item["product_stream"] + for item in normalised_results + if item["product_version"] == pv + ] + ) + ) + ) + for ps in product_streams: + result_tree[pv][ps] = {} + component_names = sorted( + list( + set( + [ + item["name"] + for item in normalised_results + if item["product_stream"] == ps + ] + ) + ) + ) + for cn in component_names: + result_tree[pv][ps][cn] = {} + nvrs = [ + item + for item in normalised_results + if item["product_stream"] == ps and item["name"] == cn + ] + + for nvr in nvrs: + result_tree[pv][ps][cn][nvr["nvr"]] = nvr + return result_tree + + +def generate_affects( + ctx, result_tree, exclude_components, flaw_operation, format="text", no_wrap=False +): + search_component_name = ctx.params["component_name"] + for pv in result_tree.keys(): + component_names = set() + for ps in result_tree[pv].keys(): + for component_name in result_tree[pv][ps].keys(): + for nvr in result_tree[pv][ps][component_name].keys(): + source_names = [ + source["name"] + for source in result_tree[pv][ps][component_name][nvr]["sources"] + if source["namespace"] == "REDHAT" + ] + component_names.update(source_names) + # we should only show component name if both {component name} and {component name-container} exists # noqa + if ( + search_component_name in component_names + and f"{search_component_name}-container" in component_names + ): + component_names.remove(f"{search_component_name}-container") + if format == "text": + for cn in component_names: + # ensure {component name} is not in profile exclude components enum + if not any([match in cn for match in exclude_components]): + console.print( + f"{pv}/{cn}={flaw_operation}", + no_wrap=no_wrap, + ) + else: + affects = [] + for cn in component_names: + # ensure {component name} is not in profile exclude components enum + if not any([match in cn for match in exclude_components]): + affects.append( + {"product_version": pv, "component_name": cn, "operation": flaw_operation} + ) + return affects + + def text_output_products_contain_component( ctx, output, exclude_products, exclude_components, no_wrap=False ): @@ -178,69 +287,10 @@ def text_output_products_contain_component( # ordered_results = sorted(output["results"], key=lambda d: d["product_stream"]) # first flatten the tree - normalised_results = list() - if "results" in output: - for item in output["results"]: - for ps in item["product_streams"]: - if ps["product_versions"][0]["name"] not in exclude_products: - if not any([match in item["name"] for match in exclude_components]): - c = { - "product_version": ps["product_versions"][0]["name"], - "product_stream": ps.get("name"), - "namespace": item.get("namespace"), - "name": item.get("name"), - "nvr": item.get("nvr"), - "type": item.get("type"), - "arch": item.get("arch"), - "version": item.get("version"), - "related_url": item.get("related_url"), - "purl": item.get("purl"), - "sources": item.get("sources"), - "upstreams": item.get("upstreams"), - } - if "software_build" in item: - c["build_source_url"] = item["software_build"].get("source") - normalised_results.append(c) - product_versions = sorted( - list(set([item["product_version"] for item in normalised_results])) + normalised_results = generate_normalised_results( + output, exclude_products, exclude_components ) - result_tree = {} - for pv in product_versions: - result_tree[pv] = {} - product_streams = sorted( - list( - set( - [ - item["product_stream"] - for item in normalised_results - if item["product_version"] == pv - ] - ) - ) - ) - for ps in product_streams: - result_tree[pv][ps] = {} - component_names = sorted( - list( - set( - [ - item["name"] - for item in normalised_results - if item["product_stream"] == ps - ] - ) - ) - ) - for cn in component_names: - result_tree[pv][ps][cn] = {} - nvrs = [ - item - for item in normalised_results - if item["product_stream"] == ps and item["name"] == cn - ] - - for nvr in nvrs: - result_tree[pv][ps][cn][nvr["nvr"]] = nvr + result_tree = generate_result_tree(normalised_results) # TODO - MAVEN component type will require special handling if ctx.params["affect_mode"]: @@ -254,30 +304,8 @@ def text_output_products_contain_component( if flaw_mode == "update": flaw_operation = "update" - for pv in result_tree.keys(): - component_names = set() - for ps in result_tree[pv].keys(): - for component_name in result_tree[pv][ps].keys(): - for nvr in result_tree[pv][ps][component_name].keys(): - source_names = [ - source["name"] - for source in result_tree[pv][ps][component_name][nvr]["sources"] - if source["namespace"] == "REDHAT" - ] - component_names.update(source_names) - # we should only show component name if both {component name} and {component name-container} exists # noqa - if ( - search_component_name in component_names - and f"{search_component_name}-container" in component_names - ): - component_names.remove(f"{search_component_name}-container") - for cn in component_names: - # ensure {component name} is not in profile exclude components enum - if not any([match in cn for match in exclude_components]): - console.print( - f"{pv}/{cn}={flaw_operation}", - no_wrap=no_wrap, - ) + generate_affects(ctx, result_tree, exclude_components, flaw_operation, no_wrap=False) + else: if ctx.obj["VERBOSE"] == 0: # product_version X component_name for pv in result_tree.keys():