summaryrefslogtreecommitdiff
path: root/build
diff options
context:
space:
mode:
Diffstat (limited to 'build')
-rw-r--r--build/instructions.go29
-rw-r--r--build/instructions_test.go12
-rw-r--r--build/macros.go60
-rw-r--r--build/macros_test.go61
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"),
+ )
+ })
+}