1"""
2Functions for working with Mako templates
3"""
4
5try:
6    from mako.lookup import (
7        TemplateCollection,
8        TemplateLookup,
9    )  # pylint: disable=import-error,3rd-party-module-not-gated
10
11    HAS_MAKO = True
12except ImportError:
13    HAS_MAKO = False
14
15if HAS_MAKO:
16    import os
17
18    import urllib.parse
19    import salt.fileclient
20    import salt.utils.url
21
22    class SaltMakoTemplateLookup(TemplateCollection):
23        """
24        Look up Mako template files using file:// or salt:// URLs with <%include/>
25        or <%namespace/>.
26
27        (1) Look up mako template files on local file system via files://... URL.
28            Make sure mako template file is present locally on minion beforehand.
29
30          Examples:
31            <%include   file="file:///etc/salt/lib/templates/sls-parts.mako"/>
32            <%namespace file="file:///etc/salt/lib/templates/utils.mako" import="helper"/>
33
34        (2) Look up mako template files on Salt master via salt://... URL.
35            If URL is a relative  path (without an URL scheme) then assume it's relative
36            to the directory of the salt file that's doing the lookup. If URL is an absolute
37            path then it's treated as if it has been prefixed with salt://.
38
39           Examples::
40             <%include   file="templates/sls-parts.mako"/>
41             <%include   file="salt://lib/templates/sls-parts.mako"/>
42             <%include   file="/lib/templates/sls-parts.mako"/>                 ##-- treated as salt://
43
44             <%namespace file="templates/utils.mako"/>
45             <%namespace file="salt://lib/templates/utils.mako" import="helper"/>
46             <%namespace file="/lib/templates/utils.mako" import="helper"/>     ##-- treated as salt://
47
48        """
49
50        def __init__(self, opts, saltenv="base", pillar_rend=False):
51            self.opts = opts
52            self.saltenv = saltenv
53            self._file_client = None
54            self.pillar_rend = pillar_rend
55            self.lookup = TemplateLookup(directories="/")
56            self.cache = {}
57
58        def file_client(self):
59            """
60            Setup and return file_client
61            """
62            if not self._file_client:
63                self._file_client = salt.fileclient.get_file_client(
64                    self.opts, self.pillar_rend
65                )
66            return self._file_client
67
68        def adjust_uri(self, uri, filename):
69            scheme = urllib.parse.urlparse(uri).scheme
70            if scheme in ("salt", "file"):
71                return uri
72            elif scheme:
73                raise ValueError("Unsupported URL scheme({}) in {}".format(scheme, uri))
74            return self.lookup.adjust_uri(uri, filename)
75
76        def get_template(self, uri, relativeto=None):
77            if uri.startswith("file://"):
78                proto = "file://"
79                searchpath = "/"
80                salt_uri = uri
81            else:
82                proto = "salt://"
83                if self.opts["file_client"] == "local":
84                    searchpath = self.opts["file_roots"][self.saltenv]
85                else:
86                    searchpath = [
87                        os.path.join(self.opts["cachedir"], "files", self.saltenv)
88                    ]
89                salt_uri = uri if uri.startswith(proto) else salt.utils.url.create(uri)
90                self.cache_file(salt_uri)
91
92            self.lookup = TemplateLookup(directories=searchpath)
93            return self.lookup.get_template(salt_uri[len(proto) :])
94
95        def cache_file(self, fpath):
96            if fpath not in self.cache:
97                self.cache[fpath] = self.file_client().get_file(
98                    fpath, "", True, self.saltenv
99                )
100