Skip to content

Commit

Permalink
Support empty and raw UID/GID users and groups, add dir creation
Browse files Browse the repository at this point in the history
  • Loading branch information
mbillow committed Mar 7, 2022
1 parent ae2bbca commit 6a9dbb4
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 33 deletions.
57 changes: 42 additions & 15 deletions template/funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -1611,10 +1611,12 @@ func md5sum(item string) (string, error) {
return fmt.Sprintf("%x", md5.Sum([]byte(item))), nil
}

// writeToFile writes the content to a file with username, group name, permissions and optional
// flags to select appending mode or add a newline.
// writeToFile writes the content to a file with permissions and optional username/UID,
// group name/GID, and flags to select appending mode or add a newline.
//
// For example:
// key "my/key/path" | writeToFile "/my/file/path.txt" "" "" "0644"
// key "my/key/path" | writeToFile "/my/file/path.txt" "100" "1000" "0644"
// key "my/key/path" | writeToFile "/my/file/path.txt" "my-user" "my-group" "0644"
// key "my/key/path" | writeToFile "/my/file/path.txt" "my-user" "my-group" "0644" "append"
// key "my/key/path" | writeToFile "/my/file/path.txt" "my-user" "my-group" "0644" "append,newline"
Expand Down Expand Up @@ -1642,6 +1644,15 @@ func writeToFile(path, username, groupName, permissions string, args ...string)
return "", err
}
} else {
dirPath := filepath.Dir(path)

if _, err := os.Stat(dirPath); err != nil {
err := os.MkdirAll(dirPath, os.ModePerm)
if err != nil {
return "", err
}
}

f, err = os.Create(path)
if err != nil {
return "", err
Expand All @@ -1659,19 +1670,35 @@ func writeToFile(path, username, groupName, permissions string, args ...string)
}

// Change ownership and permissions
u, err := user.Lookup(username)
if err != nil {
return "", err
}
g, err := user.LookupGroup(groupName)
if err != nil {
return "", err
}
uid, _ := strconv.Atoi(u.Uid)
gid, _ := strconv.Atoi(g.Gid)
err = os.Chown(path, uid, gid)
if err != nil {
return "", err
if username != "" || groupName != "" {
uid := 0
gid := 0
var convErr error
u, err := user.Lookup(username)
if err != nil {
// Check if username string is already a UID
uid, convErr = strconv.Atoi(username)
if convErr != nil {
return "", err
}
} else {
uid, _ = strconv.Atoi(u.Uid)
}

g, err := user.LookupGroup(groupName)
if err != nil {
gid, convErr = strconv.Atoi(groupName)
if convErr != nil {
return "", err
}
} else {
gid, _ = strconv.Atoi(g.Gid)
}

err = os.Chown(path, uid, gid)
if err != nil {
return "", err
}
}

err = os.Chmod(path, perm)
Expand Down
105 changes: 87 additions & 18 deletions template/template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"os/user"
"reflect"
"strconv"
"syscall"
"testing"
"time"

Expand Down Expand Up @@ -1978,54 +1979,117 @@ func TestTemplate_Execute(t *testing.T) {
}

func Test_writeToFile(t *testing.T) {
// Use current user and its primary group for input
currentUser, err := user.Current()
if err != nil {
t.Fatal(err)
}
currentUsername := currentUser.Username
currentGroup, err := user.LookupGroupId(currentUser.Gid)
if err != nil {
t.Fatal(err)
}
currentGroupName := currentGroup.Name

cases := []struct {
name string
filePath string
content string
username string
groupName string
permissions string
flags string
expectation string
wantErr bool
}{
{
"writeToFile_without_flags",
"",
"after",
currentUsername,
currentGroupName,
"0644",
"",
"after",
false,
},
{
"writeToFile_with_different_file_permissions",
"",
"after",
currentUsername,
currentGroupName,
"0666",
"",
"after",
false,
},
{
"writeToFile_with_append",
"",
"after",
currentUsername,
currentGroupName,
"0644",
`"append"`,
"beforeafter",
false,
},
{
"writeToFile_with_newline",
"",
"after",
currentUsername,
currentGroupName,
"0644",
`"newline"`,
"after\n",
false,
},
{
"writeToFile_with_append_and_newline",
"",
"after",
currentUsername,
currentGroupName,
"0644",
`"append,newline"`,
"beforeafter\n",
false,
},
{
"writeToFile_default_owner",
"",
"after",
"",
"",
"0644",
"",
"after",
false,
},
{
"writeToFile_provide_uid_gid",
"",
"after",
currentUser.Uid,
currentUser.Gid,
"0644",
"",
"after",
false,
},
{
"writeToFile_create_directory",
"demo/testing.tmp",
"after",
currentUsername,
currentGroupName,
"0644",
"",
"after",
false,
},
}

for _, tc := range cases {
Expand All @@ -2035,27 +2099,25 @@ func Test_writeToFile(t *testing.T) {
t.Fatal(err)
}
defer os.RemoveAll(outDir)
outputFile, err := ioutil.TempFile(outDir, "")
if err != nil {
t.Fatal(err)
}
outputFile.WriteString("before")

// Use current user and its primary group for input
currentUser, err := user.Current()
if err != nil {
t.Fatal(err)
var outputFilePath string
if tc.filePath == "" {
outputFile, err := ioutil.TempFile(outDir, "")
if err != nil {
t.Fatal(err)
}
_, err = outputFile.WriteString("before")
if err != nil {
t.Fatal(err)
}
outputFilePath = outputFile.Name()
} else {
outputFilePath = outDir + "/" + tc.filePath
}
currentUsername := currentUser.Username
currentGroup, err := user.LookupGroupId(currentUser.Gid)
if err != nil {
t.Fatal(err)
}
currentGroupName := currentGroup.Name

templateContent := fmt.Sprintf(
"{{ \"%s\" | writeToFile \"%s\" \"%s\" \"%s\" \"%s\" %s}}",
tc.content, outputFile.Name(), currentUsername, currentGroupName, tc.permissions, tc.flags)
tc.content, outputFilePath, tc.username, tc.groupName, tc.permissions, tc.flags)
ti := &NewTemplateInput{
Contents: templateContent,
}
Expand All @@ -2072,7 +2134,7 @@ func Test_writeToFile(t *testing.T) {

// Compare generated file content with the expectation.
// The function should generate an empty string to the output.
_generatedFileContent, err := ioutil.ReadFile(outputFile.Name())
_generatedFileContent, err := ioutil.ReadFile(outputFilePath)
generatedFileContent := string(_generatedFileContent)
if err != nil {
t.Fatal(err)
Expand All @@ -2084,7 +2146,7 @@ func Test_writeToFile(t *testing.T) {
t.Errorf("writeToFile() got = %v, want %v", generatedFileContent, tc.expectation)
}
// Assert output file permissions
sts, err := outputFile.Stat()
sts, err := os.Stat(outputFilePath)
if err != nil {
t.Fatal(err)
}
Expand All @@ -2096,6 +2158,13 @@ func Test_writeToFile(t *testing.T) {
if sts.Mode() != perm {
t.Errorf("writeToFile() wrong permissions got = %v, want %v", perm, tc.permissions)
}

stat := sts.Sys().(*syscall.Stat_t)
u := strconv.FormatUint(uint64(stat.Uid), 10)
g := strconv.FormatUint(uint64(stat.Gid), 10)
if u != currentUser.Uid || g != currentUser.Gid {
t.Errorf("writeToFile() owner = %v:%v, wanted %v:%v", u, g, currentUser.Uid, currentUser.Gid)
}
})
}
}

0 comments on commit 6a9dbb4

Please sign in to comment.