Skip to content

Commit

Permalink
Support COPY --chown flag (Closes: GoogleContainerTools#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
xanonid committed Jan 10, 2020
1 parent 464ac13 commit 5c42e63
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 41 deletions.
2 changes: 1 addition & 1 deletion cmd/executor/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ func resolveDockerfilePath() error {
// copy Dockerfile to /kaniko/Dockerfile so that if it's specified in the .dockerignore
// it won't be copied into the image
func copyDockerfile() error {
if _, err := util.CopyFile(opts.DockerfilePath, constants.DockerfilePath, ""); err != nil {
if _, err := util.CopyFile(opts.DockerfilePath, constants.DockerfilePath, "", -1, -1); err != nil {
return errors.Wrap(err, "copying dockerfile")
}
opts.DockerfilePath = constants.DockerfilePath
Expand Down
20 changes: 18 additions & 2 deletions pkg/commands/copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,25 @@ func (c *CopyCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bu
if c.cmd.From != "" {
c.buildcontext = filepath.Join(constants.KanikoDir, c.cmd.From)
}
var uid, gid int64
uid = -1
gid = -1

replacementEnvs := buildArgs.ReplacementEnvs(config.Env)

if c.cmd.Chown != "" {
chown, err := util.ResolveEnvironmentReplacement(c.cmd.Chown, replacementEnvs, false)
if err != nil {
return err
}
uid32, gid32, err := util.GetUIDAndGIDFromString(chown, true)
uid = int64(uid32)
gid = int64(gid32)
if err != nil {
return err
}
}

srcs, dest, err := util.ResolveEnvAndWildcards(c.cmd.SourcesAndDest, c.buildcontext, replacementEnvs)
if err != nil {
return err
Expand Down Expand Up @@ -80,7 +96,7 @@ func (c *CopyCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bu
// we need to add '/' to the end to indicate the destination is a directory
dest = filepath.Join(cwd, dest) + "/"
}
copiedFiles, err := util.CopyDir(fullPath, dest, c.buildcontext)
copiedFiles, err := util.CopyDir(fullPath, dest, c.buildcontext, uid, gid)
if err != nil {
return err
}
Expand All @@ -97,7 +113,7 @@ func (c *CopyCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bu
c.snapshotFiles = append(c.snapshotFiles, destPath)
} else {
// ... Else, we want to copy over a file
exclude, err := util.CopyFile(fullPath, destPath, c.buildcontext)
exclude, err := util.CopyFile(fullPath, destPath, c.buildcontext, uid, gid)
if err != nil {
return err
}
Expand Down
25 changes: 1 addition & 24 deletions pkg/commands/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"os"
"os/exec"
"os/user"
"strconv"
"strings"
"syscall"

Expand Down Expand Up @@ -73,32 +72,10 @@ func (r *RunCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bui

// If specified, run the command as a specific user
if config.User != "" {
userAndGroup := strings.Split(config.User, ":")
userStr := userAndGroup[0]
var groupStr string
if len(userAndGroup) > 1 {
groupStr = userAndGroup[1]
}

uidStr, gidStr, err := util.GetUserFromUsername(userStr, groupStr)
uid, gid, err := util.GetUIDAndGIDFromString(config.User, false)
if err != nil {
return err
}

// uid and gid need to be uint32
uid64, err := strconv.ParseUint(uidStr, 10, 32)
if err != nil {
return err
}
uid := uint32(uid64)
var gid uint32
if gidStr != "" {
gid64, err := strconv.ParseUint(gidStr, 10, 32)
if err != nil {
return err
}
gid = uint32(gid64)
}
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uid, Gid: gid}
}

Expand Down
43 changes: 42 additions & 1 deletion pkg/util/command_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"os"
"os/user"
"path/filepath"
"strconv"
"strings"

"github.com/GoogleContainerTools/kaniko/pkg/constants"
Expand Down Expand Up @@ -321,7 +322,44 @@ Loop:
return nil
}

func GetUserFromUsername(userStr string, groupStr string) (string, string, error) {
// Extract user and group id from a string formatted 'user:group'.
// If gidEqualToUidIfNotGiven is set, the gid is equal to uid if the group is not specified
// otherwise gid is set to zero.
func GetUIDAndGIDFromString(userGroupString string, gidEqualToUidIfNotGiven bool) (uint32, uint32, error) {
userAndGroup := strings.Split(userGroupString, ":")
userStr := userAndGroup[0]
var groupStr string
if len(userAndGroup) > 1 {
groupStr = userAndGroup[1]
}

uidStr, gidStr, err := GetUserFromUsername(userStr, groupStr, !gidEqualToUidIfNotGiven)
if err != nil {
return 0, 0, err
}

if len(userAndGroup) == 1 && gidEqualToUidIfNotGiven {
gidStr = uidStr;
}

// uid and gid need to be fit into uint32
uid64, err := strconv.ParseUint(uidStr, 10, 32)
if err != nil {
return 0, 0, err
}
uid := uint32(uid64)
var gid uint32
if gidStr != "" {
gid64, err := strconv.ParseUint(gidStr, 10, 32)
if err != nil {
return 0, 0, err
}
gid = uint32(gid64)
}
return uid, gid, nil
}

func GetUserFromUsername(userStr string, groupStr string, usePrimaryGroupAsFallback bool) (string, string, error) {
// Lookup by username
userObj, err := user.Lookup(userStr)
if err != nil {
Expand Down Expand Up @@ -352,6 +390,9 @@ func GetUserFromUsername(userStr string, groupStr string) (string, string, error

uid := userObj.Uid
gid := ""
if usePrimaryGroupAsFallback {
gid = userObj.Gid
}
if group != nil {
gid = group.Gid
}
Expand Down
36 changes: 24 additions & 12 deletions pkg/util/fs_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"fmt"
"io"
"io/ioutil"
"math"
"net/http"
"os"
"path/filepath"
Expand Down Expand Up @@ -245,7 +246,7 @@ func ExtractFile(dest string, hdr *tar.Header, tr io.Reader) error {
currFile.Close()
case tar.TypeDir:
logrus.Tracef("creating dir %s", path)
if err := mkdirAllWithPermissions(path, mode, uid, gid); err != nil {
if err := mkdirAllWithPermissions(path, mode, int64(uid), int64(gid)); err != nil {
return err
}

Expand Down Expand Up @@ -492,7 +493,7 @@ func DownloadFileToDest(rawurl, dest string) error {

// CopyDir copies the file or directory at src to dest
// It returns a list of files it copied over
func CopyDir(src, dest, buildcontext string) ([]string, error) {
func CopyDir(src, dest, buildcontext string, uid, gid int64) ([]string, error) {
files, err := RelativeFiles("", src)
if err != nil {
return nil, err
Expand All @@ -513,9 +514,12 @@ func CopyDir(src, dest, buildcontext string) ([]string, error) {
logrus.Tracef("Creating directory %s", destPath)

mode := fi.Mode()
uid := int(fi.Sys().(*syscall.Stat_t).Uid)
gid := int(fi.Sys().(*syscall.Stat_t).Gid)

if uid < 0 {
uid = int64(int(fi.Sys().(*syscall.Stat_t).Uid))
}
if gid < 0 {
gid = int64(int(fi.Sys().(*syscall.Stat_t).Gid))
}
if err := mkdirAllWithPermissions(destPath, mode, uid, gid); err != nil {
return nil, err
}
Expand All @@ -526,7 +530,7 @@ func CopyDir(src, dest, buildcontext string) ([]string, error) {
}
} else {
// ... Else, we want to copy over a file
if _, err := CopyFile(fullPath, destPath, buildcontext); err != nil {
if _, err := CopyFile(fullPath, destPath, buildcontext, uid, gid); err != nil {
return nil, err
}
}
Expand Down Expand Up @@ -554,7 +558,7 @@ func CopySymlink(src, dest, buildcontext string) (bool, error) {
}

// CopyFile copies the file at src to dest
func CopyFile(src, dest, buildcontext string) (bool, error) {
func CopyFile(src, dest, buildcontext string, uid, gid int64) (bool, error) {
if excludeFile(src, buildcontext) {
logrus.Debugf("%s found in .dockerignore, ignoring", src)
return true, nil
Expand All @@ -575,9 +579,13 @@ func CopyFile(src, dest, buildcontext string) (bool, error) {
return false, err
}
defer srcFile.Close()
uid := fi.Sys().(*syscall.Stat_t).Uid
gid := fi.Sys().(*syscall.Stat_t).Gid
return false, CreateFile(dest, srcFile, fi.Mode(), uid, gid)
if uid < 0 {
uid = int64(fi.Sys().(*syscall.Stat_t).Uid)
}
if gid < 0 {
gid = int64(fi.Sys().(*syscall.Stat_t).Gid)
}
return false, CreateFile(dest, srcFile, fi.Mode(), uint32(uid), uint32(gid))
}

// GetExcludedFiles gets a list of files to exclude from the .dockerignore
Expand Down Expand Up @@ -643,11 +651,15 @@ func Volumes() []string {
return volumes
}

func mkdirAllWithPermissions(path string, mode os.FileMode, uid, gid int) error {
func mkdirAllWithPermissions(path string, mode os.FileMode, uid, gid int64) error {
if err := os.MkdirAll(path, mode); err != nil {
return err
}
if err := os.Chown(path, uid, gid); err != nil {
if uid > math.MaxUint32 || gid > math.MaxUint32 {
// due to https://github.com/golang/go/issues/8537
return errors.New(fmt.Sprintf("Numeric User-ID or Group-ID greater than %v are not properly supported.", math.MaxUint32))
}
if err := os.Chown(path, int(uid), int(gid)); err != nil {
return err
}
// In some cases, MkdirAll doesn't change the permissions, so run Chmod
Expand Down
2 changes: 1 addition & 1 deletion pkg/util/fs_util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -844,7 +844,7 @@ func Test_CopyFile_skips_self(t *testing.T) {
t.Fatal(err)
}

ignored, err := CopyFile(tempFile, tempFile, "")
ignored, err := CopyFile(tempFile, tempFile, "", -1, -1)
if err != nil {
t.Fatal(err)
}
Expand Down

0 comments on commit 5c42e63

Please sign in to comment.