fix(agent): give the override file the owner of the stack (#24)
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
@@ -64,6 +65,48 @@ func (r *recorder) run(_ context.Context, dir string, out io.Writer, args ...str
|
||||
return nil
|
||||
}
|
||||
|
||||
// stubChown replaces chown for the test. Tests that use it must not run in parallel.
|
||||
func stubChown(t *testing.T, f func(path string, uid, gid int) error) {
|
||||
t.Helper()
|
||||
orig := chown
|
||||
chown = f
|
||||
t.Cleanup(func() { chown = orig })
|
||||
}
|
||||
|
||||
func TestStackOwner(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
override := filepath.Join(dir, OverrideName)
|
||||
compose := filepath.Join(dir, "compose.yaml")
|
||||
|
||||
t.Run("falls back to the compose file when there is no override", func(t *testing.T) {
|
||||
require.NoError(t, os.WriteFile(compose, nil, 0o644))
|
||||
uid, gid, err := stackOwner(override, compose)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, os.Getuid(), uid)
|
||||
assert.Equal(t, os.Getgid(), gid)
|
||||
})
|
||||
t.Run("prefers the existing override", func(t *testing.T) {
|
||||
require.NoError(t, os.WriteFile(override, nil, 0o644))
|
||||
require.NoError(t, os.Remove(compose))
|
||||
uid, _, err := stackOwner(override, compose)
|
||||
require.NoError(t, err, "the compose file is not consulted")
|
||||
assert.Equal(t, os.Getuid(), uid)
|
||||
})
|
||||
t.Run("fails when neither file exists", func(t *testing.T) {
|
||||
require.NoError(t, os.Remove(override))
|
||||
_, _, err := stackOwner(override, compose)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestChown_RealToOwnIDs(t *testing.T) {
|
||||
f := filepath.Join(t.TempDir(), "f")
|
||||
require.NoError(t, os.WriteFile(f, nil, 0o644))
|
||||
uid, gid, err := stackOwner(f, f)
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, chown(f, uid, gid))
|
||||
}
|
||||
|
||||
func TestComposeExecutor(t *testing.T) {
|
||||
const d = "sha256:d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1"
|
||||
root, _ := filepath.EvalSymlinks(t.TempDir())
|
||||
@@ -108,6 +151,43 @@ func TestComposeExecutor(t *testing.T) {
|
||||
assert.Equal(t, pinned, serviceImage(doc, "web"))
|
||||
assert.Contains(t, out.String(), "resolved "+pinned)
|
||||
})
|
||||
t.Run("the override is chowned to the owner of the compose file", func(t *testing.T) {
|
||||
t.Cleanup(cleanOverride)
|
||||
composeFile := filepath.Join(stack, "compose.yaml")
|
||||
require.NoError(t, os.WriteFile(composeFile, []byte("services: {}\n"), 0o644))
|
||||
t.Cleanup(func() { os.Remove(composeFile) })
|
||||
fi, err := os.Stat(composeFile)
|
||||
require.NoError(t, err)
|
||||
st := fi.Sys().(*syscall.Stat_t)
|
||||
var gotPath string
|
||||
var gotUID, gotGID int
|
||||
stubChown(t, func(path string, uid, gid int) error {
|
||||
gotPath, gotUID, gotGID = path, uid, gid
|
||||
return nil
|
||||
})
|
||||
var out strings.Builder
|
||||
_, err = setup(fakeCompose{rw, []container.Summary{ct}}, &fakeDist{digest: d}, &recorder{}).Execute(context.Background(), job, &out)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, overridePath, gotPath)
|
||||
assert.Equal(t, int(st.Uid), gotUID)
|
||||
assert.Equal(t, int(st.Gid), gotGID)
|
||||
assert.NotContains(t, out.String(), "warning")
|
||||
})
|
||||
t.Run("a chown failure is a warning and the job still succeeds", func(t *testing.T) {
|
||||
t.Cleanup(cleanOverride)
|
||||
composeFile := filepath.Join(stack, "compose.yaml")
|
||||
require.NoError(t, os.WriteFile(composeFile, []byte("services: {}\n"), 0o644))
|
||||
t.Cleanup(func() { os.Remove(composeFile) })
|
||||
stubChown(t, func(string, int, int) error { return errors.New("operation not permitted") })
|
||||
var out strings.Builder
|
||||
got, err := setup(fakeCompose{rw, []container.Summary{ct}}, &fakeDist{digest: d}, &recorder{}).Execute(context.Background(), job, &out)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, pinned, got)
|
||||
assert.Contains(t, out.String(), "warning: could not give "+overridePath+" the owner ")
|
||||
assert.Contains(t, out.String(), "operation not permitted")
|
||||
_, doc := readOverride(t, stack)
|
||||
assert.Equal(t, pinned, serviceImage(doc, "web"))
|
||||
})
|
||||
t.Run("a pinned ref is written verbatim and not resolved", func(t *testing.T) {
|
||||
t.Cleanup(cleanOverride)
|
||||
for _, ref := range []string{"gitea.shcizo.se/shcizo/slusk:1.3@" + d, "gitea.shcizo.se/shcizo/slusk@" + d} {
|
||||
|
||||
Reference in New Issue
Block a user