v2.30.0-pre 7341e0e

migrate_env_test.go RawHistoryFolder

1package app
2
3import (
4	"context"
5	"fmt"
6	"os"
7	"os/exec"
8	"path/filepath"
9	"testing"
10	"time"
11
12	"github.com/bnema/zerowrap"
13	"github.com/stretchr/testify/assert"
14	"github.com/stretchr/testify/require"
15
16	"github.com/bnema/gordon/internal/adapters/out/domainsecrets"
17	"github.com/bnema/gordon/internal/domain"
18)
19
20func passCmd(args ...string) error {
21	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
22	defer cancel()
23	return exec.CommandContext(ctx, "pass", args...).Run()
24}
25
26func requirePass(t *testing.T) {
27	if err := passCmd("version"); err != nil {
28		t.Skip("pass not available")
29	}
30	if err := passCmd("ls"); err != nil {
31		t.Skip("pass store not initialized")
32	}
33}
34
35func cleanupPassDomain(t *testing.T, domainName string, keys []string) {
36	safeDomain, err := domain.SanitizeDomainForEnvFile(domainName)
37	if err != nil {
38		return
39	}
40
41	for _, key := range keys {
42		path := fmt.Sprintf("%s/%s/%s", domainsecrets.PassDomainSecretsPath, safeDomain, key)
43		_ = passCmd("rm", "-f", path)
44	}
45
46	manifestPath := fmt.Sprintf("%s/%s/.keys", domainsecrets.PassDomainSecretsPath, safeDomain)
47	_ = passCmd("rm", "-f", manifestPath)
48}
49
50func TestMigrateEnvFile(t *testing.T) {
51	requirePass(t)
52
53	tmpDir, err := os.MkdirTemp("", "migrate-env-test-*")
54	require.NoError(t, err)
55	defer os.RemoveAll(tmpDir)
56
57	logger := zerowrap.Default()
58	store, err := domainsecrets.NewPassStore(logger)
59	require.NoError(t, err)
60
61	domainName := fmt.Sprintf("test.%d.example.com", time.Now().UnixNano())
62	keys := []string{"API_KEY", "DB_PASSWORD"}
63	defer cleanupPassDomain(t, domainName, keys)
64
65	envContent := "API_KEY=secret123\nDB_PASSWORD=pass456\n"
66	envFileName := domainName + ".env"
67	envFilePath := filepath.Join(tmpDir, envFileName)
68	err = os.WriteFile(envFilePath, []byte(envContent), 0600)
69	require.NoError(t, err)
70
71	err = migrateEnvFile(tmpDir, envFileName, store, logger)
72	require.NoError(t, err)
73
74	migratedPath := envFilePath + ".migrated"
75	_, err = os.Stat(envFilePath)
76	assert.True(t, os.IsNotExist(err), "original .env file should be removed")
77
78	_, err = os.Stat(migratedPath)
79	assert.NoError(t, err, ".env.migrated file should exist")
80
81	passKeys, err := store.ListKeys(domainName)
82	require.NoError(t, err)
83	assert.ElementsMatch(t, keys, passKeys)
84
85	values, err := store.GetAll(domainName)
86	require.NoError(t, err)
87	assert.Equal(t, "secret123", values["API_KEY"])
88	assert.Equal(t, "pass456", values["DB_PASSWORD"])
89}
90
91func cleanupPassAttachment(t *testing.T, containerName string, keys []string) {
92	for _, key := range keys {
93		path := fmt.Sprintf("%s/%s/%s", domainsecrets.PassAttachmentPath, containerName, key)
94		_ = passCmd("rm", "-f", path)
95	}
96
97	manifestPath := fmt.Sprintf("%s/%s/.keys", domainsecrets.PassAttachmentPath, containerName)
98	_ = passCmd("rm", "-f", manifestPath)
99}
100
101func TestMigrateAttachmentEnvFile(t *testing.T) {
102	requirePass(t)
103
104	tmpDir, err := os.MkdirTemp("", "migrate-attachment-env-test-*")
105	require.NoError(t, err)
106	defer os.RemoveAll(tmpDir)
107
108	logger := zerowrap.Default()
109	store, err := domainsecrets.NewPassStore(logger)
110	require.NoError(t, err)
111
112	containerName := fmt.Sprintf("gitea-postgres-%d", time.Now().UnixNano())
113	envFileName := "gordon-" + containerName + ".env"
114	// extractContainerNameFromAttachmentFile strips the .env suffix, so the
115	// stored name includes the "gordon-" prefix from the filename.
116	storedContainerName := extractContainerNameFromAttachmentFile(envFileName)
117	keys := []string{"POSTGRES_USER", "POSTGRES_PASSWORD"}
118	defer cleanupPassAttachment(t, storedContainerName, keys)
119
120	envContent := "POSTGRES_USER=gitea\nPOSTGRES_PASSWORD=secret123\n"
121	envFilePath := filepath.Join(tmpDir, envFileName)
122	err = os.WriteFile(envFilePath, []byte(envContent), 0600)
123	require.NoError(t, err)
124
125	err = migrateAttachmentEnvFile(tmpDir, envFileName, store, logger)
126	require.NoError(t, err)
127
128	migratedPath := envFilePath + ".migrated"
129	_, err = os.Stat(envFilePath)
130	assert.True(t, os.IsNotExist(err), "original .env file should be removed")
131
132	_, err = os.Stat(migratedPath)
133	assert.NoError(t, err, ".env.migrated file should exist")
134
135	values, err := store.GetAllAttachment(storedContainerName)
136	require.NoError(t, err)
137	assert.Equal(t, "gitea", values["POSTGRES_USER"])
138	assert.Equal(t, "secret123", values["POSTGRES_PASSWORD"])
139}
140
141func TestIsAttachmentEnvFile(t *testing.T) {
142	tests := []struct {
143		name     string
144		filename string
145		want     bool
146	}{
147		{
148			name:     "attachment env file",
149			filename: "gordon-gitea-postgres.env",
150			want:     true,
151		},
152		{
153			name:     "attachment env file with complex name",
154			filename: "gordon-git-example-com-gitea-postgres.env",
155			want:     true,
156		},
157		{
158			name:     "plain domain env file",
159			filename: "example.com.env",
160			want:     false,
161		},
162		{
163			name:     "migrated attachment file",
164			filename: "gordon-gitea-postgres.env.migrated",
165			want:     false,
166		},
167		{
168			name:     "non-env file",
169			filename: "config.yaml",
170			want:     false,
171		},
172		{
173			name:     "gordon prefix but no env suffix",
174			filename: "gordon-something",
175			want:     false,
176		},
177	}
178
179	for _, tt := range tests {
180		t.Run(tt.name, func(t *testing.T) {
181			got := isAttachmentEnvFile(tt.filename)
182			assert.Equal(t, tt.want, got)
183		})
184	}
185}
186
187func TestExtractContainerNameFromAttachmentFile(t *testing.T) {
188	tests := []struct {
189		name     string
190		filename string
191		want     string
192	}{
193		{
194			name:     "simple attachment file",
195			filename: "gordon-gitea-postgres.env",
196			want:     "gordon-gitea-postgres",
197		},
198		{
199			name:     "complex attachment file",
200			filename: "gordon-git-example-com-gitea-redis.env",
201			want:     "gordon-git-example-com-gitea-redis",
202		},
203		{
204			name:     "single word container",
205			filename: "gordon-redis.env",
206			want:     "gordon-redis",
207		},
208	}
209
210	for _, tt := range tests {
211		t.Run(tt.name, func(t *testing.T) {
212			got := extractContainerNameFromAttachmentFile(tt.filename)
213			assert.Equal(t, tt.want, got)
214		})
215	}
216}

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