// Config: parse templates.yaml and expand templates // Copyright (C) 2021-2024 Simon Ruderich // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . package config import ( "bytes" "fmt" "os" "path/filepath" "strings" "text/template" "gopkg.in/yaml.v2" "ruderich.org/simon/safcm" ) func LoadTemplates(group string, files map[string]*safcm.File, host string, groups []string, allHosts *Hosts, allGroups map[string][]string) error { path := filepath.Join(group, "templates.yaml") var templates []string x, err := os.ReadFile(path) if err != nil { if os.IsNotExist(err) { return nil } return err } err = yaml.UnmarshalStrict(x, &templates) if err != nil { return fmt.Errorf("%s: failed to load: %v", path, err) } groupsMap := make(map[string]bool) for _, x := range groups { groupsMap[x] = true } allHostsMap := make(map[string]bool) for x := range allHosts.Map { allHostsMap[x] = true } allGroupsMap := make(map[string]bool) for x := range allGroups { allGroupsMap[x] = true } for _, x := range templates { f, ok := files[x] if !ok { return fmt.Errorf("%s: %q does not exist in files/", path, x) } if f.Mode.Type() != 0 /* regular file */ { return fmt.Errorf("%s: %q is not a regular file", path, x) } tmplPath := filepath.Join(group, "files", x) // Parse and expand template var buf bytes.Buffer tmpl, err := template.New(tmplPath).Parse(string(f.Data)) if err != nil { return fmt.Errorf("%s: invalid %v", path, err) } err = tmpl.Execute(&buf, &templateArgs{ host: host, groups: groupsMap, allHosts: allHostsMap, allGroups: allGroupsMap, }) if err != nil { return fmt.Errorf("%s: %v", path, err) } f.Data = buf.Bytes() } return nil } // templateArgs is passed to .Execute() of the template. type templateArgs struct { host string groups map[string]bool allHosts map[string]bool allGroups map[string]bool } // TODO: extend data passed to template func (t *templateArgs) IsHost(host string) bool { // Don't permit invalid hosts to detect typos if !t.allHosts[host] { panic(fmt.Sprintf("host %q does not exist", host)) } return t.host == host } func (t *templateArgs) InGroup(group string) bool { // Don't permit invalid groups to detect typos; detected groups cannot // be checked if group != GroupAll && !t.allGroups[group] && !t.allHosts[group] && !strings.HasPrefix(group, GroupDetectedPrefix) { panic(fmt.Sprintf("group %q does not exist", group)) } return t.groups[group] }