Files
dokku/plugins/common/io.go

239 lines
4.7 KiB
Go

package common
import (
"bufio"
"fmt"
"os"
"os/user"
"strconv"
"strings"
"github.com/otiai10/copy"
)
// CatFile cats the contents of a file (if it exists)
func CatFile(filename string) {
f, err := os.Open(filename)
if err != nil {
return
}
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
LogDebug(fmt.Sprintf("line: '%s'", scanner.Text()))
}
}
// Copy copies a file/directory from src to dst. If the source is a file, it will also
// convert line endings to unix style
func Copy(src, dst string) error {
fi, err := os.Stat(src)
if err != nil {
return err
}
if !fi.Mode().IsRegular() {
return copy.Copy(src, dst)
}
// ensure file has the correct line endings
dos2unixCmd := NewShellCmd(strings.Join([]string{
"dos2unix",
"-l",
"-n",
src,
dst,
}, " "))
dos2unixCmd.ShowOutput = false
dos2unixCmd.Execute()
// ensure file permissions are correct
b, err := os.ReadFile(dst)
if err != nil {
return err
}
err = os.WriteFile(dst, b, fi.Mode())
if err != nil {
return err
}
return nil
}
// DirectoryExists returns if a path exists and is a directory
func DirectoryExists(filename string) bool {
fi, err := os.Stat(filename)
if err != nil {
return false
}
return fi.IsDir()
}
// FileToSlice reads in all the lines from a file into a string slice
func FileToSlice(filename string) (lines []string, err error) {
f, err := os.Open(filename)
if err != nil {
return
}
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
text := strings.TrimSpace(scanner.Text())
if text == "" {
continue
}
lines = append(lines, text)
}
err = scanner.Err()
return
}
// FileExists returns if a path exists and is a file
func FileExists(filename string) bool {
fi, err := os.Stat(filename)
if err != nil {
return false
}
return fi.Mode().IsRegular()
}
// IsAbsPath returns 0 if input path is absolute
func IsAbsPath(path string) bool {
return strings.HasPrefix(path, "/")
}
// ListFilesWithPrefix lists files within a given path that have a given prefix
func ListFilesWithPrefix(path string, prefix string) []string {
names, err := os.ReadDir(path)
if err != nil {
return []string{}
}
files := []string{}
for _, f := range names {
if prefix != "" && !strings.HasPrefix(f.Name(), prefix) {
continue
}
if f.Type().IsRegular() {
files = append(files, fmt.Sprintf("%s/%s", path, f.Name()))
}
}
return files
}
// ReadFirstLine gets the first line of a file that has contents and returns it
// if there are no contents, an empty string is returned
// will also return an empty string if the file does not exist
func ReadFirstLine(filename string) (text string) {
if !FileExists(filename) {
return
}
f, err := os.Open(filename)
if err != nil {
return
}
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
if text = strings.TrimSpace(scanner.Text()); text == "" {
continue
}
return
}
return
}
// SetPermissions sets the proper owner and filemode for a given file
func SetPermissions(path string, fileMode os.FileMode) error {
if err := os.Chmod(path, fileMode); err != nil {
return err
}
systemGroup := GetenvWithDefault("DOKKU_SYSTEM_GROUP", "dokku")
systemUser := GetenvWithDefault("DOKKU_SYSTEM_USER", "dokku")
if strings.HasPrefix(path, "/etc/sudoers.d/") {
systemGroup = "root"
systemUser = "root"
}
group, err := user.LookupGroup(systemGroup)
if err != nil {
return err
}
user, err := user.Lookup(systemUser)
if err != nil {
return err
}
uid, err := strconv.Atoi(user.Uid)
if err != nil {
return err
}
gid, err := strconv.Atoi(group.Gid)
if err != nil {
return err
}
return os.Chown(path, uid, gid)
}
// TouchFile creates an empty file at the specified path
func TouchFile(filename string) error {
mode := os.FileMode(0600)
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, mode)
if err != nil {
return err
}
defer file.Close()
if err := file.Chmod(mode); err != nil {
return err
}
if err := SetPermissions(filename, mode); err != nil {
return err
}
return nil
}
type WriteSliceToFileInput struct {
Filename string
Lines []string
Mode os.FileMode
}
// WriteSliceToFile writes a slice of strings to a file
func WriteSliceToFile(input WriteSliceToFileInput) error {
file, err := os.OpenFile(input.Filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, input.Mode)
if err != nil {
return err
}
defer file.Close()
w := bufio.NewWriter(file)
for _, line := range input.Lines {
fmt.Fprintln(w, line)
}
if err = w.Flush(); err != nil {
return err
}
if err := file.Chmod(input.Mode); err != nil {
return err
}
if err := SetPermissions(input.Filename, input.Mode); err != nil {
return err
}
return nil
}