]> ruderich.org/simon Gitweb - safcm/safcm.git/blob - cmd/safcm/config/templates.go
01cfb19af1330310b692cb91492e3910b63865f1
[safcm/safcm.git] / cmd / safcm / config / templates.go
1 // Config: parse templates.yaml and expand templates
2
3 // Copyright (C) 2021-2024  Simon Ruderich
4 //
5 // This program is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18 package config
19
20 import (
21         "bytes"
22         "fmt"
23         "os"
24         "path/filepath"
25         "strings"
26         "text/template"
27
28         "gopkg.in/yaml.v2"
29
30         "ruderich.org/simon/safcm"
31 )
32
33 func LoadTemplates(group string, files map[string]*safcm.File,
34         host string, groups []string,
35         allHosts *Hosts, allGroups map[string][]string) error {
36
37         path := filepath.Join(group, "templates.yaml")
38
39         var templates []string
40         x, err := os.ReadFile(path)
41         if err != nil {
42                 if os.IsNotExist(err) {
43                         return nil
44                 }
45                 return err
46         }
47         err = yaml.UnmarshalStrict(x, &templates)
48         if err != nil {
49                 return fmt.Errorf("%s: failed to load: %v", path, err)
50         }
51
52         groupsMap := make(map[string]bool)
53         for _, x := range groups {
54                 groupsMap[x] = true
55         }
56         allHostsMap := make(map[string]bool)
57         for x := range allHosts.Map {
58                 allHostsMap[x] = true
59         }
60         allGroupsMap := make(map[string]bool)
61         for x := range allGroups {
62                 allGroupsMap[x] = true
63         }
64
65         for _, x := range templates {
66                 f, ok := files[x]
67                 if !ok {
68                         return fmt.Errorf("%s: %q does not exist in files/",
69                                 path, x)
70                 }
71                 if f.Mode.Type() != 0 /* regular file */ {
72                         return fmt.Errorf("%s: %q is not a regular file",
73                                 path, x)
74                 }
75
76                 tmplPath := filepath.Join(group, "files", x)
77
78                 // Parse and expand template
79                 var buf bytes.Buffer
80                 tmpl, err := template.New(tmplPath).Parse(string(f.Data))
81                 if err != nil {
82                         return fmt.Errorf("%s: invalid %v", path, err)
83                 }
84                 err = tmpl.Execute(&buf, &templateArgs{
85                         host:      host,
86                         groups:    groupsMap,
87                         allHosts:  allHostsMap,
88                         allGroups: allGroupsMap,
89                 })
90                 if err != nil {
91                         return fmt.Errorf("%s: %v", path, err)
92                 }
93                 f.Data = buf.Bytes()
94         }
95
96         return nil
97 }
98
99 // templateArgs is passed to .Execute() of the template.
100 type templateArgs struct {
101         host      string
102         groups    map[string]bool
103         allHosts  map[string]bool
104         allGroups map[string]bool
105 }
106
107 // TODO: extend data passed to template
108
109 func (t *templateArgs) IsHost(host string) bool {
110         // Don't permit invalid hosts to detect typos
111         if !t.allHosts[host] {
112                 panic(fmt.Sprintf("host %q does not exist", host))
113         }
114         return t.host == host
115 }
116 func (t *templateArgs) InGroup(group string) bool {
117         // Don't permit invalid groups to detect typos; detected groups cannot
118         // be checked
119         if group != GroupAll &&
120                 !t.allGroups[group] &&
121                 !t.allHosts[group] &&
122                 !strings.HasPrefix(group, GroupDetectedPrefix) {
123                 panic(fmt.Sprintf("group %q does not exist", group))
124         }
125         return t.groups[group]
126 }