Skip to content

Commit 596a1ff

Browse files
committed
fix: db name
1 parent e96b76d commit 596a1ff

File tree

6 files changed

+85
-130
lines changed

6 files changed

+85
-130
lines changed

internal/server/branch_handlers.go

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,14 +101,22 @@ func (s *Server) createBranch(c *gin.Context) {
101101
}
102102
}
103103

104-
// Return connection details with correct restore name from the Restore record
104+
// Determine the actual database name inside the PostgreSQL cluster
105+
// - For Crunchy Bridge restores: use the configured database name
106+
// - For logical restores: extract from connection string
107+
databaseName := config.DatabaseName
108+
if config.CrunchyBridgeDatabaseName != "" {
109+
databaseName = config.CrunchyBridgeDatabaseName
110+
}
111+
112+
// Return connection details with correct database name
105113
response := CreateBranchResponse{
106114
ID: branch.ID,
107115
User: branch.User,
108116
Password: branch.Password,
109117
Host: host,
110118
Port: branch.Port,
111-
Database: restore.Name, // Use restore name from Restore record, not config
119+
Database: databaseName,
112120
}
113121

114122
c.JSON(http.StatusCreated, response)
@@ -195,6 +203,14 @@ func (s *Server) listBranches(c *gin.Context) {
195203
}
196204
}
197205

206+
// Determine the actual database name inside the PostgreSQL cluster
207+
// - For Crunchy Bridge restores: use the configured database name
208+
// - For logical restores: extract from connection string
209+
databaseName := config.DatabaseName
210+
if config.CrunchyBridgeDatabaseName != "" {
211+
databaseName = config.CrunchyBridgeDatabaseName
212+
}
213+
198214
response := make([]BranchListResponse, 0, len(branches))
199215
for _, branch := range branches {
200216
// Determine created by
@@ -203,13 +219,13 @@ func (s *Server) listBranches(c *gin.Context) {
203219
createdBy = branch.CreatedBy.Email
204220
}
205221

206-
// Build connection URL using the restore name and determined host
222+
// Build connection URL using the actual database name
207223
connectionURL := fmt.Sprintf("postgresql://%s:%s@%s:%d/%s",
208224
branch.User,
209225
branch.Password,
210226
host,
211227
branch.Port,
212-
branch.Restore.Name,
228+
databaseName,
213229
)
214230

215231
response = append(response, BranchListResponse{

internal/server/config_handlers.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,12 @@ func (s *Server) updateConfig(c *gin.Context) {
129129
// Update Crunchy Bridge configuration if provided
130130
if req.CrunchyBridgeAPIKey != "" {
131131
config.CrunchyBridgeAPIKey = req.CrunchyBridgeAPIKey
132+
133+
// Automatically disable schema-only when switching to Crunchy Bridge
134+
if config.SchemaOnly {
135+
s.logger.Info().Msg("Automatically disabling schema_only for Crunchy Bridge (not supported by pgBackRest)")
136+
config.SchemaOnly = false
137+
}
132138
}
133139
if req.CrunchyBridgeClusterName != "" {
134140
config.CrunchyBridgeClusterName = req.CrunchyBridgeClusterName
@@ -212,6 +218,13 @@ func (s *Server) updateConfig(c *gin.Context) {
212218

213219
// Update schema-only flag if provided
214220
if req.SchemaOnly != nil {
221+
// Validate: schema-only is not supported for Crunchy Bridge restores
222+
if *req.SchemaOnly && config.CrunchyBridgeAPIKey != "" {
223+
c.JSON(http.StatusBadRequest, gin.H{
224+
"error": "schema_only is not supported for Crunchy Bridge restores (pgBackRest always restores full database)",
225+
})
226+
return
227+
}
215228
config.SchemaOnly = *req.SchemaOnly
216229
}
217230

internal/server/restore_handlers.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,10 +146,17 @@ func (s *Server) triggerRestore(c *gin.Context) {
146146
Bool("has_crunchy_bridge", hasCrunchyBridge).
147147
Msg("Manually triggering restore")
148148

149+
// Determine schema-only flag
150+
// Note: Crunchy Bridge (pgBackRest) doesn't support schema-only, only logical restore (pg_dump) does
151+
schemaOnly := config.SchemaOnly
152+
if hasCrunchyBridge {
153+
schemaOnly = false
154+
}
155+
149156
// Create a new restore record with UTC datetime-based name (e.g., restore_20251017143202)
150157
restore := models.Restore{
151158
Name: models.GenerateRestoreName(),
152-
SchemaOnly: config.SchemaOnly,
159+
SchemaOnly: schemaOnly,
153160
Port: 5432,
154161
}
155162

internal/workers/crunchy_bridge_restore.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,8 @@ max_wal_size = 512MB
169169
min_wal_size = 80MB
170170
171171
# Extensions
172-
shared_preload_libraries = 'pg_stat_statements'
172+
# Note: pgaudit is included because Crunchy Bridge databases have it installed
173+
shared_preload_libraries = 'pg_stat_statements,pgaudit'
173174
174175
# Logging
175176
logging_collector = on

internal/workers/refresh_scheduler.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,17 @@ func checkAndEnqueueRefreshTasks(client *asynq.Client, db *gorm.DB, logger zerol
6262
}()).
6363
Msg("Config refresh due - creating new database and enqueueing restore task")
6464

65+
// Determine schema-only flag
66+
// Note: Crunchy Bridge (pgBackRest) doesn't support schema-only, only logical restore (pg_dump) does
67+
schemaOnly := config.SchemaOnly
68+
if config.CrunchyBridgeAPIKey != "" {
69+
schemaOnly = false
70+
}
71+
6572
// Create a new database record for the refresh
6673
database := models.Restore{
6774
Name: models.GenerateRestoreName(),
68-
SchemaOnly: config.SchemaOnly,
75+
SchemaOnly: schemaOnly,
6976
Port: 5432, // Main PostgreSQL cluster port
7077
}
7178

tests/e2e/crunchybridge_test.go

Lines changed: 34 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ func TestCrunchyBridgeIntegration(t *testing.T) {
8989
require.Equal(t, crunchyBridgeClusterName, config["crunchy_bridge_cluster_name"])
9090
require.Equal(t, crunchyBridgeDatabaseName, config["crunchy_bridge_database_name"])
9191
require.NotEmpty(t, config["crunchy_bridge_api_key"], "API key should be set (redacted)")
92-
require.Equal(t, true, config["schema_only"], "Config should default to schema_only=true")
92+
require.Equal(t, false, config["schema_only"], "Crunchy Bridge restores don't support schema_only (pgBackRest limitation)")
9393

9494
t.Log("Crunchy Bridge configured successfully")
9595
})
@@ -112,21 +112,22 @@ func TestCrunchyBridgeIntegration(t *testing.T) {
112112
})
113113

114114
// ===================================================================
115-
// Test 2: Trigger Schema-Only Restore from Crunchy Bridge
115+
// Test 2: Trigger Restore from Crunchy Bridge
116+
// Note: Crunchy Bridge uses pgBackRest which always does full restores (schema + data)
116117
// ===================================================================
117-
var schemaOnlyRestoreID string
118+
var firstRestoreID string
118119

119-
t.Run("TriggerSchemaOnlyRestore", func(t *testing.T) {
120-
t.Log("Triggering schema-only restore from Crunchy Bridge...")
120+
t.Run("TriggerRestore", func(t *testing.T) {
121+
t.Log("Triggering restore from Crunchy Bridge...")
121122

122-
// Trigger restore explicitly (config is already set with schema_only=true)
123+
// Trigger restore explicitly
123124
vm.APICall(t, "POST", "/api/restores/trigger-restore", nil)
124125

125-
t.Log("Schema-only restore triggered (via pgBackRest)")
126+
t.Log("Restore triggered (via pgBackRest)")
126127
})
127128

128-
t.Run("WaitForSchemaOnlyRestore", func(t *testing.T) {
129-
t.Log("Waiting for schema-only restore to complete...")
129+
t.Run("WaitForRestore", func(t *testing.T) {
130+
t.Log("Waiting for restore to complete...")
130131

131132
// Poll until restore is ready (pgBackRest restore may take longer than pg_dump)
132133
vm.WaitForCondition(t, 120*time.Second, func() bool {
@@ -140,128 +141,38 @@ func TestCrunchyBridgeIntegration(t *testing.T) {
140141
return ok && schemaReady
141142
})
142143

143-
t.Log("Schema-only restore completed via pgBackRest")
144+
t.Log("Restore completed via pgBackRest")
144145

145146
// Verify restore state
146147
restores := vm.APICallList(t, "GET", "/api/restores", nil)
147148
require.Len(t, restores, 1, "Should have exactly 1 restore")
148149

149150
restore := restores[0]
150-
schemaOnlyRestoreID = restore["id"].(string)
151+
firstRestoreID = restore["id"].(string)
151152
require.True(t, restore["schema_ready"].(bool), "Restore schema should be ready")
152-
require.True(t, restore["schema_only"].(bool), "First restore should be schema-only")
153+
require.False(t, restore["schema_only"].(bool), "Crunchy Bridge restores are always full (pgBackRest limitation)")
153154

154155
t.Log("Verified restore was created from Crunchy Bridge backup")
155156
})
156157

157158
// ===================================================================
158-
// Test 3: Schema-Only Branch from Crunchy Bridge Restore
159+
// Test 3: Branch from Crunchy Bridge Restore
159160
// ===================================================================
160-
t.Run("TestSchemaOnlyBranch", func(t *testing.T) {
161-
t.Log("Testing schema-only branch from Crunchy Bridge restore...")
161+
t.Run("TestBranch", func(t *testing.T) {
162+
t.Log("Testing branch from Crunchy Bridge restore...")
162163

163-
branchName := fmt.Sprintf("cb-schema-branch-%d", timestamp)
164-
branchID := vm.TestBranchOperations(t, ctx, branchName, true, true)
164+
branchName := fmt.Sprintf("cb-branch-%d", timestamp)
165+
// Note: schemaOnly=false because Crunchy Bridge restores always have data
166+
branchID := vm.TestBranchOperations(t, ctx, branchName, false, true)
165167
require.NotEmpty(t, branchID, "Branch ID should not be empty")
166168

167-
t.Log("Schema-only branch test completed")
169+
t.Log("Branch test completed")
168170
})
169171

170172
// ===================================================================
171-
// Test 4: Update Config to Trigger Full Restore
173+
// Test 4: Refresh Flow (Tests Crunchy Bridge re-restore)
172174
// ===================================================================
173-
var fullRestoreID string
174-
175-
t.Run("UpdateConfigForFullRestore", func(t *testing.T) {
176-
t.Log("Updating config to disable schema-only mode...")
177-
178-
// Update config to disable schema-only (will trigger full restore on next activation)
179-
vm.APICall(t, "PATCH", "/api/config", map[string]interface{}{
180-
"schemaOnly": false,
181-
})
182-
183-
// Verify config was updated
184-
config := vm.APICall(t, "GET", "/api/config", nil)
185-
require.False(t, config["schema_only"].(bool), "schema_only should be false")
186-
187-
t.Log("Config updated to full restore mode")
188-
})
189-
190-
t.Run("TriggerFullRestore", func(t *testing.T) {
191-
t.Log("Triggering full restore from Crunchy Bridge...")
192-
193-
// Trigger restore explicitly (config was updated to schema_only=false)
194-
vm.APICall(t, "POST", "/api/restores/trigger-restore", nil)
195-
196-
t.Log("Full restore triggered (via pgBackRest with data)")
197-
})
198-
199-
t.Run("WaitForFullRestore", func(t *testing.T) {
200-
t.Log("Waiting for full restore to complete...")
201-
202-
// Wait until we have 2 restores (schema-only + full)
203-
// Note: pgBackRest full restore may take longer than pg_dump
204-
vm.WaitForCondition(t, 180*time.Second, func() bool {
205-
restores := vm.APICallList(t, "GET", "/api/restores", nil)
206-
if len(restores) < 2 {
207-
return false
208-
}
209-
210-
// Find the full restore (schema_only=false)
211-
for _, restore := range restores {
212-
if !restore["schema_only"].(bool) {
213-
schemaReady, ok1 := restore["schema_ready"].(bool)
214-
dataReady, ok2 := restore["data_ready"].(bool)
215-
if ok1 && ok2 && schemaReady && dataReady {
216-
fullRestoreID = restore["id"].(string)
217-
return true
218-
}
219-
}
220-
}
221-
return false
222-
})
223-
224-
t.Log("Full restore completed via pgBackRest")
225-
226-
// Verify restore state
227-
restores := vm.APICallList(t, "GET", "/api/restores", nil)
228-
require.GreaterOrEqual(t, len(restores), 1, "Should have at least 1 restore")
229-
230-
// Find full restore
231-
var fullRestore map[string]interface{}
232-
for _, restore := range restores {
233-
if !restore["schema_only"].(bool) {
234-
fullRestore = restore
235-
break
236-
}
237-
}
238-
require.NotNil(t, fullRestore, "Should find full restore")
239-
require.True(t, fullRestore["schema_ready"].(bool), "Full restore schema should be ready")
240-
require.True(t, fullRestore["data_ready"].(bool), "Full restore data should be ready")
241-
242-
t.Log("Verified full restore from Crunchy Bridge backup")
243-
})
244-
245-
// ===================================================================
246-
// Test 5: Full Database Branch (With Anonymized Data)
247-
// ===================================================================
248-
var fullBranchID string
249-
250-
t.Run("TestFullDatabaseBranch", func(t *testing.T) {
251-
t.Log("Testing full database branch with anonymization...")
252-
253-
// Keep the branch (deleteBranch=false) to verify it's preserved after cleanup
254-
branchName := fmt.Sprintf("cb-full-branch-%d", timestamp)
255-
fullBranchID = vm.TestBranchOperations(t, ctx, branchName, false, false)
256-
require.NotEmpty(t, fullBranchID, "Branch ID should not be empty")
257-
258-
t.Log("Full database branch test completed with anonymization verified")
259-
})
260-
261-
// ===================================================================
262-
// Test 6: Refresh Flow (Tests Crunchy Bridge re-restore)
263-
// ===================================================================
264-
var refreshedRestoreID string
175+
var secondRestoreID string
265176

266177
t.Run("RefreshRestores", func(t *testing.T) {
267178
t.Log("Testing refresh flow with Crunchy Bridge...")
@@ -278,40 +189,40 @@ func TestCrunchyBridgeIntegration(t *testing.T) {
278189
vm.WaitForCondition(t, 180*time.Second, func() bool {
279190
restores := vm.APICallList(t, "GET", "/api/restores", nil)
280191

281-
// Look for newest restore (by created_at or just find one that's ready and different)
192+
// Look for newest restore (find one that's ready and different from first restore)
282193
for _, restore := range restores {
283-
if restore["id"].(string) != fullRestoreID && restore["id"].(string) != schemaOnlyRestoreID {
194+
if restore["id"].(string) != firstRestoreID {
284195
schemaReady, ok1 := restore["schema_ready"].(bool)
285196
dataReady, ok2 := restore["data_ready"].(bool)
286197
if ok1 && ok2 && schemaReady && dataReady {
287-
refreshedRestoreID = restore["id"].(string)
198+
secondRestoreID = restore["id"].(string)
288199
return true
289200
}
290201
}
291202
}
292203
return false
293204
})
294205

295-
require.NotEmpty(t, refreshedRestoreID, "Refreshed restore should be created")
296-
t.Logf("Refresh completed via Crunchy Bridge, new restore ID: %s", refreshedRestoreID)
206+
require.NotEmpty(t, secondRestoreID, "Refreshed restore should be created")
207+
t.Logf("Refresh completed via Crunchy Bridge, new restore ID: %s", secondRestoreID)
297208

298209
// Verify old restore WITH branch was preserved
299210
afterRestores := vm.APICallList(t, "GET", "/api/restores", nil)
300211

301-
var foundFullRestore bool
212+
var foundFirstRestore bool
302213
for _, restore := range afterRestores {
303-
if restore["id"].(string) == fullRestoreID {
304-
foundFullRestore = true
214+
if restore["id"].(string) == firstRestoreID {
215+
foundFirstRestore = true
305216
break
306217
}
307218
}
308-
require.True(t, foundFullRestore, "Old full restore should still exist (has branch: cb-full-branch)")
219+
require.True(t, foundFirstRestore, "Old restore should still exist (has branch: cb-branch)")
309220

310221
t.Log("Verified old restore with branch was preserved during refresh")
311222
})
312223

313224
// ===================================================================
314-
// Test 7: New Branches Use Refreshed Restore
225+
// Test 5: New Branches Use Refreshed Restore
315226
// ===================================================================
316227
t.Run("TestRefreshedBranch", func(t *testing.T) {
317228
t.Log("Testing that new branches use refreshed restore...")
@@ -337,7 +248,7 @@ func TestCrunchyBridgeIntegration(t *testing.T) {
337248
})
338249

339250
// ===================================================================
340-
// Test 8: Multiple Branches (Port Allocation)
251+
// Test 6: Multiple Branches (Port Allocation)
341252
// ===================================================================
342253
t.Run("CreateMultipleBranches", func(t *testing.T) {
343254
t.Log("Testing multiple branch creation and port allocation...")
@@ -383,7 +294,7 @@ func TestCrunchyBridgeIntegration(t *testing.T) {
383294
})
384295

385296
// ===================================================================
386-
// Test 9: Verify Crunchy Bridge Integration Details
297+
// Test 7: Verify Crunchy Bridge Integration Details
387298
// ===================================================================
388299
t.Run("VerifyCrunchyBridgeDetails", func(t *testing.T) {
389300
t.Log("Verifying Crunchy Bridge integration details...")

0 commit comments

Comments
 (0)