]> ruderich.org/simon Gitweb - safcm/safcm.git/blob - cmd/safcm/config/templates.go
Use SPDX license identifiers
[safcm/safcm.git] / cmd / safcm / config / templates.go
1 // Config: parse templates.yaml and expand templates
2
3 // SPDX-License-Identifier: GPL-3.0-or-later
4 // Copyright (C) 2021-2024  Simon Ruderich
5
6 package config
7
8 import (
9         "bytes"
10         "fmt"
11         "os"
12         "path/filepath"
13         "strings"
14         "text/template"
15
16         "gopkg.in/yaml.v2"
17
18         "ruderich.org/simon/safcm"
19 )
20
21 func LoadTemplates(group string, files map[string]*safcm.File,
22         host string, groups []string,
23         allHosts *Hosts, allGroups map[string][]string) error {
24
25         path := filepath.Join(group, "templates.yaml")
26
27         var templates []string
28         x, err := os.ReadFile(path)
29         if err != nil {
30                 if os.IsNotExist(err) {
31                         return nil
32                 }
33                 return err
34         }
35         err = yaml.UnmarshalStrict(x, &templates)
36         if err != nil {
37                 return fmt.Errorf("%s: failed to load: %v", path, err)
38         }
39
40         groupsMap := make(map[string]bool)
41         for _, x := range groups {
42                 groupsMap[x] = true
43         }
44         allHostsMap := make(map[string]bool)
45         for x := range allHosts.Map {
46                 allHostsMap[x] = true
47         }
48         allGroupsMap := make(map[string]bool)
49         for x := range allGroups {
50                 allGroupsMap[x] = true
51         }
52
53         for _, x := range templates {
54                 f, ok := files[x]
55                 if !ok {
56                         return fmt.Errorf("%s: %q does not exist in files/",
57                                 path, x)
58                 }
59                 if f.Mode.Type() != 0 /* regular file */ {
60                         return fmt.Errorf("%s: %q is not a regular file",
61                                 path, x)
62                 }
63
64                 tmplPath := filepath.Join(group, "files", x)
65
66                 // Parse and expand template
67                 var buf bytes.Buffer
68                 tmpl, err := template.New(tmplPath).Parse(string(f.Data))
69                 if err != nil {
70                         return fmt.Errorf("%s: invalid %v", path, err)
71                 }
72                 err = tmpl.Execute(&buf, &templateArgs{
73                         host:      host,
74                         groups:    groupsMap,
75                         allHosts:  allHostsMap,
76                         allGroups: allGroupsMap,
77                 })
78                 if err != nil {
79                         return fmt.Errorf("%s: %v", path, err)
80                 }
81                 f.Data = buf.Bytes()
82         }
83
84         return nil
85 }
86
87 // templateArgs is passed to .Execute() of the template.
88 type templateArgs struct {
89         host      string
90         groups    map[string]bool
91         allHosts  map[string]bool
92         allGroups map[string]bool
93 }
94
95 // TODO: extend data passed to template
96
97 func (t *templateArgs) IsHost(host string) bool {
98         // Don't permit invalid hosts to detect typos
99         if !t.allHosts[host] {
100                 panic(fmt.Sprintf("host %q does not exist", host))
101         }
102         return t.host == host
103 }
104 func (t *templateArgs) InGroup(group string) bool {
105         // Don't permit invalid groups to detect typos; detected groups cannot
106         // be checked
107         if group != GroupAll &&
108                 !t.allGroups[group] &&
109                 !t.allHosts[group] &&
110                 !strings.HasPrefix(group, GroupDetectedPrefix) {
111                 panic(fmt.Sprintf("group %q does not exist", group))
112         }
113         return t.groups[group]
114 }