v3.3.1 31f1eec

runtime_guard_internal_test.go RawHistoryFolder

1package deployment
2
3import (
4	"context"
5	"os"
6	"path/filepath"
7	"testing"
8
9	"github.com/bnema/zerowrap"
10	"github.com/stretchr/testify/assert"
11	"github.com/stretchr/testify/mock"
12	"github.com/stretchr/testify/require"
13
14	outmocks "github.com/bnema/gordon/internal/boundaries/out/mocks"
15	"github.com/bnema/gordon/internal/domain"
16)
17
18func TestCreateContainer_GuardRefuses(t *testing.T) {
19	protected := t.TempDir()
20	child := filepath.Join(protected, "registry")
21	require.NoError(t, os.MkdirAll(child, 0o700))
22	cases := map[string]*domain.ContainerConfig{
23		"host network": {Name: "c", NetworkMode: "host"},
24		"protected bind": {
25			Name:  "c",
26			Binds: []domain.ContainerBind{{Name: "conf", Source: child, Destination: "/etc/app", ReadOnly: true}},
27		},
28	}
29	for name, config := range cases {
30		t.Run(name, func(t *testing.T) {
31			runtime := outmocks.NewMockContainerRuntime(t)
32			svc := NewService(Deps{Runtime: runtime, Guard: domain.RuntimeGuard{ProtectedPaths: []string{protected}}}, zerowrap.Default())
33
34			_, err := svc.createContainer(context.Background(), "web", config)
35			require.ErrorIs(t, err, domain.ErrRuntimeGuard)
36			assert.NotContains(t, err.Error(), protected)
37			runtime.AssertNotCalled(t, "CreateContainer", mock.Anything, mock.Anything)
38		})
39	}
40}
41
42func TestCreateWithSecretFiles_GuardAllowsOwnSecretBind(t *testing.T) {
43	dataDir := t.TempDir()
44	base := filepath.Join(dataDir, "secrets-run")
45	store := outmocks.NewMockSecretFileStore(t)
46	runtime := outmocks.NewMockContainerRuntime(t)
47	guard := domain.RuntimeGuard{ProtectedPaths: []string{dataDir}, SecretFilesDir: base}
48	svc := NewService(Deps{Runtime: runtime, SecretFiles: store, Guard: guard}, zerowrap.Default())
49	config := &domain.ContainerConfig{Name: "gordon-blog--web--abc", Labels: appLabels("blog", "web", "rev-1")}
50
51	store.EXPECT().Write(mock.Anything, "blog", "gordon-blog--web--abc", mock.Anything).
52		Return(map[string]string{"B": filepath.Join(base, "blog", "gordon-blog--web--abc", "B")}, nil).Once()
53	runtime.EXPECT().CreateContainer(mock.Anything, mock.Anything).Return(&domain.Container{ID: "c-1"}, nil).Once()
54
55	_, err := svc.createWithSecretFiles(context.Background(), "blog", "web", config, map[string]string{"B": "v"})
56	require.NoError(t, err)
57}
58
59func TestResolveServiceBinds_GuardRefusesProtectedSource(t *testing.T) {
60	protected := t.TempDir()
61	svc := NewService(Deps{Guard: domain.RuntimeGuard{ProtectedPaths: []string{protected}}}, zerowrap.Default()).
62		WithBindPolicies(map[string]domain.AppBindPolicy{
63			"config": bindPolicyFor(protected, protected, true, []string{"blog"}, []string{"web"}),
64		})
65
66	_, err := svc.resolveServiceBinds("blog", domain.AppService{
67		Name:  "web",
68		Binds: []domain.AppBind{{Name: "config", Path: "/etc/app", ReadOnly: true}},
69	})
70	require.ErrorIs(t, err, domain.ErrRuntimeGuard)
71	assert.NotContains(t, err.Error(), protected)
72}
73
74// TestCreateAndStart_GuardRefusesBeforeRuntimeMutation proves the guard in
75// resolveServiceBinds stops a protected bind before secrets, volumes, or
76// container create.
77func TestCreateAndStart_GuardRefusesBeforeRuntimeMutation(t *testing.T) {
78	protected := t.TempDir()
79	runtime := outmocks.NewMockContainerRuntime(t)
80	svc := NewService(Deps{Runtime: runtime, Guard: domain.RuntimeGuard{ProtectedPaths: []string{protected}}}, zerowrap.Default()).
81		WithBindPolicies(map[string]domain.AppBindPolicy{
82			"config": bindPolicyFor(protected, protected, true, []string{"blog"}, []string{"web"}),
83		})
84	p := pinnedService{name: "web", spec: domain.AppService{
85		Name:  "web",
86		Binds: []domain.AppBind{{Name: "config", Path: "/etc/app", ReadOnly: true}},
87	}}
88
89	_, err := svc.createAndStart(context.Background(), "blog", "rev-1", p, "op-1", nil)
90	require.ErrorIs(t, err, domain.ErrRuntimeGuard)
91	runtime.AssertNotCalled(t, "CreateContainer", mock.Anything, mock.Anything)
92	runtime.AssertNotCalled(t, "CreateVolume", mock.Anything, mock.Anything)
93}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close