aboutsummaryrefslogtreecommitdiff
path: root/scripts/resolve-profile.py.in
blob: 29054232e910c08aa07228433f676b59365b18b1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
@PYTHON@
# vim: set ts=4 et:

import os
import sys
import json
import shutil
import argparse
from datetime import datetime, timedelta

from pyhocon import ConfigFactory


# Just group together our transforms
class Transforms:

    NOW = datetime.utcnow()
    TOMORROW = NOW + timedelta(days=1)

    unquote = lambda x: x.strip('"')

    @staticmethod
    def force_iso_date(input):
        return datetime.fromisoformat(input).isoformat(timespec="seconds")

    @classmethod
    def resolve_tomorrow(cls, input):
        return cls.TOMORROW.isoformat(timespec="seconds")

    @classmethod
    def resolve_now(cls, input):
        return cls.NOW.strftime("%Y%m%d%H%M%S")

    @classmethod
    def fold_comma(cls, input):
        return ",".join([cls.unquote(k) for k in input.keys()])

    @classmethod
    def fold_space(cls, input):
        return " ".join([cls.unquote(k) for k in input.keys()])

    @classmethod
    def fold_repos(cls, input):
        return "\n".join(
            f"@{v} {cls.unquote(k)}" if isinstance(v, str) else cls.unquote(k)
            for k, v in input.items())

    @staticmethod
    def fold_packages(input):
        return " ".join(
            f"{k}@{v}" if isinstance(v, str) else k
            for k, v in input.items())

    @staticmethod
    def fold_services(input):
        return " ".join(
            "{}={}".format(k, ",".join(v.keys()))
            for k, v in input.items())


class ConfigBuilder:

    _CFG_TRANSFORMS = {
        "ami_access"     : Transforms.fold_comma,
        "ami_regions"    : Transforms.fold_comma,
        "kernel_modules" : Transforms.fold_comma,
        "kernel_options" : Transforms.fold_space,
        "repos"          : Transforms.fold_repos,
        "pkgs"           : Transforms.fold_packages,
        "svcs"           : Transforms.fold_services,
        "revision"       : Transforms.resolve_now,
        "end_of_life"    : lambda x: \
                Transforms.force_iso_date(Transforms.resolve_tomorrow(x)),
    }

    def __init__(self, config_path, out_dir):
        self.config_path = config_path
        self.out_dir = out_dir

    def build(self, profile):
        build_config = ConfigFactory.parse_file(self.config_path)

        for build, cfg in build_config["BUILDS"].items():
            build_dir = os.path.join(self.out_dir, build)

            # Always start fresh
            shutil.rmtree(build_dir, ignore_errors=True)
            os.makedirs(build_dir)

            cfg["profile"] = profile
            cfg["profile_build"] = build

            # Order of operations is important here
            for k, v in cfg.items():
                transform = self._CFG_TRANSFORMS.get(k)
                if transform:
                    cfg[k] = transform(v)

                if isinstance(v, str) and "{var." in v:
                    cfg[k] = v.format(var=cfg)

            with open(os.path.join(build_dir, "vars.json"), "w") as out:
                json.dump(cfg, out, indent=4, separators=(",", ": "))


def find_repo_root():
    path = os.getcwd()

    while ".git" not in set(os.listdir(path)) and path != "/":
        path = os.path.dirname(path)

    if path == "/":
        raise Exception("No repo found, stopping at /")

    return path


def main(args):
    parser = argparse.ArgumentParser(description="Build Packer JSON variable "
        "files from HOCON build profiles")
    parser.add_argument("profile", help="name of profile to build")
    args = parser.parse_args()

    root = find_repo_root()

    ConfigBuilder(
        os.path.join(root, "profiles", f"{args.profile}.conf"),
        os.path.join(root, "build", "profile", args.profile)
    ).build(args.profile)


if __name__ == "__main__":
    main(sys.argv)