package provision import ( "crypto/rand" "fmt" "os" "text/template" ) // newUUID returns a RFC-4122 v4 UUID string. Self-contained (crypto/rand) so // the package does not depend on the not-yet-implemented idgen package. func newUUID() (string, error) { var b [16]byte if _, err := rand.Read(b[:]); err != nil { return "", fmt.Errorf("provision: uuid entropy: %w", err) } b[6] = (b[6] & 0x0f) | 0x40 // version 4 b[8] = (b[8] & 0x3f) | 0x80 // variant 10 return fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:16]), nil } // TemplateRenderer renders the node cloud-init document from a Go text/template // file (infra/cloud-init/node.yaml.tmpl). It satisfies CloudInitRenderer. // // The rendered output carries the one-time bootstrap token and MUST NOT be // logged. text/template (not html/template) is used because the output is YAML. type TemplateRenderer struct { tmpl *template.Template } // NewTemplateRenderer parses the cloud-init template file at path. func NewTemplateRenderer(path string) (*TemplateRenderer, error) { raw, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("provision: read cloud-init template: %w", err) } return NewTemplateRendererFromString(string(raw)) } // NewTemplateRendererFromString parses an in-memory template (used in tests). func NewTemplateRendererFromString(body string) (*TemplateRenderer, error) { t, err := template.New("node-cloud-init").Option("missingkey=error").Parse(body) if err != nil { return nil, fmt.Errorf("provision: parse cloud-init template: %w", err) } return &TemplateRenderer{tmpl: t}, nil } // Render executes the template against data. func (r *TemplateRenderer) Render(data CloudInitData) (string, error) { var sb stringsBuilder if err := r.tmpl.Execute(&sb, data); err != nil { return "", fmt.Errorf("provision: render cloud-init: %w", err) } return sb.String(), nil } // stringsBuilder is a tiny alias to avoid importing strings just for Builder // alongside the strings import elsewhere; kept local for clarity. type stringsBuilder struct{ buf []byte } func (s *stringsBuilder) Write(p []byte) (int, error) { s.buf = append(s.buf, p...) return len(p), nil } func (s *stringsBuilder) String() string { return string(s.buf) }