ensure/file/main.go

165 lines
3.3 KiB
Go

package file
import (
"bytes"
"crypto/sha256"
"fmt"
"io"
"io/ioutil"
"os"
"os/user"
"strconv"
"syscall"
)
var (
DefaultPerm os.FileMode = 0740
)
type (
F struct {
Path string
Mode os.FileMode
IsDir bool // IsDir ensures the resource is a directory.
Content []byte
Owner string
Group string
}
)
// Is will check if the file resource exists.
func (f *F) Is() bool {
if f.Path == "" {
return false
}
if f.Mode == 0 {
f.Mode = DefaultPerm
}
stat := syscall.Stat_t{}
if err := syscall.Stat(f.Path, &stat); err != nil && os.IsNotExist(err) {
return false
} else if err != nil {
return false
}
mode := (os.FileMode)(stat.Mode)
if mode.IsDir() != f.IsDir {
return false
} else if f.Mode != 0 && mode.Perm() != f.Mode {
return false
}
if f.Owner != "" {
uid, err := getUID(f.Owner)
if err != nil {
return false
}
if int(stat.Uid) != uid {
return false
}
}
if f.Group != "" {
gid, err := getGID(f.Group)
if err != nil {
return false
}
if int(stat.Gid) != gid {
return false
}
}
want := sha256.New()
is := sha256.New()
file, err := os.Open(f.Path)
if err != nil {
return false
}
defer file.Close()
l, err := io.Copy(is, file)
if l != int64(len(f.Content)) {
return false
}
want.Write(f.Content)
if bytes.Compare(want.Sum(nil), is.Sum(nil)) != 0 {
return false
}
return true
}
// Ensure writes the file out onto the disk.
func (f *F) Ensure() error {
if f.Path == "" {
return fmt.Errorf("path is empty")
}
if f.Mode == 0 {
f.Mode = DefaultPerm
}
if f.IsDir && len(f.Content) > 0 {
return fmt.Errorf("directory can't have content")
}
if f.IsDir {
if err := os.Mkdir(f.Path, f.Mode); err != nil && !os.IsExist(err) {
return fmt.Errorf("could not create directory '%s': %w", f.Path, err)
}
} else {
if err := ioutil.WriteFile(f.Path, f.Content, f.Mode); err != nil {
return fmt.Errorf("could not write file '%s': %w", f.Path, err)
}
}
if stat, err := os.Stat(f.Path); err != nil {
return fmt.Errorf("could not check resource '%s': %w", f.Path, err)
} else if stat.Mode() != f.Mode {
if err := os.Chmod(f.Path, os.FileMode(f.Mode)); err != nil {
return fmt.Errorf("could not set resource '%s' mode: %w", f.Path, err)
}
}
if f.Owner != "" {
uid, err := getUID(f.Owner)
if err != nil {
return fmt.Errorf("could not get user id '%s': %w", f.Owner, err)
}
if err := os.Chown(f.Path, uid, -1); err != nil {
return fmt.Errorf("could not set owner for '%s': %w", f.Path, err)
}
}
if f.Group != "" {
gid, err := getGID(f.Group)
if err != nil {
return fmt.Errorf("could not get group id '%s': %w", f.Group, err)
}
if err := os.Chown(f.Path, -1, gid); err != nil {
return fmt.Errorf("could not set group for '%s': %w", f.Path, err)
}
}
return nil
}
func getUID(name string) (int, error) {
user, err := user.Lookup(name)
if err != nil {
return 0, fmt.Errorf("could not get user id: %w", err)
}
uid, err := strconv.Atoi(user.Uid)
if err != nil {
return 0, fmt.Errorf("could not get user id: %w", err)
}
return uid, nil
}
func getGID(name string) (int, error) {
user, err := user.LookupGroup(name)
if err != nil {
return 0, fmt.Errorf("could not get user id: %w", err)
}
gid, err := strconv.Atoi(user.Gid)
if err != nil {
return 0, fmt.Errorf("could not get user id: %w", err)
}
return gid, nil
}