@@ -12,12 +12,16 @@ import (
12
12
13
13
"github.com/docker/docker/api/types"
14
14
"github.com/docker/docker/api/types/container"
15
+ "github.com/docker/docker/api/types/mount"
15
16
"github.com/docker/docker/api/types/network"
16
17
"github.com/docker/docker/client"
17
18
"github.com/docker/docker/pkg/stdcopy"
18
19
20
+ "gitlab.com/postgres-ai/database-lab/v3/internal/cloning"
21
+ "gitlab.com/postgres-ai/database-lab/v3/internal/provision/pool"
19
22
"gitlab.com/postgres-ai/database-lab/v3/internal/retrieval/engine/postgres/tools"
20
23
"gitlab.com/postgres-ai/database-lab/v3/internal/retrieval/engine/postgres/tools/cont"
24
+ dle_types "gitlab.com/postgres-ai/database-lab/v3/pkg/client/dblabapi/types"
21
25
"gitlab.com/postgres-ai/database-lab/v3/pkg/log"
22
26
"gitlab.com/postgres-ai/database-lab/v3/pkg/models"
23
27
"gitlab.com/postgres-ai/database-lab/v3/pkg/util"
@@ -29,41 +33,84 @@ const schemaDiffImage = "supabase/pgadmin-schema-diff"
29
33
// Diff defines a schema generator.
30
34
type Diff struct {
31
35
cli * client.Client
36
+ cl * cloning.Base
37
+ pm * pool.Manager
32
38
}
33
39
34
40
// NewDiff creates a new Diff service.
35
- func NewDiff (cli * client.Client ) * Diff {
36
- return & Diff {cli : cli }
37
- }
38
-
39
- func connStr (clone * models.Clone ) string {
40
- // TODO: fix params
41
- return fmt .Sprintf ("postgres://%s:%s@%s:%s/%s" ,
42
- clone .DB .Username ,
43
- "test" , // clone.DB.Password,
44
- util .GetCloneNameStr (clone .DB .Port ),
45
- clone .DB .Port ,
46
- "test" , // clone.DB.DBName,
47
- )
41
+ func NewDiff (cli * client.Client , cl * cloning.Base , pm * pool.Manager ) * Diff {
42
+ return & Diff {cli : cli , cl : cl , pm : pm }
48
43
}
49
44
50
45
// GenerateDiff generate difference between database schemas.
51
- func (d * Diff ) GenerateDiff (actual , origin * models.Clone , instanceID string ) (string , error ) {
52
- log .Dbg ("Origin clone:" , origin )
53
- log .Dbg ("Actual clone:" , actual .DB .ConnStr + " password=test" )
46
+ func (d * Diff ) GenerateDiff (ctx context.Context , actual * models.Clone , instanceID string ) (string , error ) {
47
+ origin , err := d .createOriginClone (ctx , actual )
48
+ if err != nil {
49
+ return "" , fmt .Errorf ("cannot create a clone based on snapshot %s: %w" , actual .Snapshot .ID , err )
50
+ }
54
51
55
- ctx := context .Background ()
52
+ defer func () {
53
+ if err := d .cl .DestroyClone (origin .ID ); err != nil {
54
+ log .Err ("Failed to destroy origin clone:" , err )
55
+ }
56
+ }()
57
+
58
+ diffContID , err := d .startDiffContainer (ctx , actual , origin , instanceID )
59
+ if err != nil {
60
+ return "" , fmt .Errorf ("failed to start diff container: %w" , err )
61
+ }
56
62
57
- if origin .Status .Code != models .StatusOK {
58
- if _ , err := d .watchCloneStatus (ctx , origin , origin .Status .Code ); err != nil {
59
- return "" , fmt .Errorf ("failed to watch the clone status: %w" , err )
63
+ defer func () {
64
+ if err := d .cli .ContainerRemove (ctx , diffContID , types.ContainerRemoveOptions {
65
+ RemoveVolumes : true ,
66
+ Force : true ,
67
+ }); err != nil {
68
+ log .Err ("failed to remove the diff container:" , diffContID , err )
60
69
}
70
+ }()
71
+
72
+ statusCh , errCh := d .cli .ContainerWait (ctx , diffContID , container .WaitConditionNotRunning )
73
+ select {
74
+ case err := <- errCh :
75
+ if err != nil {
76
+ return "" , fmt .Errorf ("error on container waiting: %w" , err )
77
+ }
78
+ case <- statusCh :
79
+ }
80
+
81
+ out , err := d .cli .ContainerLogs (ctx , diffContID , types.ContainerLogsOptions {ShowStdout : true })
82
+ if err != nil {
83
+ return "" , fmt .Errorf ("failed to get container logs: %w" , err )
61
84
}
62
85
86
+ buf := bytes .NewBuffer ([]byte {})
87
+
88
+ if _ , err = stdcopy .StdCopy (buf , os .Stderr , out ); err != nil {
89
+ return "" , fmt .Errorf ("failed to copy container output: %w" , err )
90
+ }
91
+
92
+ filteredOutput , err := filterOutput (buf )
93
+ if err != nil {
94
+ return "" , fmt .Errorf ("failed to filter output: %w" , err )
95
+ }
96
+
97
+ return filteredOutput .String (), nil
98
+ }
99
+
100
+ // startDiffContainer starts a new diff container.
101
+ func (d * Diff ) startDiffContainer (ctx context.Context , actual , origin * models.Clone , instanceID string ) (string , error ) {
63
102
if err := tools .PullImage (ctx , d .cli , schemaDiffImage ); err != nil {
64
103
return "" , fmt .Errorf ("failed to pull image: %w" , err )
65
104
}
66
105
106
+ fsm , err := d .pm .GetFSManager (actual .Snapshot .Pool )
107
+ if err != nil {
108
+ return "" , fmt .Errorf ("failed to get pool filesystem manager %s: %w" , actual .Snapshot .ID , err )
109
+ }
110
+
111
+ originSocketDir := fsm .Pool ().SocketCloneDir (util .GetCloneNameStr (origin .DB .Port ))
112
+ actualSocketDir := fsm .Pool ().SocketCloneDir (util .GetCloneNameStr (actual .DB .Port ))
113
+
67
114
diffCont , err := d .cli .ContainerCreate (ctx ,
68
115
& container.Config {
69
116
Labels : map [string ]string {
@@ -72,14 +119,16 @@ func (d *Diff) GenerateDiff(actual, origin *models.Clone, instanceID string) (st
72
119
},
73
120
Image : schemaDiffImage ,
74
121
Cmd : []string {
75
- connStr (actual ),
76
- connStr (origin ),
122
+ connString (actual , actualSocketDir ),
123
+ connString (origin , originSocketDir ),
77
124
},
78
125
},
79
- & container.HostConfig {},
126
+ & container.HostConfig {
127
+ Mounts : d .getDiffMounts (actualSocketDir , originSocketDir ),
128
+ },
80
129
& network.NetworkingConfig {},
81
130
nil ,
82
- "clone-diff-" + actual . ID ,
131
+ d . cloneDiffName ( actual ) ,
83
132
)
84
133
if err != nil {
85
134
return "" , fmt .Errorf ("failed to create diff container: %w" , err )
@@ -89,47 +138,57 @@ func (d *Diff) GenerateDiff(actual, origin *models.Clone, instanceID string) (st
89
138
return "" , fmt .Errorf ("failed to connect UI container to the internal Docker network: %w" , err )
90
139
}
91
140
92
- err = d .cli .ContainerStart (ctx , diffCont .ID , types.ContainerStartOptions {})
93
- if err != nil {
141
+ if err = d .cli .ContainerStart (ctx , diffCont .ID , types.ContainerStartOptions {}); err != nil {
94
142
return "" , fmt .Errorf ("failed to create diff container: %w" , err )
95
143
}
96
144
97
- defer func () {
98
- if err := d .cli .ContainerRemove (ctx , diffCont .ID , types.ContainerRemoveOptions {
99
- RemoveVolumes : true ,
100
- Force : true ,
101
- }); err != nil {
102
- log .Err ("failed to remove the diff container:" , diffCont .ID , err )
103
- }
104
- }()
145
+ return diffCont .ID , nil
146
+ }
105
147
106
- statusCh , errCh := d .cli .ContainerWait (ctx , diffCont .ID , container .WaitConditionNotRunning )
107
- select {
108
- case err := <- errCh :
109
- if err != nil {
110
- return "" , fmt .Errorf ("error on container waiting: %w" , err )
111
- }
112
- case <- statusCh :
148
+ func (d * Diff ) createOriginClone (ctx context.Context , clone * models.Clone ) (* models.Clone , error ) {
149
+ originClone , err := d .cl .CreateClone (& dle_types.CloneCreateRequest {
150
+ ID : d .cloneDiffName (clone ),
151
+ DB : & dle_types.DatabaseRequest {
152
+ Username : clone .DB .Username ,
153
+ DBName : clone .DB .DBName ,
154
+ },
155
+ Snapshot : & dle_types.SnapshotCloneFieldRequest {ID : clone .Snapshot .ID },
156
+ })
157
+ if err != nil {
158
+ return nil , fmt .Errorf ("cannot create a clone based on snapshot %s: %w" , clone .Snapshot .ID , err )
113
159
}
114
160
115
- out , err := d .cli .ContainerLogs (ctx , diffCont .ID , types.ContainerLogsOptions {ShowStdout : true })
116
- if err != nil {
117
- return "" , fmt .Errorf ("failed to get container logs: %w" , err )
161
+ if originClone .Status .Code != models .StatusOK {
162
+ if _ , err := d .watchCloneStatus (ctx , originClone , originClone .Status .Code ); err != nil {
163
+ return nil , fmt .Errorf ("failed to watch the clone status: %w" , err )
164
+ }
118
165
}
119
166
120
- buf := bytes .NewBuffer ([]byte {})
167
+ return originClone , nil
168
+ }
121
169
122
- _ , err = stdcopy .StdCopy (buf , os .Stderr , out )
123
- if err != nil {
124
- return "" , fmt .Errorf ("failed to copy container output: %w" , err )
170
+ func (d * Diff ) getDiffMounts (actualHost , originHost string ) []mount.Mount {
171
+ return []mount.Mount {
172
+ {
173
+ Type : mount .TypeBind ,
174
+ Source : actualHost ,
175
+ Target : actualHost ,
176
+ },
177
+ {
178
+ Type : mount .TypeBind ,
179
+ Source : originHost ,
180
+ Target : originHost ,
181
+ },
125
182
}
183
+ }
126
184
127
- filteredOutput , err := filterOutput (buf )
128
- if err != nil {
129
- return "" , fmt .Errorf ("failed to filter output: %w" , err )
130
- }
185
+ func (d * Diff ) cloneDiffName (actual * models.Clone ) string {
186
+ return "clone-diff-" + actual .ID
187
+ }
131
188
132
- return filteredOutput .String (), nil
189
+ func connString (clone * models.Clone , socketDir string ) string {
190
+ return fmt .Sprintf ("host=%s port=%s user=%s dbname=%s" ,
191
+ socketDir , clone .DB .Port , clone .DB .Username , clone .DB .DBName )
133
192
}
134
193
135
194
// watchCloneStatus checks the clone status for changing.
@@ -176,6 +235,7 @@ func filterOutput(b *bytes.Buffer) (*strings.Builder, error) {
176
235
return nil , err
177
236
}
178
237
238
+ // Filter empty lines, comments and warnings.
179
239
if len (line ) == 0 || bytes .HasPrefix (line , []byte ("--" )) || bytes .HasPrefix (line , []byte ("NOTE:" )) {
180
240
continue
181
241
}
0 commit comments