diff options
Diffstat (limited to 'build')
-rw-r--r-- | build/instructions.go | 29 | ||||
-rw-r--r-- | build/instructions_test.go | 12 | ||||
-rw-r--r-- | build/macros.go | 60 | ||||
-rw-r--r-- | build/macros_test.go | 61 |
4 files changed, 162 insertions, 0 deletions
diff --git a/build/instructions.go b/build/instructions.go index 3035b9f..91caa6b 100644 --- a/build/instructions.go +++ b/build/instructions.go @@ -78,6 +78,22 @@ func (copy Copy) Compile() []string { return append(quoteAll(copy.Sources), quote(copy.Destination)) } +// CopyAs is a concrete build instruction for copying source +// files/directories and setting their ownership to the given UID/GID. +// +type CopyAs struct { + UID uint // owner UID + GID uint // owner GID + Copy +} + +// Compile returns the variant name unquoted and all quoted CopyAs instruction +// fields. +// +func (ca CopyAs) Compile() []string { + return append([]string{fmt.Sprintf("%d:%d", ca.UID, ca.GID)}, ca.Copy.Compile()...) +} + // CopyFrom is a concrete build instruction for copying source // files/directories from one variant image to another. // @@ -121,6 +137,19 @@ func (label Label) Compile() []string { return compileSortedKeyValues(label.Definitions) } +// User is a build instruction for setting which user will run future +// commands. +// +type User struct { + Name string // user name +} + +// Compile returns the quoted user name. +// +func (user User) Compile() []string { + return []string{quote(user.Name)} +} + // Volume is a concrete build instruction for defining a volume mount point // within the container. // diff --git a/build/instructions_test.go b/build/instructions_test.go index 4098711..f6e3628 100644 --- a/build/instructions_test.go +++ b/build/instructions_test.go @@ -36,6 +36,12 @@ func TestCopy(t *testing.T) { assert.Equal(t, []string{`"source1"`, `"source2"`, `"dest"`}, i.Compile()) } +func TestCopyAs(t *testing.T) { + i := build.CopyAs{123, 124, build.Copy{[]string{"source1", "source2"}, "dest"}} + + assert.Equal(t, []string{"123:124", `"source1"`, `"source2"`, `"dest"`}, i.Compile()) +} + func TestCopyFrom(t *testing.T) { i := build.CopyFrom{"foo", build.Copy{[]string{"source1", "source2"}, "dest"}} @@ -70,6 +76,12 @@ func TestLabel(t *testing.T) { }, i.Compile()) } +func TestUser(t *testing.T) { + i := build.User{"foo"} + + assert.Equal(t, []string{`"foo"`}, i.Compile()) +} + func TestVolume(t *testing.T) { i := build.Volume{"/foo/dir"} diff --git a/build/macros.go b/build/macros.go new file mode 100644 index 0000000..5d3422e --- /dev/null +++ b/build/macros.go @@ -0,0 +1,60 @@ +package build + +import ( + "fmt" +) + +// ApplyUser wraps any build.Copy instructions as build.CopyAs using the given +// UID/GID. +// +func ApplyUser(uid uint, gid uint, instructions []Instruction) []Instruction { + applied := make([]Instruction, len(instructions)) + + for i, instruction := range instructions { + if copy, iscopy := instruction.(Copy); iscopy { + applied[i] = CopyAs{uid, gid, copy} + } else { + applied[i] = instruction + } + } + + return applied +} + +// Chown returns a build.Run instruction for setting ownership on the given +// path. +// +func Chown(uid uint, gid uint, path string) Run { + return Run{"chown %s:%s", []string{fmt.Sprint(uid), fmt.Sprint(gid), path}} +} + +// CreateDirectory returns a build.Run instruction for creating the given +// directory. +// +func CreateDirectory(path string) Run { + return Run{"mkdir -p", []string{path}} +} + +// CreateUser returns build.Run instructions for creating the given user +// account and group. +// +func CreateUser(name string, uid uint, gid uint) []Run { + return []Run{ + {"groupadd -o -g %s -r", []string{fmt.Sprint(gid), name}}, + {"useradd -o -m -d %s -r -g %s -u %s", []string{homeDir(name), name, fmt.Sprint(uid), name}}, + } +} + +// Home returns a build.Env instruction for setting the user's home directory. +// +func Home(name string) Env { + return Env{map[string]string{"HOME": homeDir(name)}} +} + +func homeDir(name string) string { + if name == "root" { + return "/root" + } + + return "/home/" + name +} diff --git a/build/macros_test.go b/build/macros_test.go new file mode 100644 index 0000000..e47cf8d --- /dev/null +++ b/build/macros_test.go @@ -0,0 +1,61 @@ +package build_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "phabricator.wikimedia.org/source/blubber/build" +) + +func TestApplyUser(t *testing.T) { + instructions := []build.Instruction{ + build.Copy{[]string{"foo"}, "bar"}, + build.Copy{[]string{"baz"}, "qux"}, + } + + assert.Equal(t, + []build.Instruction{ + build.CopyAs{123, 223, build.Copy{[]string{"foo"}, "bar"}}, + build.CopyAs{123, 223, build.Copy{[]string{"baz"}, "qux"}}, + }, + build.ApplyUser(123, 223, instructions), + ) +} + +func TestChown(t *testing.T) { + i := build.Chown(123, 124, "/foo") + + assert.Equal(t, []string{`chown "123":"124" "/foo"`}, i.Compile()) +} + +func TestCreateDirectory(t *testing.T) { + i := build.CreateDirectory("/foo") + + assert.Equal(t, []string{`mkdir -p "/foo"`}, i.Compile()) +} + +func TestCreateUser(t *testing.T) { + i := build.CreateUser("foo", 123, 124) + + if assert.Len(t, i, 2) { + assert.Equal(t, []string{`groupadd -o -g "124" -r "foo"`}, i[0].Compile()) + assert.Equal(t, []string{`useradd -o -m -d "/home/foo" -r -g "foo" -u "123" "foo"`}, i[1].Compile()) + } +} + +func TestHome(t *testing.T) { + t.Run("root", func(t *testing.T) { + assert.Equal(t, + build.Env{map[string]string{"HOME": "/root"}}, + build.Home("root"), + ) + }) + + t.Run("non-root", func(t *testing.T) { + assert.Equal(t, + build.Env{map[string]string{"HOME": "/home/foo"}}, + build.Home("foo"), + ) + }) +} |