From 93b22a886a12cde2886616298446fcc3eee1bd4d Mon Sep 17 00:00:00 2001 From: Julien Dessaux Date: Tue, 12 Mar 2024 23:23:10 +0100 Subject: feat(files): support creating intermediate directories --- pkg/files.go | 37 +++++++++++++++++++++++++------------ pkg/utils.go | 26 ++++++++++++++++++++++++++ stdlib/os/debian/apt.go | 13 +++++++++++-- 3 files changed, 62 insertions(+), 14 deletions(-) diff --git a/pkg/files.go b/pkg/files.go index 35420d4..379b437 100644 --- a/pkg/files.go +++ b/pkg/files.go @@ -8,6 +8,7 @@ import ( "io/fs" "log/slog" "os" + "path/filepath" ) // ----- Globals --------------------------------------------------------------- @@ -20,22 +21,24 @@ func init() { // ----- Public ---------------------------------------------------------------- type FilePromise struct { - chain []Promise - contents Value - err error - filename Value - permissions *Permissions - status Status + chain []Promise + contents Value + dirPermissions *Permissions + err error + filename Value + permissions *Permissions + status Status } func File(filename any) *FilePromise { return &FilePromise{ - chain: nil, - contents: nil, - err: nil, - filename: interfaceToTemplateValue(filename), - permissions: nil, - status: PROMISED, + chain: nil, + contents: nil, + dirPermissions: nil, + err: nil, + filename: interfaceToTemplateValue(filename), + permissions: nil, + status: PROMISED, } } @@ -44,6 +47,11 @@ func (f *FilePromise) Contents(contents any) *FilePromise { return f } +func (f *FilePromise) DirectoriesPermissions(p *Permissions) *FilePromise { + f.dirPermissions = p + return f +} + func (f *FilePromise) Permissions(p *Permissions) *FilePromise { f.permissions = p return f @@ -67,6 +75,11 @@ func (f *FilePromise) Promise() Promise { func (f *FilePromise) Resolve() { 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 { var sumFile []byte sumFile, f.err = sha256sumOfFile(filename) diff --git a/pkg/utils.go b/pkg/utils.go index f62d1c3..c3301f2 100644 --- a/pkg/utils.go +++ b/pkg/utils.go @@ -2,6 +2,10 @@ package gonf import ( "crypto/sha256" + "errors" + "io/fs" + "os" + "path/filepath" ) 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 } +// 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 { h := sha256.New() h.Write(contents) diff --git a/stdlib/os/debian/apt.go b/stdlib/os/debian/apt.go index 5ece696..aad8b65 100644 --- a/stdlib/os/debian/apt.go +++ b/stdlib/os/debian/apt.go @@ -26,12 +26,21 @@ var apt_norecommends []byte var sources_list []byte func Promise() { + rootDir := gonf.ModeUserGroup(0755, "root", "root") rootRO := gonf.ModeUserGroup(0444, "root", "root") gonf.Default("debian-release", "stable") gonf.AppendVariable("debian-extra-sources", "# Extra sources") 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/apt.conf.d/99_norecommends").Permissions(rootRO).Template(apt_norecommends).Promise() + gonf.File("/etc/apt/sources.list"). + 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.Service("opensmtpd").State("enabled", "started").Promise() systemd.Promise() -- cgit v1.2.3