summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulien Dessaux2024-02-15 00:48:48 +0100
committerJulien Dessaux2024-03-07 01:00:25 +0100
commitc1f22b97e689e1bd41878a9ff358f8224322e9d6 (patch)
tree31c44aa6a670e47337194b9817c5782e5edcd2ab
parentchore(files): updated FilePromise API (diff)
downloadgonf-c1f22b97e689e1bd41878a9ff358f8224322e9d6.tar.gz
gonf-c1f22b97e689e1bd41878a9ff358f8224322e9d6.tar.bz2
gonf-c1f22b97e689e1bd41878a9ff358f8224322e9d6.zip
feat(files): implemented file permission promises
-rw-r--r--gonf/files.go51
-rw-r--r--gonf/permissions.go89
-rw-r--r--gonf/values.go38
3 files changed, 153 insertions, 25 deletions
diff --git a/gonf/files.go b/gonf/files.go
index ac6a0aa..974c19c 100644
--- a/gonf/files.go
+++ b/gonf/files.go
@@ -20,20 +20,22 @@ func init() {
// ----- Public ----------------------------------------------------------------
type FilePromise struct {
- chain []Promise
- contents Value
- err error
- filename Value
- status Status
+ chain []Promise
+ contents Value
+ err error
+ filename Value
+ permissions *Permissions
+ status Status
}
func File(filename any) *FilePromise {
return &FilePromise{
- chain: nil,
- contents: nil,
- err: nil,
- filename: interfaceToTemplateValue(filename),
- status: PROMISED,
+ chain: nil,
+ contents: nil,
+ err: nil,
+ filename: interfaceToTemplateValue(filename),
+ permissions: nil,
+ status: PROMISED,
}
}
@@ -42,6 +44,11 @@ func (f *FilePromise) Contents(contents any) *FilePromise {
return f
}
+func (f *FilePromise) Permissions(p *Permissions) *FilePromise {
+ f.permissions = p
+ return f
+}
+
func (f *FilePromise) Template(contents any) *FilePromise {
f.contents = interfaceToTemplateValue(contents)
return f
@@ -79,16 +86,28 @@ func (f *FilePromise) Resolve() {
return
}
f.status = REPAIRED
- slog.Info("file", "filename", f.filename, "status", f.status)
- for _, p := range f.chain {
- p.Resolve()
- }
}
}
- if f.status == PROMISED {
+ if f.permissions != nil {
+ var status Status
+ status, f.err = f.permissions.resolve(filename)
+ if f.status == PROMISED || status == BROKEN {
+ f.status = status
+ }
+ if f.err != nil {
+ slog.Error("file", "filename", f.filename, "status", f.status, "error", f.err)
+ return
+ }
+ }
+ if f.status == REPAIRED {
+ slog.Info("file", "filename", f.filename, "status", f.status)
+ for _, p := range f.chain {
+ p.Resolve()
+ }
+ } else {
f.status = KEPT
+ slog.Debug("file", "filename", f.filename, "status", f.status)
}
- slog.Debug("file", "filename", f.filename, "status", f.status)
}
// ----- Internal --------------------------------------------------------------
diff --git a/gonf/permissions.go b/gonf/permissions.go
new file mode 100644
index 0000000..2bd73a9
--- /dev/null
+++ b/gonf/permissions.go
@@ -0,0 +1,89 @@
+package gonf
+
+import (
+ "errors"
+ "io/fs"
+ "os"
+ "os/user"
+ "strconv"
+ "syscall"
+)
+
+type Permissions struct {
+ group Value
+ mode Value
+ user Value
+}
+
+func ModeUserGroup(mode, user, group interface{}) *Permissions {
+ return &Permissions{
+ group: interfaceToTemplateValue(group),
+ mode: interfaceToTemplateValue(mode),
+ user: interfaceToTemplateValue(user),
+ }
+}
+
+func (p *Permissions) resolve(filename string) (Status, error) {
+ g, ok := p.group.(*IntValue)
+ if !ok {
+ if group, err := user.LookupGroup(p.group.String()); err != nil {
+ return BROKEN, err
+ } else {
+ if groupId, err := strconv.Atoi(group.Gid); err != nil {
+ return BROKEN, err
+ } else {
+ g = &IntValue{groupId}
+ p.group = g
+ }
+ }
+ }
+ m, ok := p.mode.(*IntValue)
+ if !ok {
+ if i, err := strconv.Atoi(p.mode.String()); err != nil {
+ return BROKEN, err
+ } else {
+ m = &IntValue{i}
+ p.mode = m
+ }
+ }
+ u, ok := p.user.(*IntValue)
+ if !ok {
+ if user, err := user.Lookup(p.user.String()); err != nil {
+ return BROKEN, err
+ } else {
+ if userId, err := strconv.Atoi(user.Uid); err != nil {
+ return BROKEN, err
+ } else {
+ u = &IntValue{userId}
+ p.group = u
+ }
+ }
+ }
+ var status Status = KEPT
+ if fileInfo, err := os.Lstat(filename); err != nil {
+ return BROKEN, err
+ } else {
+ gv := g.Int()
+ mv := fs.FileMode(m.Int())
+ uv := u.Int()
+ if fileInfo.Mode() != mv {
+ if err := os.Chmod(filename, mv); err != nil {
+ return BROKEN, err
+ }
+ status = REPAIRED
+ }
+ if stat, ok := fileInfo.Sys().(*syscall.Stat_t); ok {
+ if stat.Gid != uint32(gv) || stat.Uid != uint32(uv) {
+ if err := os.Chown(filename, uv, gv); err != nil {
+ return BROKEN, err
+ }
+ status = REPAIRED
+ }
+ } else {
+ return BROKEN, errors.New("Unsupported operating system")
+ }
+ _ = gv
+ _ = uv
+ }
+ return status, nil
+}
diff --git a/gonf/values.go b/gonf/values.go
index b77e703..56743e2 100644
--- a/gonf/values.go
+++ b/gonf/values.go
@@ -11,12 +11,15 @@ type Value interface {
}
func interfaceToValue(v any) Value {
- if vv, ok := v.(string); ok {
- return &StringValue{vv}
- }
if vv, ok := v.([]byte); ok {
return &BytesValue{vv}
}
+ if vv, ok := v.(int); ok {
+ return &IntValue{vv}
+ }
+ if vv, ok := v.(string); ok {
+ return &StringValue{vv}
+ }
if vv, ok := v.(*VariablePromise); ok {
return vv
}
@@ -25,12 +28,15 @@ func interfaceToValue(v any) Value {
}
func interfaceToTemplateValue(v any) Value {
- if vv, ok := v.(string); ok {
- return &TemplateValue{data: vv}
- }
if vv, ok := v.([]byte); ok {
return &TemplateValue{data: string(vv)}
}
+ if vv, ok := v.(int); ok {
+ return &IntValue{vv}
+ }
+ if vv, ok := v.(string); ok {
+ return &TemplateValue{data: vv}
+ }
if vv, ok := v.(*VariablePromise); ok {
return vv
}
@@ -38,7 +44,7 @@ func interfaceToTemplateValue(v any) Value {
panic(fmt.Sprintf("interfaceToTemplateValue cannot take type %T as argument. Value was %#v.", v, v))
}
-// ----- BytesValue -----------------------------------------------------------------
+// ----- BytesValue ------------------------------------------------------------
type BytesValue struct {
value []byte
}
@@ -46,12 +52,26 @@ type BytesValue struct {
func (b BytesValue) Bytes() []byte {
return b.value
}
-
func (b BytesValue) String() string {
return string(b.value[:])
}
-// ----- StringValue ----------------------------------------------------------------
+// ----- IntValue --------------------------------------------------------------
+type IntValue struct {
+ value int
+}
+
+func (i IntValue) Bytes() []byte {
+ return []byte(string(i.value))
+}
+func (i IntValue) Int() int {
+ return i.value
+}
+func (i IntValue) String() string {
+ return string(i.value)
+}
+
+// ----- StringValue -----------------------------------------------------------
type StringValue struct {
value string
}