feat(files): support creating intermediate directories

This commit is contained in:
Julien Dessaux 2024-03-12 23:23:10 +01:00
parent b40723b0b8
commit 93b22a886a
Signed by: adyxax
GPG key ID: F92E51B86E07177E
3 changed files with 62 additions and 14 deletions

View file

@ -8,6 +8,7 @@ import (
"io/fs" "io/fs"
"log/slog" "log/slog"
"os" "os"
"path/filepath"
) )
// ----- Globals --------------------------------------------------------------- // ----- Globals ---------------------------------------------------------------
@ -20,22 +21,24 @@ func init() {
// ----- Public ---------------------------------------------------------------- // ----- Public ----------------------------------------------------------------
type FilePromise struct { type FilePromise struct {
chain []Promise chain []Promise
contents Value contents Value
err error dirPermissions *Permissions
filename Value err error
permissions *Permissions filename Value
status Status permissions *Permissions
status Status
} }
func File(filename any) *FilePromise { func File(filename any) *FilePromise {
return &FilePromise{ return &FilePromise{
chain: nil, chain: nil,
contents: nil, contents: nil,
err: nil, dirPermissions: nil,
filename: interfaceToTemplateValue(filename), err: nil,
permissions: nil, filename: interfaceToTemplateValue(filename),
status: PROMISED, permissions: nil,
status: PROMISED,
} }
} }
@ -44,6 +47,11 @@ func (f *FilePromise) Contents(contents any) *FilePromise {
return f return f
} }
func (f *FilePromise) DirectoriesPermissions(p *Permissions) *FilePromise {
f.dirPermissions = p
return f
}
func (f *FilePromise) Permissions(p *Permissions) *FilePromise { func (f *FilePromise) Permissions(p *Permissions) *FilePromise {
f.permissions = p f.permissions = p
return f return f
@ -67,6 +75,11 @@ func (f *FilePromise) Promise() Promise {
func (f *FilePromise) Resolve() { func (f *FilePromise) Resolve() {
filename := f.filename.String() filename := f.filename.String()
if f.dirPermissions != nil {
if f.status, f.err = makeDirectoriesHierarchy(filepath.Dir(filename), f.dirPermissions); f.err != nil {
return
}
}
if f.contents != nil { if f.contents != nil {
var sumFile []byte var sumFile []byte
sumFile, f.err = sha256sumOfFile(filename) sumFile, f.err = sha256sumOfFile(filename)

View file

@ -2,6 +2,10 @@ package gonf
import ( import (
"crypto/sha256" "crypto/sha256"
"errors"
"io/fs"
"os"
"path/filepath"
) )
var builtinTemplateFunctions = map[string]any{ var builtinTemplateFunctions = map[string]any{
@ -20,6 +24,28 @@ func FilterSlice[T any](slice *[]T, predicate func(T) bool) {
*slice = (*slice)[:i] // or truncated out of the slice *slice = (*slice)[:i] // or truncated out of the slice
} }
// We cannot just use os.MakedirAll because we need to set the user:group on every intermediate directories created
func makeDirectoriesHierarchy(dir string, perms *Permissions) (Status, error) {
if _, err := os.Lstat(dir); err != nil {
if errors.Is(err, fs.ErrNotExist) {
if status, err := makeDirectoriesHierarchy(filepath.Dir(dir), perms); err != nil {
return status, err
}
m, err := perms.mode.Int()
if err != nil {
return BROKEN, err
}
os.Mkdir(dir, fs.FileMode(m))
perms.resolve(dir)
return REPAIRED, nil
} else {
return BROKEN, err
}
} else {
return KEPT, nil
}
}
func sha256sum(contents []byte) []byte { func sha256sum(contents []byte) []byte {
h := sha256.New() h := sha256.New()
h.Write(contents) h.Write(contents)

View file

@ -26,12 +26,21 @@ var apt_norecommends []byte
var sources_list []byte var sources_list []byte
func Promise() { func Promise() {
rootDir := gonf.ModeUserGroup(0755, "root", "root")
rootRO := gonf.ModeUserGroup(0444, "root", "root") rootRO := gonf.ModeUserGroup(0444, "root", "root")
gonf.Default("debian-release", "stable") gonf.Default("debian-release", "stable")
gonf.AppendVariable("debian-extra-sources", "# Extra sources") gonf.AppendVariable("debian-extra-sources", "# Extra sources")
apt_update := gonf.Command("apt-get", "update", "-qq") apt_update := gonf.Command("apt-get", "update", "-qq")
gonf.File("/etc/apt/sources.list").Permissions(rootRO).Template(sources_list).Promise().IfRepaired(apt_update) gonf.File("/etc/apt/sources.list").
gonf.File("/etc/apt/apt.conf.d/99_norecommends").Permissions(rootRO).Template(apt_norecommends).Promise() Permissions(rootRO).
Template(sources_list).
Promise().
IfRepaired(apt_update)
gonf.File("/etc/apt/apt.conf.d/99_norecommends").
DirectoriesPermissions(rootDir).
Permissions(rootRO).
Template(apt_norecommends).
Promise()
gonf.SetPackagesConfiguration(packages_install, packages_list, apt_update) gonf.SetPackagesConfiguration(packages_install, packages_list, apt_update)
gonf.Service("opensmtpd").State("enabled", "started").Promise() gonf.Service("opensmtpd").State("enabled", "started").Promise()
systemd.Promise() systemd.Promise()