Passed
Pull Request — master (#5209)
by Matěj
02:19
created

compile_profiles.make_name_to_profile_mapping()   A

Complexity

Conditions 3

Size

Total Lines 12
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 11
nop 1
dl 0
loc 12
ccs 0
cts 10
cp 0
crap 12
rs 9.85
c 0
b 0
f 0
1
from __future__ import print_function
2
3
import argparse
4
import sys
5
import os.path
6
from glob import glob
7
8
import ssg.build_yaml
9
10
11
class ResolvableProfile(ssg.build_yaml.Profile):
12
    def __init__(self, * args, ** kwargs):
13
        super(ResolvableProfile, self).__init__(* args, ** kwargs)
14
        self.resolved = False
15
16
    def resolve(self, all_profiles):
17
        if self.resolved:
18
            return
19
20
        resolved_selections = set(self.selected)
21
        if self.extends:
22
            if self.extends not in all_profiles:
23
                msg = (
24
                    "Profile {name} extends profile {extended}, but"
25
                    "only profiles {known_profiles} are available for resolution."
26
                    .format(name=self.id_, extended=self.extends,
27
                            profiles=list(all_profiles.keys())))
28
                raise RuntimeError(msg)
29
            extended_profile = all_profiles[self.extends]
30
            extended_profile.resolve(all_profiles)
31
32
            extended_selects = set(extended_profile.selected)
33
            resolved_selections.update(extended_selects)
34
35
            updated_variables = dict(extended_profile.variables)
36
            updated_variables.update(self.variables)
37
            self.variables = updated_variables
38
39
            updated_refinements = dict(extended_profile.refine_rules)
40
            updated_refinements.update(self.refine_rules)
41
            self.refine_rules = updated_refinements
42
43
        for uns in self.unselected:
44
            resolved_selections.discard(uns)
45
46
        self.unselected = []
47
        self.selected = sorted(resolved_selections)
48
49
        self.resolved = True
50
51
52
def create_parser():
53
    parser = argparse.ArgumentParser()
54
    parser.add_argument("profile_file", nargs="*")
55
    parser.add_argument(
56
        "--build-config-yaml",
57
        help="YAML file with information about the build configuration. "
58
        "e.g.: ~/scap-security-guide/build/build_config.yml "
59
        "needed for autodetection of profile root"
60
    )
61
    parser.add_argument(
62
        "--product-yaml",
63
        help="YAML file with information about the product we are building. "
64
        "e.g.: ~/scap-security-guide/rhel7/product.yml "
65
        "needed for autodetection of profile root"
66
    )
67
    parser.add_argument(
68
        "--output", "-o", default="{name}.profile",
69
        help="The template for saving processed profile files."
70
    )
71
    return parser
72
73
74
def make_name_to_profile_mapping(profile_files):
75
    name_to_profile = {}
76
    for f in profile_files:
77
        try:
78
            p = ResolvableProfile.from_yaml(f)
79
            name_to_profile[p.id_] = p
80
        except Exception as exc:
81
            # The profile is probably doc-incomplete
82
            msg = "Not building profile from {fname}: {err}".format(
83
                fname=f, err=str(exc))
84
            print(msg, file=sys.stderr)
85
    return name_to_profile
86
87
88
def get_env_yaml(build_config_yaml, product_yaml):
89
    if build_config_yaml is None or product_yaml is None:
90
        return None
91
92
    env_yaml = ssg.yaml.open_environment(build_config_yaml, product_yaml)
93
    return env_yaml
94
95
96
def get_profile_files_from_root(env_yaml, product_yaml):
97
    profile_files = []
98
    if env_yaml:
99
        base_dir = os.path.dirname(product_yaml)
100
        profiles_root = ssg.utils.required_key(env_yaml, "profiles_root")
101
        profile_files = glob("{base_dir}/{profiles_root}/*.profile"
102
                             .format(profiles_root=profiles_root, base_dir=base_dir))
103
    return profile_files
104
105
106
def main():
107
    parser = create_parser()
108
    args = parser.parse_args()
109
    env_yaml = get_env_yaml(args.build_config_yaml, args.product_yaml)
110
111
    profile_files = get_profile_files_from_root(env_yaml, args.product_yaml)
112
    profile_files.extend(args.profile_file)
113
    profiles = make_name_to_profile_mapping(profile_files)
114
    for pname in profiles:
115
        profiles[pname].resolve(profiles)
116
117
    for name, p in profiles.items():
118
        p.dump_yaml(args.output.format(name=name))
119
120
121
if __name__ == "__main__":
122
    main()
123