summaryrefslogtreecommitdiff
path: root/postgresqleu/confreg/contextutil.py
blob: 101e7cd4a7ccdc2a70ddd5feba133a234b34ed0b (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
import os
import json
import logging
import copy

from postgresqleu.util.context_processors import settings_context


try:
    import yaml
    has_yaml = True
except ImportError:
    has_yaml = False


# XXX: keep in sync with deploystatic.py!
def deep_update_context(target, source):
    for k, v in source.items():
        if type(v) == dict:
            # If this is a dict stored in the dict
            if k not in target:
                # Target didn't have it, so copy it over
                target[k] = copy.deepcopy(v)
            elif type(target[k]) != dict:
                # Target had something but it's not a dict, so overwrite it
                target[k] = copy.deepcopy(v)
            else:
                deep_update_context(target[k], v)
        else:
            target[k] = copy.copy(v)


def _load_context_file(filename, ignore_exceptions=True):
    try:
        with open(filename, encoding='utf8') as f:
            if filename.endswith('.json'):
                return json.load(f)
            else:
                return yaml.safe_load(f)
    except ValueError as e:
        # Malformatted JSON -- pass it through as an exception
        raise
    except Exception:
        if not ignore_exceptions:
            raise
        return {}


def load_base_context(rootdir):
    c = {}
    if os.path.isfile(os.path.join(rootdir, 'templates/context.json')):
        deep_update_context(c, _load_context_file(os.path.join(rootdir, 'templates/context.json')))
    if has_yaml and os.path.isfile(os.path.join(rootdir, 'templates/context.yaml')):
        deep_update_context(c, _load_context_file(os.path.join(rootdir, 'templates/context.yaml')))
    return c


def load_override_context(rootdir):
    # Load contexts in override directory, if any
    c = {}
    if os.path.isdir(os.path.join(rootdir, 'templates/context.override.d')):
        for fn in sorted(os.listdir(os.path.join(rootdir, 'templates/context.override.d'))):
            if fn.endswith('.json') or (has_yaml and fn.endswith('.yaml')):
                try:
                    deep_update_context(c, _load_context_file(os.path.join(rootdir, 'templates/context.override.d', fn), False))
                except Exception as e:
                    logging.getLogger(__name__).warning(
                        'Failed to load context file {}: {}'.format(os.path.join(rootdir, 'templates/context.override.d', fn), e)
                    )
    return c


def update_with_override_context(context, rootdir):
    deep_update_context(context, load_override_context(rootdir))


# Locate the git revision for a repository in the given path, including
# walking up the tree to find it if the specified path is not the root.
def find_git_revision(path):
    while path != '/':
        if os.path.exists(os.path.join(path, ".git/HEAD")):
            # Found it!
            with open(os.path.join(path, '.git/HEAD')) as f:
                ref = f.readline().strip()
            if not ref.startswith('ref: refs/heads/'):
                return None
            refname = os.path.join(path, ".git/", ref[5:])
            if not os.path.isfile(refname):
                return None
            with open(refname) as f:
                fullref = f.readline()
                return fullref[:7]
        elif os.path.exists(os.path.join(path, ".deploystatic_githash")):
            with open(os.path.join(path, ".deploystatic_githash")) as f:
                return f.readline().strip()

        # Else step up one level
        path = os.path.dirname(path)
    # If no direct git hash found, search for a deploystatic file
    return None


def load_all_context(conference, inject, dictionary=None):
    if conference and conference.jinjaenabled and conference.jinjadir:
        try:
            c = load_base_context(conference.jinjadir)
        except ValueError as e:
            return HttpResponse("JSON parse failed: {0}".format(e), content_type="text/plain")
    else:
        c = {}

    c.update(inject)

    if conference and conference.jinjaenabled and conference.jinjadir:
        c['githash'] = find_git_revision(conference.jinjadir)

    if dictionary:
        c.update(dictionary)

    if conference and conference.jinjaenabled and conference.jinjadir:
        update_with_override_context(c, conference.jinjadir)

    c.update(settings_context())

    return c