v2.10.2 705579a

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	keys := []string{"POSTGRES_USER", "POSTGRES_PASSWORD"}
114	defer cleanupPassAttachment(t, containerName, keys)
115
116	envContent := "POSTGRES_USER=gitea\nPOSTGRES_PASSWORD=secret123\n"
117	envFileName := "gordon-" + containerName + ".env"
118	envFilePath := filepath.Join(tmpDir, envFileName)
119	err = os.WriteFile(envFilePath, []byte(envContent), 0600)
120	require.NoError(t, err)
121
122	err = migrateAttachmentEnvFile(tmpDir, envFileName, store, logger)
123	require.NoError(t, err)
124
125	migratedPath := envFilePath + ".migrated"
126	_, err = os.Stat(envFilePath)
127	assert.True(t, os.IsNotExist(err), "original .env file should be removed")
128
129	_, err = os.Stat(migratedPath)
130	assert.NoError(t, err, ".env.migrated file should exist")
131
132	values, err := store.GetAllAttachment(containerName)
133	require.NoError(t, err)
134	assert.Equal(t, "gitea", values["POSTGRES_USER"])
135	assert.Equal(t, "secret123", values["POSTGRES_PASSWORD"])
136}
137
138func TestIsAttachmentEnvFile(t *testing.T) {
139	tests := []struct {
140		name     string
141		filename string
142		want     bool
143	}{
144		{
145			name:     "attachment env file",
146			filename: "gordon-gitea-postgres.env",
147			want:     true,
148		},
149		{
150			name:     "attachment env file with complex name",
151			filename: "gordon-git-example-com-gitea-postgres.env",
152			want:     true,
153		},
154		{
155			name:     "plain domain env file",
156			filename: "example.com.env",
157			want:     false,
158		},
159		{
160			name:     "migrated attachment file",
161			filename: "gordon-gitea-postgres.env.migrated",
162			want:     false,
163		},
164		{
165			name:     "non-env file",
166			filename: "config.yaml",
167			want:     false,
168		},
169		{
170			name:     "gordon prefix but no env suffix",
171			filename: "gordon-something",
172			want:     false,
173		},
174	}
175
176	for _, tt := range tests {
177		t.Run(tt.name, func(t *testing.T) {
178			got := isAttachmentEnvFile(tt.filename)
179			assert.Equal(t, tt.want, got)
180		})
181	}
182}
183
184func TestExtractContainerNameFromAttachmentFile(t *testing.T) {
185	tests := []struct {
186		name     string
187		filename string
188		want     string
189	}{
190		{
191			name:     "simple attachment file",
192			filename: "gordon-gitea-postgres.env",
193			want:     "gordon-gitea-postgres",
194		},
195		{
196			name:     "complex attachment file",
197			filename: "gordon-git-example-com-gitea-redis.env",
198			want:     "gordon-git-example-com-gitea-redis",
199		},
200		{
201			name:     "single word container",
202			filename: "gordon-redis.env",
203			want:     "gordon-redis",
204		},
205	}
206
207	for _, tt := range tests {
208		t.Run(tt.name, func(t *testing.T) {
209			got := extractContainerNameFromAttachmentFile(tt.filename)
210			assert.Equal(t, tt.want, got)
211		})
212	}
213}

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