Skip to content

Commit 869b650

Browse files
author
Bogdan Tsechoev
committed
Merge branch '605-snapshot-id' into 'dle-4-0'
fix: take precedence snapshot ID over the branch name when clone creates (#605) See merge request postgres-ai/database-lab!1011
2 parents 97659d8 + ea61a00 commit 869b650

File tree

5 files changed

+90
-8
lines changed

5 files changed

+90
-8
lines changed

engine/internal/srv/routes.go

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,30 @@ func (s *Server) createClone(w http.ResponseWriter, r *http.Request) {
485485
return
486486
}
487487

488-
if cloneRequest.Branch != "" {
488+
if cloneRequest.Snapshot != nil && cloneRequest.Snapshot.ID != "" {
489+
fsm, err := s.getFSManagerForSnapshot(cloneRequest.Snapshot.ID)
490+
if err != nil {
491+
api.SendBadRequestError(w, r, err.Error())
492+
return
493+
}
494+
495+
if fsm == nil {
496+
api.SendBadRequestError(w, r, "no pool manager found")
497+
return
498+
}
499+
500+
branch := branching.ParseBranchNameFromSnapshot(cloneRequest.Snapshot.ID, fsm.Pool().Name)
501+
if branch == "" {
502+
branch = branching.DefaultBranch
503+
}
504+
505+
// Snapshot ID takes precedence over the branch name.
506+
cloneRequest.Branch = branch
507+
} else {
508+
if cloneRequest.Branch == "" {
509+
cloneRequest.Branch = branching.DefaultBranch
510+
}
511+
489512
fsm, err := s.getFSManagerForBranch(cloneRequest.Branch)
490513
if err != nil {
491514
api.SendBadRequestError(w, r, err.Error())
@@ -510,8 +533,6 @@ func (s *Server) createClone(w http.ResponseWriter, r *http.Request) {
510533
}
511534

512535
cloneRequest.Snapshot = &types.SnapshotCloneFieldRequest{ID: snapshotID}
513-
} else {
514-
cloneRequest.Branch = branching.DefaultBranch
515536
}
516537

517538
if cloneRequest.ID != "" {

engine/internal/validator/validator.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
package validator
77

88
import (
9+
"errors"
910
"fmt"
1011
"strings"
1112

12-
"github.com/pkg/errors"
1313
passwordvalidator "github.com/wagslane/go-password-validator"
1414

1515
"gitlab.com/postgres-ai/database-lab/v3/pkg/client/dblabapi/types"
@@ -36,7 +36,7 @@ func (v Service) ValidateCloneRequest(cloneRequest *types.CloneCreateRequest) er
3636
}
3737

3838
if cloneRequest.ID != "" && strings.Contains(cloneRequest.ID, "/") {
39-
return errors.New("Clone ID cannot contain slash ('/'). Please choose another ID")
39+
return errors.New("clone ID cannot contain slash ('/'). Please choose another ID")
4040
}
4141

4242
if err := passwordvalidator.Validate(cloneRequest.DB.Password, minEntropyBits); err != nil {

engine/internal/validator/validator_test.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ func TestValidationCloneRequest(t *testing.T) {
1919
DB: &types.DatabaseRequest{
2020
Username: "username",
2121
Password: "secret_password",
22-
}})
22+
},
23+
})
2324

2425
assert.Nil(t, err)
2526
}
@@ -31,7 +32,8 @@ func TestWeakPassword(t *testing.T) {
3132
DB: &types.DatabaseRequest{
3233
Username: "username",
3334
Password: "password",
34-
}})
35+
},
36+
})
3537

3638
assert.ErrorContains(t, err, "insecure password")
3739
}
@@ -60,7 +62,7 @@ func TestValidationCloneRequestErrors(t *testing.T) {
6062
DB: &types.DatabaseRequest{Username: "user", Password: "password"},
6163
ID: "test/ID",
6264
},
63-
error: "Clone ID cannot contain slash ('/'). Please choose another ID",
65+
error: "clone ID cannot contain slash ('/'). Please choose another ID",
6466
},
6567
}
6668

engine/pkg/util/branching/branching.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,27 @@ func parseCloneDataset(cloneDataset, poolName string) []string {
8484

8585
return splits
8686
}
87+
88+
// ParseBranchNameFromSnapshot parses branch name from the snapshot ID.
89+
func ParseBranchNameFromSnapshot(snapshot, poolName string) string {
90+
dataset, _, found := strings.Cut(snapshot, "@")
91+
if !found {
92+
return ""
93+
}
94+
95+
branchPrefix := poolName + "/" + BranchDir + "/"
96+
if !strings.HasPrefix(dataset, branchPrefix) {
97+
return ""
98+
}
99+
100+
trimmedDataset := strings.TrimPrefix(dataset, branchPrefix)
101+
102+
splits := strings.SplitN(trimmedDataset, "/", 2)
103+
if len(splits) < 1 {
104+
return ""
105+
}
106+
107+
branch := splits[0]
108+
109+
return branch
110+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package branching
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestParsingBranchNameFromSnapshot(t *testing.T) {
10+
const poolName = "pool/pg17"
11+
12+
testCases := []struct {
13+
input string
14+
expected string
15+
}{
16+
{
17+
input: "pool/pg17@snapshot_20250407101616",
18+
expected: "",
19+
},
20+
{
21+
input: "pool/pg17/branch/dev@20250407101828",
22+
expected: "dev",
23+
},
24+
{
25+
input: "pool/pg17/branch/main/cvpqe8gn9i6s73b49e3g/r0@20250407102140",
26+
expected: "main",
27+
},
28+
}
29+
30+
for _, tc := range testCases {
31+
branchName := ParseBranchNameFromSnapshot(tc.input, poolName)
32+
33+
assert.Equal(t, tc.expected, branchName)
34+
}
35+
}

0 commit comments

Comments
 (0)