Skip to content

Commit c93373c

Browse files
Merge pull request #656 from ankitathomas/deprecate-fix
check presence of all bundles before deprecate
2 parents a2c4b3c + b0d4c54 commit c93373c

File tree

2 files changed

+292
-1
lines changed

2 files changed

+292
-1
lines changed

pkg/lib/registry/registry.go

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,21 @@ func (r RegistryUpdater) DeprecateFromRegistry(request DeprecateFromRegistryRequ
342342
return fmt.Errorf("unable to migrate database: %s", err)
343343
}
344344

345-
deprecator := sqlite.NewSQLDeprecatorForBundles(dbLoader, request.Bundles)
345+
// Check if all bundlepaths are valid
346+
var toDeprecate []string
347+
348+
dbQuerier := sqlite.NewSQLLiteQuerierFromDb(db)
349+
350+
toDeprecate, _, err = checkForBundlePaths(dbQuerier, request.Bundles)
351+
if err != nil {
352+
if !request.Permissive {
353+
r.Logger.WithError(err).Error("permissive mode disabled")
354+
return err
355+
}
356+
r.Logger.WithError(err).Warn("permissive mode enabled")
357+
}
358+
359+
deprecator := sqlite.NewSQLDeprecatorForBundles(dbLoader, toDeprecate)
346360
if err := deprecator.Deprecate(); err != nil {
347361
r.Logger.Debugf("unable to deprecate bundles from database: %s", err)
348362
if !request.Permissive {
@@ -354,3 +368,37 @@ func (r RegistryUpdater) DeprecateFromRegistry(request DeprecateFromRegistryRequ
354368

355369
return nil
356370
}
371+
372+
// checkForBundlePaths verifies presence of a list of bundle paths in the registry.
373+
func checkForBundlePaths(querier registry.GRPCQuery, bundlePaths []string) ([]string, []string, error) {
374+
if len(bundlePaths) == 0 {
375+
return bundlePaths, nil, nil
376+
}
377+
378+
registryBundles, err := querier.ListBundles(context.TODO())
379+
if err != nil {
380+
return bundlePaths, nil, err
381+
}
382+
383+
if len(registryBundles) == 0 {
384+
return nil, bundlePaths, nil
385+
}
386+
387+
registryBundlePaths := map[string]struct{}{}
388+
for _, b := range registryBundles {
389+
registryBundlePaths[b.BundlePath] = struct{}{}
390+
}
391+
392+
var found, missing []string
393+
for _, b := range bundlePaths {
394+
if _, ok := registryBundlePaths[b]; ok {
395+
found = append(found, b)
396+
continue
397+
}
398+
missing = append(missing, b)
399+
}
400+
if len(missing) > 0 {
401+
return found, missing, fmt.Errorf("target bundlepaths for deprecation missing from registry: %v", missing)
402+
}
403+
return found, missing, nil
404+
}

pkg/lib/registry/registry_test.go

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
package registry
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"errors"
7+
"fmt"
8+
"testing"
9+
"testing/fstest"
10+
11+
"github.com/stretchr/testify/require"
12+
13+
"github.com/operator-framework/operator-registry/internal/model"
14+
"github.com/operator-framework/operator-registry/internal/property"
15+
"github.com/operator-framework/operator-registry/pkg/image"
16+
"github.com/operator-framework/operator-registry/pkg/registry"
17+
)
18+
19+
func fakeBundlePathFromName(name string) string {
20+
return fmt.Sprintf("%s-path", name)
21+
}
22+
23+
func newQuerier(bundles []*model.Bundle) *registry.Querier {
24+
pkgs := map[string]*model.Package{}
25+
channels := map[string]map[string]*model.Channel{}
26+
27+
for _, b := range bundles {
28+
if len(b.Image) == 0 {
29+
b.Image = fakeBundlePathFromName(b.Name)
30+
}
31+
channelName := b.Channel.Name
32+
packageName := b.Package.Name
33+
if _, ok := pkgs[packageName]; !ok {
34+
pkgs[packageName] = &model.Package{
35+
Name: packageName,
36+
}
37+
channels[packageName] = map[string]*model.Channel{
38+
channelName: {
39+
Package: pkgs[packageName],
40+
Name: channelName,
41+
Bundles: map[string]*model.Bundle{b.Name: b},
42+
},
43+
}
44+
pkgs[packageName].Channels = channels[packageName]
45+
pkgs[packageName].DefaultChannel = channels[packageName][channelName]
46+
}
47+
48+
if _, ok := channels[packageName][channelName]; !ok {
49+
channels[packageName][channelName] = &model.Channel{
50+
Package: pkgs[packageName],
51+
Name: channelName,
52+
Bundles: map[string]*model.Bundle{b.Name: b},
53+
}
54+
pkgs[packageName].Channels[channelName] = channels[packageName][channelName]
55+
}
56+
b.Package = pkgs[packageName]
57+
b.Channel = channels[packageName][channelName]
58+
var pkgPropertyFound bool
59+
for _, p := range b.Properties {
60+
if p.Type == property.TypePackage {
61+
pkgPropertyFound = true
62+
break
63+
}
64+
}
65+
if !pkgPropertyFound {
66+
pkgJson, _ := json.Marshal(property.Package{
67+
PackageName: b.Package.Name,
68+
Version: b.Name,
69+
})
70+
b.Properties = append(b.Properties, property.Property{
71+
Type: property.TypePackage,
72+
Value: pkgJson,
73+
})
74+
}
75+
}
76+
return registry.NewQuerier(pkgs)
77+
}
78+
79+
func TestCheckForBundlePaths(t *testing.T) {
80+
type testResult struct {
81+
err error
82+
found []string
83+
missing []string
84+
}
85+
86+
tests := []struct {
87+
description string
88+
querier registry.GRPCQuery
89+
checkPaths []string
90+
expected testResult
91+
}{
92+
{
93+
description: "BundleListPresent",
94+
querier: newQuerier([]*model.Bundle{
95+
{
96+
Package: &model.Package{Name: "pkg-0"},
97+
Channel: &model.Channel{Name: "stable"},
98+
Name: "csv-a",
99+
},
100+
{
101+
Package: &model.Package{Name: "pkg-0"},
102+
Channel: &model.Channel{Name: "alpha"},
103+
Name: "csv-b",
104+
},
105+
}),
106+
checkPaths: []string{
107+
fakeBundlePathFromName("csv-a"),
108+
},
109+
expected: testResult{
110+
err: nil,
111+
found: []string{fakeBundlePathFromName("csv-a")},
112+
missing: nil,
113+
},
114+
},
115+
{
116+
description: "BundleListPartiallyMissing",
117+
querier: newQuerier([]*model.Bundle{
118+
{
119+
Package: &model.Package{Name: "pkg-0"},
120+
Channel: &model.Channel{Name: "stable"},
121+
Name: "csv-a",
122+
},
123+
{
124+
Package: &model.Package{Name: "pkg-0"},
125+
Channel: &model.Channel{Name: "alpha"},
126+
Name: "csv-b",
127+
},
128+
}),
129+
checkPaths: []string{
130+
fakeBundlePathFromName("csv-a"),
131+
fakeBundlePathFromName("missing"),
132+
},
133+
expected: testResult{
134+
err: fmt.Errorf("target bundlepaths for deprecation missing from registry: %v", []string{fakeBundlePathFromName("missing")}),
135+
found: []string{fakeBundlePathFromName("csv-a")},
136+
missing: []string{fakeBundlePathFromName("missing")},
137+
},
138+
},
139+
{
140+
description: "EmptyRegistry",
141+
querier: newQuerier(nil),
142+
checkPaths: []string{
143+
fakeBundlePathFromName("missing"),
144+
},
145+
expected: testResult{
146+
err: nil,
147+
missing: []string{fakeBundlePathFromName("missing")},
148+
},
149+
},
150+
{
151+
description: "EmptyDeprecateList",
152+
querier: newQuerier([]*model.Bundle{
153+
{
154+
Package: &model.Package{Name: "pkg-0"},
155+
Channel: &model.Channel{Name: "stable"},
156+
Name: "csv-a",
157+
},
158+
}),
159+
checkPaths: []string{},
160+
expected: testResult{
161+
err: nil,
162+
found: []string{},
163+
missing: nil,
164+
},
165+
},
166+
{
167+
description: "InvalidQuerier",
168+
querier: registry.NewEmptyQuerier(),
169+
checkPaths: []string{fakeBundlePathFromName("missing")},
170+
expected: testResult{
171+
err: errors.New("empty querier: cannot list bundles"),
172+
found: []string{},
173+
missing: nil,
174+
},
175+
},
176+
}
177+
for _, tt := range tests {
178+
t.Run(tt.description, func(t *testing.T) {
179+
found, missing, err := checkForBundlePaths(tt.querier, tt.checkPaths)
180+
if tt.expected.err != nil {
181+
require.EqualError(t, err, tt.expected.err.Error())
182+
return
183+
}
184+
require.NoError(t, err)
185+
186+
require.EqualValues(t, tt.expected.found, found)
187+
require.EqualValues(t, tt.expected.missing, missing)
188+
})
189+
}
190+
}
191+
192+
func TestUnpackImage(t *testing.T) {
193+
type testResult struct {
194+
dstImage string
195+
err error
196+
}
197+
tests := []struct {
198+
description string
199+
registryImages []string
200+
srcImage image.Reference
201+
expected testResult
202+
}{
203+
{
204+
description: "unpackFS",
205+
registryImages: []string{"image"},
206+
srcImage: image.SimpleReference("image"),
207+
expected: testResult{
208+
dstImage: "image",
209+
err: nil,
210+
},
211+
},
212+
{
213+
description: "missingImage",
214+
registryImages: []string{},
215+
srcImage: image.SimpleReference("missing"),
216+
expected: testResult{
217+
dstImage: "",
218+
err: errors.New("not found"),
219+
},
220+
},
221+
}
222+
for _, tt := range tests {
223+
t.Run(tt.description, func(t *testing.T) {
224+
images := map[image.Reference]*image.MockImage{}
225+
for _, i := range tt.registryImages {
226+
images[image.SimpleReference(i)] = &image.MockImage{
227+
FS: fstest.MapFS{},
228+
}
229+
}
230+
ref, _, cleanup, err := unpackImage(context.TODO(), &image.MockRegistry{RemoteImages: images}, tt.srcImage)
231+
if cleanup != nil {
232+
cleanup()
233+
}
234+
235+
if tt.expected.err != nil {
236+
require.EqualError(t, err, tt.expected.err.Error())
237+
return
238+
}
239+
require.NoError(t, err)
240+
require.EqualValues(t, ref, tt.expected.dstImage)
241+
})
242+
}
243+
}

0 commit comments

Comments
 (0)