1
+ package migrations
2
+
3
+ import (
4
+ "context"
5
+ "database/sql"
6
+ "github.com/operator-framework/operator-registry/pkg/registry"
7
+ )
8
+
9
+ const AssociateApisWithBundleMigrationKey = 6
10
+
11
+ // Register this migration
12
+ func init () {
13
+ registerMigration (AssociateApisWithBundleMigrationKey , bundleApiMigration )
14
+ }
15
+
16
+
17
+ // This migration moves the link between the provided and required apis table from the channel_entry to the
18
+ // bundle itself. This simplifies loading and minimizes changes that need to happen when a new bundle is
19
+ // inserted into an existing database.
20
+ // Before:
21
+ // api_provider: FOREIGN KEY(channel_entry_id) REFERENCES channel_entry(entry_id),
22
+ // api_requirer: FOREIGN KEY(channel_entry_id) REFERENCES channel_entry(entry_id),
23
+ // After:
24
+ // api_provider: FOREIGN KEY(operatorbundle_name, operatorbundle_version, operatorbundle_path) REFERENCES operatorbundle(name, version, bundlepath),
25
+ // api_requirer: FOREIGN KEY(operatorbundle_name, operatorbundle_version, operatorbundle_path) REFERENCES operatorbundle(name, version, bundlepath),
26
+
27
+ var bundleApiMigration = & Migration {
28
+ Id : AssociateApisWithBundleMigrationKey ,
29
+ Up : func (ctx context.Context , tx * sql.Tx ) error {
30
+ createNew := `
31
+ CREATE TABLE api_provider_new (
32
+ group_name TEXT,
33
+ version TEXT,
34
+ kind TEXT,
35
+ operatorbundle_name TEXT,
36
+ operatorbundle_version TEXT,
37
+ operatorbundle_path TEXT,
38
+ FOREIGN KEY (operatorbundle_name) REFERENCES operatorbundle(name) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
39
+ FOREIGN KEY(group_name, version, kind) REFERENCES api(group_name, version, kind)
40
+ );
41
+ CREATE TABLE api_requirer_new (
42
+ group_name TEXT,
43
+ version TEXT,
44
+ kind TEXT,
45
+ operatorbundle_name TEXT,
46
+ operatorbundle_version TEXT,
47
+ operatorbundle_path TEXT,
48
+ FOREIGN KEY (operatorbundle_name) REFERENCES operatorbundle(name) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
49
+ FOREIGN KEY(group_name, version, kind) REFERENCES api(group_name, version, kind)
50
+ );
51
+ `
52
+ _ , err := tx .ExecContext (ctx , createNew )
53
+ if err != nil {
54
+ return err
55
+ }
56
+
57
+ insertProvided := `INSERT INTO api_provider_new(group_name, version, kind, operatorbundle_name, operatorbundle_version, operatorbundle_path) VALUES (?, ?, ?, ?, ?, ?)`
58
+ insertRequired := `INSERT INTO api_requirer_new(group_name, version, kind, operatorbundle_name, operatorbundle_version, operatorbundle_path) VALUES (?, ?, ?, ?, ?, ?)`
59
+
60
+ bundleApis , err := getApisForBundles (ctx , tx )
61
+ if err != nil {
62
+ return err
63
+ }
64
+ for bundle , apis := range bundleApis {
65
+ for provided := range apis .provided {
66
+ _ , err := tx .ExecContext (ctx , insertProvided , provided .Group , provided .Version , provided .Kind , bundle .CsvName , bundle .Version , bundle .BundlePath )
67
+ if err != nil {
68
+ return err
69
+ }
70
+ }
71
+ for required := range apis .required {
72
+ _ , err := tx .ExecContext (ctx , insertRequired , required .Group , required .Version , required .Kind , bundle .CsvName , bundle .Version , bundle .BundlePath )
73
+ if err != nil {
74
+ return err
75
+ }
76
+ }
77
+ }
78
+
79
+ renameNewAndDropOld := `
80
+ DROP TABLE api_provider;
81
+ DROP TABLE api_requirer;
82
+ ALTER TABLE api_provider_new RENAME TO api_provider;
83
+ ALTER TABLE api_requirer_new RENAME TO api_requirer;
84
+ `
85
+ _ , err = tx .ExecContext (ctx , renameNewAndDropOld )
86
+ if err != nil {
87
+ return err
88
+ }
89
+
90
+ return err
91
+ },
92
+ Down : func (ctx context.Context , tx * sql.Tx ) error {
93
+ // TODO
94
+ return nil
95
+ },
96
+ }
97
+
98
+ type apis struct {
99
+ provided map [registry.APIKey ]struct {}
100
+ required map [registry.APIKey ]struct {}
101
+ }
102
+
103
+ func getApisForBundles (ctx context.Context , tx * sql.Tx ) (map [registry.BundleKey ]apis , error ) {
104
+ bundles := map [registry.BundleKey ]apis {}
105
+
106
+ providedQuery := `SELECT api_provider.group_name, api_provider.version, api_provider.kind, operatorbundle.name, operatorbundle.version, operatorbundle.bundlepath
107
+ FROM api_provider
108
+ INNER JOIN channel_entry ON channel_entry.entry_id = api_provider.channel_entry_id
109
+ INNER JOIN operatorbundle ON operatorbundle.name = channel_entry.operatorbundle_name`
110
+
111
+ requiredQuery := `SELECT api_requirer.group_name, api_requirer.version, api_requirer.kind, operatorbundle.name, operatorbundle.version, operatorbundle.bundlepath
112
+ FROM api_requirer
113
+ INNER JOIN channel_entry ON channel_entry.entry_id = api_requirer.channel_entry_id
114
+ INNER JOIN operatorbundle ON operatorbundle.name = channel_entry.operatorbundle_name`
115
+
116
+ providedRows , err := tx .QueryContext (ctx , providedQuery )
117
+ if err != nil {
118
+ return nil , err
119
+ }
120
+ for providedRows .Next () {
121
+ var group sql.NullString
122
+ var apiVersion sql.NullString
123
+ var kind sql.NullString
124
+ var name sql.NullString
125
+ var bundleVersion sql.NullString
126
+ var path sql.NullString
127
+ if err = providedRows .Scan (& group , & apiVersion , & kind , & name , & bundleVersion , & path ); err != nil {
128
+ return nil , err
129
+ }
130
+ if ! group .Valid || ! apiVersion .Valid || ! kind .Valid || ! name .Valid || ! bundleVersion .Valid || ! path .Valid {
131
+ continue
132
+ }
133
+ key := registry.BundleKey {
134
+ BundlePath : path .String ,
135
+ Version : bundleVersion .String ,
136
+ CsvName : name .String ,
137
+ }
138
+ bundleApis , ok := bundles [key ]
139
+ if ! ok {
140
+ bundleApis = apis {
141
+ provided : map [registry.APIKey ]struct {}{},
142
+ required : map [registry.APIKey ]struct {}{},
143
+ }
144
+ }
145
+
146
+ bundleApis .provided [registry.APIKey {
147
+ Group : group .String ,
148
+ Version : apiVersion .String ,
149
+ Kind : kind .String ,
150
+ }] = struct {}{}
151
+
152
+ bundles [key ] = bundleApis
153
+ }
154
+
155
+ requiredRows , err := tx .QueryContext (ctx , requiredQuery )
156
+ if err != nil {
157
+ return nil , err
158
+ }
159
+ for requiredRows .Next () {
160
+ var group sql.NullString
161
+ var apiVersion sql.NullString
162
+ var kind sql.NullString
163
+ var name sql.NullString
164
+ var bundleVersion sql.NullString
165
+ var path sql.NullString
166
+ if err = requiredRows .Scan (& group , & apiVersion , & kind , & name , & bundleVersion , & path ); err != nil {
167
+ return nil , err
168
+ }
169
+ if ! group .Valid || ! apiVersion .Valid || ! kind .Valid || ! name .Valid || ! bundleVersion .Valid || ! path .Valid {
170
+ continue
171
+ }
172
+ key := registry.BundleKey {
173
+ BundlePath : path .String ,
174
+ Version : bundleVersion .String ,
175
+ CsvName : name .String ,
176
+ }
177
+ bundleApis , ok := bundles [key ]
178
+ if ! ok {
179
+ bundleApis = apis {
180
+ provided : map [registry.APIKey ]struct {}{},
181
+ required : map [registry.APIKey ]struct {}{},
182
+ }
183
+ }
184
+
185
+ bundleApis .required [registry.APIKey {
186
+ Group : group .String ,
187
+ Version : apiVersion .String ,
188
+ Kind : kind .String ,
189
+ }] = struct {}{}
190
+
191
+ bundles [key ] = bundleApis
192
+ }
193
+
194
+ return bundles , nil
195
+ }
196
+
197
+
198
+ // OLD queries
199
+ //
200
+ //func GetChannelEntriesThatProvide(ctx context.Context, group, version, kind string) (entries []*registry.ChannelEntry, err error) {
201
+ // query := `SELECT DISTINCT channel_entry.package_name, channel_entry.channel_name, channel_entry.operatorbundle_name, replaces.operatorbundle_name
202
+ // FROM channel_entry
203
+ // INNER JOIN api_provider ON channel_entry.entry_id = api_provider.channel_entry_id
204
+ // LEFT OUTER JOIN channel_entry replaces ON channel_entry.replaces = replaces.entry_id
205
+ // WHERE api_provider.group_name = ? AND api_provider.version = ? AND api_provider.kind = ?`
206
+ //
207
+ // rows, err := s.db.QueryContext(ctx, query, group, version, kind)
208
+ // if err != nil {
209
+ // return
210
+ // }
211
+ // defer rows.Close()
212
+ //
213
+ // entries = []*registry.ChannelEntry{}
214
+ //
215
+ // for rows.Next() {
216
+ // var pkgNameSQL sql.NullString
217
+ // var channelNameSQL sql.NullString
218
+ // var bundleNameSQL sql.NullString
219
+ // var replacesSQL sql.NullString
220
+ // if err = rows.Scan(&pkgNameSQL, &channelNameSQL, &bundleNameSQL, &replacesSQL); err != nil {
221
+ // return
222
+ // }
223
+ //
224
+ // entries = append(entries, ®istry.ChannelEntry{
225
+ // PackageName: pkgNameSQL.String,
226
+ // ChannelName: channelNameSQL.String,
227
+ // BundleName: bundleNameSQL.String,
228
+ // Replaces: replacesSQL.String,
229
+ // })
230
+ // }
231
+ // if len(entries) == 0 {
232
+ // err = fmt.Errorf("no channel entries found that provide %s %s %s", group, version, kind)
233
+ // return
234
+ // }
235
+ // return
236
+ //}
0 commit comments