refactor: move common io-related functions into new file
This commit is contained in:
committed by
Jose Diaz-Gonzalez
parent
696df45306
commit
33d3722060
@@ -1,14 +1,12 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
@@ -439,16 +437,6 @@ func ContainerIsRunning(containerID string) bool {
|
||||
return strings.TrimSpace(string(b[:])) == "true"
|
||||
}
|
||||
|
||||
// DirectoryExists returns if a path exists and is a directory
|
||||
func DirectoryExists(filePath string) bool {
|
||||
fi, err := os.Stat(filePath)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return fi.IsDir()
|
||||
}
|
||||
|
||||
// DockerContainerCreate creates a new container and returns the container ID
|
||||
func DockerContainerCreate(image string, containerCreateArgs []string) (string, error) {
|
||||
cmd := []string{
|
||||
@@ -513,36 +501,6 @@ func DokkuApps() (apps []string, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// FileToSlice reads in all the lines from a file into a string slice
|
||||
func FileToSlice(filePath string) (lines []string, err error) {
|
||||
f, err := os.Open(filePath)
|
||||
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(filePath string) bool {
|
||||
fi, err := os.Stat(filePath)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return fi.Mode().IsRegular()
|
||||
}
|
||||
|
||||
// GetAppImageName returns image identifier for a given app, tag tuple. validate if tag is presented
|
||||
func GetAppImageName(appName, imageTag, imageRepo string) (imageName string) {
|
||||
err := VerifyAppName(appName)
|
||||
@@ -565,11 +523,6 @@ func GetAppImageName(appName, imageTag, imageRepo string) (imageName string) {
|
||||
return
|
||||
}
|
||||
|
||||
// IsAbsPath returns 0 if input path is absolute
|
||||
func IsAbsPath(path string) bool {
|
||||
return strings.HasPrefix(path, "/")
|
||||
}
|
||||
|
||||
// IsDeployed returns true if given app has a running container
|
||||
func IsDeployed(appName string) bool {
|
||||
files, err := ioutil.ReadDir(AppRoot(appName))
|
||||
@@ -612,29 +565,6 @@ func GetenvWithDefault(key string, defaultValue string) (val string) {
|
||||
return
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// ReportSingleApp is an internal function that displays a report for an app
|
||||
func ReportSingleApp(reportType string, appName string, infoFlag string, infoFlags map[string]string, trimPrefix bool, uppercaseFirstCharacter bool) error {
|
||||
flags := []string{}
|
||||
@@ -805,36 +735,6 @@ func PlugnTriggerSetup(triggerName string, args ...string) *sh.Session {
|
||||
return sh.Command("plugn", shellArgs...)
|
||||
}
|
||||
|
||||
// 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")
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func times(str string, n int) (out string) {
|
||||
for i := 0; i < n; i++ {
|
||||
out += str
|
||||
|
||||
@@ -10,10 +10,10 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
testAppName = "test-app-1"
|
||||
testAppDir = strings.Join([]string{"/home/dokku/", testAppName}, "")
|
||||
testEnvFile = strings.Join([]string{testAppDir, "/ENV"}, "")
|
||||
testEnvLine = "export testKey=TESTING"
|
||||
testAppName = "test-app-1"
|
||||
testAppDir = strings.Join([]string{"/home/dokku/", testAppName}, "")
|
||||
testEnvFile = strings.Join([]string{testAppDir, "/ENV"}, "")
|
||||
testEnvLine = "export testKey=TESTING"
|
||||
testAppName2 = "01-test-app-1"
|
||||
testAppDir2 = strings.Join([]string{"/home/dokku/", testAppName2}, "")
|
||||
testEnvFile2 = strings.Join([]string{testAppDir2, "/ENV"}, "")
|
||||
@@ -95,32 +95,6 @@ func TestCommonDokkuApps(t *testing.T) {
|
||||
teardownTestApp()
|
||||
}
|
||||
|
||||
func TestCommonFileToSlice(t *testing.T) {
|
||||
RegisterTestingT(t)
|
||||
Expect(setupTestApp()).To(Succeed())
|
||||
lines, err := FileToSlice(testEnvFile)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(lines).To(Equal([]string{testEnvLine}))
|
||||
teardownTestApp()
|
||||
}
|
||||
|
||||
func TestCommonFileExists(t *testing.T) {
|
||||
RegisterTestingT(t)
|
||||
Expect(setupTestApp()).To(Succeed())
|
||||
Expect(FileExists(testEnvFile)).To(BeTrue())
|
||||
teardownTestApp()
|
||||
}
|
||||
|
||||
func TestCommonReadFirstLine(t *testing.T) {
|
||||
RegisterTestingT(t)
|
||||
line := ReadFirstLine(testEnvFile)
|
||||
Expect(line).To(Equal(""))
|
||||
Expect(setupTestApp()).To(Succeed())
|
||||
line = ReadFirstLine(testEnvFile)
|
||||
Expect(line).To(Equal(testEnvLine))
|
||||
teardownTestApp()
|
||||
}
|
||||
|
||||
func TestCommonStripInlineComments(t *testing.T) {
|
||||
RegisterTestingT(t)
|
||||
text := StripInlineComments(strings.Join([]string{testEnvLine, "# testing comment"}, " "))
|
||||
|
||||
191
plugins/common/io.go
Normal file
191
plugins/common/io.go
Normal file
@@ -0,0 +1,191 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/user"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CopyFile copies a file from src to dst. If src and dst files exist, and are
|
||||
// the same, then return success. Otherise, attempt to create a hard link
|
||||
// between the two files. If that fail, copy the file contents from src to dst.
|
||||
// FROM: https://stackoverflow.com/a/21067803/1515875
|
||||
func CopyFile(src, dst string) (err error) {
|
||||
sfi, err := os.Stat(src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !sfi.Mode().IsRegular() {
|
||||
// cannot copy non-regular files (e.g., directories,
|
||||
// symlinks, devices, etc.)
|
||||
return fmt.Errorf("CopyFile: non-regular source file %s (%q)", sfi.Name(), sfi.Mode().String())
|
||||
}
|
||||
dfi, err := os.Stat(dst)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if !(dfi.Mode().IsRegular()) {
|
||||
return fmt.Errorf("CopyFile: non-regular destination file %s (%q)", dfi.Name(), dfi.Mode().String())
|
||||
}
|
||||
if os.SameFile(sfi, dfi) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if err = os.Link(src, dst); err == nil {
|
||||
return
|
||||
}
|
||||
err = copyFileContents(src, dst)
|
||||
return
|
||||
}
|
||||
|
||||
// copyFileContents copies the contents of the file named src to the file named
|
||||
// by dst. The file will be created if it does not already exist. If the
|
||||
// destination file exists, all it's contents will be replaced by the contents
|
||||
// of the source file.
|
||||
// FROM: https://stackoverflow.com/a/21067803/1515875
|
||||
func copyFileContents(src, dst string) (err error) {
|
||||
in, err := os.Open(src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer in.Close()
|
||||
out, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
cerr := out.Close()
|
||||
if err == nil {
|
||||
err = cerr
|
||||
}
|
||||
}()
|
||||
if _, err = io.Copy(out, in); err != nil {
|
||||
return
|
||||
}
|
||||
err = out.Sync()
|
||||
return
|
||||
}
|
||||
|
||||
// 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, "/")
|
||||
}
|
||||
|
||||
// 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")
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func WriteSliceToFile(filename string, lines []string) error {
|
||||
file, err := os.OpenFile(filename, os.O_RDWR|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w := bufio.NewWriter(file)
|
||||
for _, line := range lines {
|
||||
fmt.Fprintln(w, line)
|
||||
}
|
||||
if err = w.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
file.Chmod(0600)
|
||||
SetPermissions(filename, 0600)
|
||||
|
||||
return nil
|
||||
}
|
||||
28
plugins/common/io_test.go
Normal file
28
plugins/common/io_test.go
Normal file
@@ -0,0 +1,28 @@
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestCommonFileToSlice(t *testing.T) {
|
||||
RegisterTestingT(t)
|
||||
Expect(setupTestApp()).To(Succeed())
|
||||
lines, err := FileToSlice(testEnvFile)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(lines).To(Equal([]string{testEnvLine}))
|
||||
teardownTestApp()
|
||||
}
|
||||
|
||||
func TestCommonFileExists(t *testing.T) {
|
||||
RegisterTestingT(t)
|
||||
Expect(setupTestApp()).To(Succeed())
|
||||
Expect(FileExists(testEnvFile)).To(BeTrue())
|
||||
teardownTestApp()
|
||||
}
|
||||
|
||||
func TestCommonReadFirstLine(t *testing.T) {
|
||||
RegisterTestingT(t)
|
||||
line := ReadFirstLine(testEnvFile)
|
||||
Expect(line).To(Equal(""))
|
||||
Expect(setupTestApp()).To(Succeed())
|
||||
line = ReadFirstLine(testEnvFile)
|
||||
Expect(line).To(Equal(testEnvLine))
|
||||
teardownTestApp()
|
||||
}
|
||||
@@ -82,21 +82,5 @@ func touchPhaseFile(appName string, phase string) error {
|
||||
|
||||
func writeDockerOptionsForPhase(appName string, phase string, options []string) error {
|
||||
phaseFilePath := getPhaseFilePath(appName, phase)
|
||||
file, err := os.OpenFile(phaseFilePath, os.O_RDWR|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w := bufio.NewWriter(file)
|
||||
for _, option := range options {
|
||||
fmt.Fprintln(w, option)
|
||||
}
|
||||
if err = w.Flush(); err != nil {
|
||||
return fmt.Errorf("Unable to update docker options phase file %s.%s: %s", appName, phase, err.Error())
|
||||
}
|
||||
|
||||
file.Chmod(0600)
|
||||
common.SetPermissions(phaseFilePath, 0600)
|
||||
|
||||
return nil
|
||||
return common.WriteSliceToFile(phaseFilePath, options)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user