Skip to content

Commit f5ee2f9

Browse files
committedJul 19, 2019
refactor(load, query): don't rely on json extensions to sqlite, and
don't rely on types imported from OLM
1 parent 2dcf74f commit f5ee2f9

10 files changed

+337
-183
lines changed
 

‎Makefile

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@ CMDS := $(addprefix bin/, $(shell go list $(MOD_FLAGS) ./cmd/... | xargs -I{} b
66
all: clean test build
77

88
$(CMDS):
9-
go build $(MOD_FLAGS) -tags json1 $(extra_flags) -o $@ ./cmd/$(shell basename $@)
9+
go build $(MOD_FLAGS) $(extra_flags) -o $@ ./cmd/$(shell basename $@)
1010

1111
build: clean $(CMDS)
1212

1313
static: extra_flags=-ldflags '-w -extldflags "-static"'
1414
static: build
1515

1616
unit:
17-
go test $(MOD_FLAGS) -count=1 --tags json1 -v -race ./pkg/...
17+
go test $(MOD_FLAGS) -count=1 -v -race ./pkg/...
1818

1919
image:
2020
docker build .

‎pkg/registry/bundle.go

+24-15
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
package registry
22

33
import (
4-
"fmt"
5-
"k8s.io/apimachinery/pkg/util/yaml"
64
"encoding/json"
5+
"fmt"
76
"strings"
87

9-
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
108
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
119
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
1210
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
1311
"k8s.io/apimachinery/pkg/runtime"
1412
"k8s.io/apimachinery/pkg/runtime/serializer"
13+
"k8s.io/apimachinery/pkg/util/yaml"
1514
)
1615

1716
// Scheme is the default instance of runtime.Scheme to which types in the Kubernetes API are already registered.
@@ -25,10 +24,6 @@ func DefaultYAMLDecoder() runtime.Decoder {
2524
}
2625

2726
func init() {
28-
if err := v1alpha1.AddToScheme(Scheme); err != nil {
29-
panic(err)
30-
}
31-
3227
if err := v1beta1.AddToScheme(Scheme); err != nil {
3328
panic(err)
3429
}
@@ -39,7 +34,7 @@ type Bundle struct {
3934
Objects []*unstructured.Unstructured
4035
Package string
4136
Channel string
42-
csv *v1alpha1.ClusterServiceVersion
37+
csv *ClusterServiceVersion
4338
crds []*apiextensions.CustomResourceDefinition
4439
cacheStale bool
4540
}
@@ -74,7 +69,7 @@ func (b *Bundle) Add(obj *unstructured.Unstructured) {
7469
b.cacheStale = true
7570
}
7671

77-
func (b *Bundle) ClusterServiceVersion() (*v1alpha1.ClusterServiceVersion, error) {
72+
func (b *Bundle) ClusterServiceVersion() (*ClusterServiceVersion, error) {
7873
if err := b.cache(); err != nil {
7974
return nil, err
8075
}
@@ -108,7 +103,8 @@ func (b *Bundle) ProvidedAPIs() (map[APIKey]struct{}, error) {
108103
return nil, err
109104
}
110105

111-
for _, api := range csv.Spec.APIServiceDefinitions.Owned {
106+
ownedAPIs, _, err := csv.GetApiServiceDefinitions()
107+
for _, api := range ownedAPIs {
112108
provided[APIKey{Group: api.Group, Version: api.Version, Kind: api.Kind, Plural: api.Name}] = struct{}{}
113109
}
114110
return provided, nil
@@ -120,15 +116,24 @@ func (b *Bundle) RequiredAPIs() (map[APIKey]struct{}, error) {
120116
if err != nil {
121117
return nil, err
122118
}
123-
for _, api := range csv.Spec.CustomResourceDefinitions.Required {
119+
120+
_, requiredCRDs, err := csv.GetCustomResourceDefintions()
121+
if err != nil {
122+
return nil, err
123+
}
124+
for _, api := range requiredCRDs{
124125
parts := strings.SplitN(api.Name, ".", 2)
125126
if len(parts) < 2 {
126127
return nil, fmt.Errorf("couldn't parse plural.group from crd name: %s", api.Name)
127128
}
128129
required[APIKey{parts[1], api.Version, api.Kind, parts[0]}] = struct{}{}
129130

130131
}
131-
for _, api := range csv.Spec.APIServiceDefinitions.Required {
132+
_, requiredAPIs, err := csv.GetApiServiceDefinitions()
133+
if err != nil {
134+
return nil, err
135+
}
136+
for _, api := range requiredAPIs {
132137
required[APIKey{Group: api.Group, Version: api.Version, Kind: api.Kind, Plural: api.Name}] = struct{}{}
133138
}
134139
return required, nil
@@ -143,8 +148,12 @@ func (b *Bundle) AllProvidedAPIsInBundle() error {
143148
if err != nil {
144149
return err
145150
}
146-
shouldExist := make(map[APIKey]struct{}, len(csv.Spec.CustomResourceDefinitions.Owned))
147-
for _, crdDef := range csv.Spec.CustomResourceDefinitions.Owned {
151+
ownedCRDs, _, err := csv.GetCustomResourceDefintions()
152+
if err != nil {
153+
return err
154+
}
155+
shouldExist := make(map[APIKey]struct{}, len(ownedCRDs))
156+
for _, crdDef := range ownedCRDs {
148157
parts := strings.SplitN(crdDef.Name, ".", 2)
149158
if len(parts) < 2 {
150159
return fmt.Errorf("couldn't parse plural.group from crd name: %s", crdDef.Name)
@@ -191,7 +200,7 @@ func (b *Bundle) cache() error {
191200
}
192201
for _, o := range b.Objects {
193202
if o.GetObjectKind().GroupVersionKind().Kind == "ClusterServiceVersion" {
194-
csv := &v1alpha1.ClusterServiceVersion{}
203+
csv := &ClusterServiceVersion{}
195204
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(o.UnstructuredContent(), csv); err != nil {
196205
return err
197206
}

‎pkg/registry/csv.go

+164
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
package registry
2+
3+
import (
4+
"encoding/json"
5+
6+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
7+
)
8+
9+
const (
10+
// Name of the section under which the list of owned and required list of
11+
// CRD(s) is specified inside an operator manifest.
12+
customResourceDefinitions = "customresourcedefinitions"
13+
14+
// Name of the section under which the list of owned and required list of
15+
// apiservices is specified inside an operator manifest.
16+
apiServiceDefinitions = "apiservicedefinitions"
17+
18+
// The yaml attribute that points to the name of an older
19+
// ClusterServiceVersion object that the current ClusterServiceVersion
20+
// replaces.
21+
replaces = "replaces"
22+
23+
24+
// The yaml attribute that points to the names of older
25+
// ClusterServiceVersion objects that the current ClusterServiceVersion
26+
// skips
27+
skips = "skips"
28+
)
29+
30+
// ClusterServiceVersion is a structured representation of cluster service
31+
// version object(s) specified inside the 'clusterServiceVersions' section of
32+
// an operator manifest.
33+
type ClusterServiceVersion struct {
34+
// Type metadata.
35+
metav1.TypeMeta `json:",inline"`
36+
37+
// Object metadata.
38+
metav1.ObjectMeta `json:"metadata"`
39+
40+
// Spec is the raw representation of the 'spec' element of
41+
// ClusterServiceVersion object. Since we are
42+
// not interested in the content of spec we are not parsing it.
43+
Spec json.RawMessage `json:"spec"`
44+
}
45+
46+
// GetReplaces returns the name of the older ClusterServiceVersion object that
47+
// is replaced by this ClusterServiceVersion object.
48+
//
49+
// If not defined, the function returns an empty string.
50+
func (csv *ClusterServiceVersion) GetReplaces() (string, error) {
51+
var objmap map[string]*json.RawMessage
52+
if err := json.Unmarshal(csv.Spec, &objmap); err != nil {
53+
return "", err
54+
}
55+
56+
rawValue, ok := objmap[replaces]
57+
if !ok || rawValue == nil {
58+
return "", nil
59+
}
60+
61+
var replaces string
62+
if err := json.Unmarshal(*rawValue, &replaces); err != nil {
63+
return "", err
64+
}
65+
66+
return replaces, nil
67+
}
68+
69+
// GetSkips returns the name of the older ClusterServiceVersion objects that
70+
// are skipped by this ClusterServiceVersion object.
71+
//
72+
// If not defined, the function returns an empty string.
73+
func (csv *ClusterServiceVersion) GetSkips() ([]string, error) {
74+
var objmap map[string]*json.RawMessage
75+
if err := json.Unmarshal(csv.Spec, &objmap); err != nil {
76+
return nil, err
77+
}
78+
79+
rawValue, ok := objmap[skips]
80+
if !ok || rawValue == nil {
81+
return nil, nil
82+
}
83+
84+
var skips []string
85+
if err := json.Unmarshal(*rawValue, &skips); err != nil {
86+
return nil, err
87+
}
88+
89+
return skips, nil
90+
}
91+
92+
// GetCustomResourceDefintions returns a list of owned and required
93+
// CustomResourceDefinition object(s) specified inside the
94+
// 'customresourcedefinitions' section of a ClusterServiceVersion 'spec'.
95+
//
96+
// owned represents the list of CRD(s) managed by this ClusterServiceVersion
97+
// object.
98+
// required represents the list of CRD(s) that this ClusterServiceVersion
99+
// object depends on.
100+
//
101+
// If owned or required is not defined in the spec then an empty list is
102+
// returned respectively.
103+
func (csv *ClusterServiceVersion) GetCustomResourceDefintions() (owned []*DefinitionKey, required []*DefinitionKey, err error) {
104+
var objmap map[string]*json.RawMessage
105+
106+
if err = json.Unmarshal(csv.Spec, &objmap); err != nil {
107+
return
108+
}
109+
110+
rawValue, ok := objmap[customResourceDefinitions]
111+
if !ok || rawValue == nil {
112+
return
113+
}
114+
115+
var definitions struct {
116+
Owned []*DefinitionKey `json:"owned"`
117+
Required []*DefinitionKey `json:"required"`
118+
}
119+
120+
if err = json.Unmarshal(*rawValue, &definitions); err != nil {
121+
return
122+
}
123+
124+
owned = definitions.Owned
125+
required = definitions.Required
126+
return
127+
}
128+
129+
// GetApiServiceDefinitions returns a list of owned and required
130+
// APISerivces specified inside the
131+
// 'apiservicedefinitions' section of a ClusterServiceVersion 'spec'.
132+
//
133+
// owned represents the list of apiservices managed by this ClusterServiceVersion
134+
// object.
135+
// required represents the list of apiservices that this ClusterServiceVersion
136+
// object depends on.
137+
//
138+
// If owned or required is not defined in the spec then an empty list is
139+
// returned respectively.
140+
func (csv *ClusterServiceVersion) GetApiServiceDefinitions() (owned []*DefinitionKey, required []*DefinitionKey, err error) {
141+
var objmap map[string]*json.RawMessage
142+
143+
if err = json.Unmarshal(csv.Spec, &objmap); err != nil {
144+
return
145+
}
146+
147+
rawValue, ok := objmap[apiServiceDefinitions]
148+
if !ok || rawValue == nil {
149+
return
150+
}
151+
152+
var definitions struct {
153+
Owned []*DefinitionKey `json:"owned"`
154+
Required []*DefinitionKey `json:"required"`
155+
}
156+
157+
if err = json.Unmarshal(*rawValue, &definitions); err != nil {
158+
return
159+
}
160+
161+
owned = definitions.Owned
162+
required = definitions.Required
163+
return
164+
}

‎pkg/registry/interface.go

-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
type Load interface {
88
AddOperatorBundle(bundle *Bundle) error
99
AddPackageChannels(manifest PackageManifest) error
10-
AddProvidedAPIs() error
1110
}
1211

1312
type Query interface {

‎pkg/registry/types.go

+8
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ func (k APIKey) String() string {
1414
return fmt.Sprintf("%s/%s/%s (%s)", k.Group, k.Version, k.Kind, k.Plural)
1515
}
1616

17+
// DefinitionKey represents the metadata for either an APIservice or a CRD from a CSV spec
18+
type DefinitionKey struct {
19+
Group string `json:"group"`
20+
Kind string `json:"kind"`
21+
Name string `json:"name"`
22+
Version string `json:"version"`
23+
}
24+
1725
// PackageManifest holds information about a package, which is a reference to one (or more)
1826
// channels under a single package.
1927
type PackageManifest struct {

‎pkg/sqlite/configmap.go

+9-10
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"strings"
77

88
"github.com/ghodss/yaml"
9-
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
109
"github.com/operator-framework/operator-registry/pkg/registry"
1110
"github.com/sirupsen/logrus"
1211
"k8s.io/api/core/v1"
@@ -108,23 +107,27 @@ func (c *ConfigMapLoader) Populate() error {
108107
return err
109108
}
110109

111-
var parsedCSVList []v1alpha1.ClusterServiceVersion
112-
err = json.Unmarshal([]byte(csvListJson), &parsedCSVList)
110+
var parsedCSVList []registry.ClusterServiceVersion
111+
err = json.Unmarshal(csvListJson, &parsedCSVList)
113112
if err != nil {
114113
c.log.WithError(err).Debug("error parsing CSV list")
115114
return err
116115
}
117116

118117
for _, csv := range parsedCSVList {
119-
c.log.WithField("csv", csv.Name).Debug("loading CSV")
118+
c.log.WithField("csv", csv.GetName()).Debug("loading CSV")
120119
csvUnst, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&csv)
121120
if err != nil {
122121
c.log.WithError(err).Debug("error remarshalling csv")
123122
return err
124123
}
125124

126125
bundle := registry.NewBundle(csv.GetName(), "", "", &unstructured.Unstructured{Object: csvUnst})
127-
for _, owned := range csv.Spec.CustomResourceDefinitions.Owned {
126+
ownedCRDs, _, err := csv.GetCustomResourceDefintions()
127+
if err != nil {
128+
return err
129+
}
130+
for _, owned := range ownedCRDs {
128131
split := strings.SplitN(owned.Name, ".", 2)
129132
if len(split) < 2 {
130133
c.log.WithError(err).Debug("error parsing owned name")
@@ -155,7 +158,7 @@ func (c *ConfigMapLoader) Populate() error {
155158
}
156159

157160
var parsedPackageManifests []registry.PackageManifest
158-
err = json.Unmarshal([]byte(packageListJson), &parsedPackageManifests)
161+
err = json.Unmarshal(packageListJson, &parsedPackageManifests)
159162
if err != nil {
160163
c.log.WithError(err).Debug("error parsing package list")
161164
return err
@@ -167,9 +170,5 @@ func (c *ConfigMapLoader) Populate() error {
167170
}
168171
}
169172

170-
c.log.Info("extracting provided API information")
171-
if err := c.store.AddProvidedAPIs(); err != nil {
172-
return err
173-
}
174173
return nil
175174
}

‎pkg/sqlite/configmap_test.go

+1-1
Large diffs are not rendered by default.

‎pkg/sqlite/directory.go

+10-22
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,20 @@ import (
77
"path/filepath"
88
"strings"
99

10-
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
1110
"github.com/sirupsen/logrus"
1211
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
1312
"k8s.io/apimachinery/pkg/util/yaml"
1413

1514
"github.com/operator-framework/operator-registry/pkg/registry"
16-
"github.com/operator-framework/operator-registry/pkg/schema"
1715
)
1816

17+
const ClusterServiceVersionKind = "ClusterServiceVersion"
18+
1919
type SQLPopulator interface {
2020
Populate() error
2121
}
2222

2323
// DirectoryLoader loads a directory of resources into the database
24-
// files ending in `.crd.yaml` will be parsed as CRDs
25-
// files ending in `.clusterserviceversion.yaml` will be parsed as CSVs
26-
// files ending in `.package.yaml` will be parsed as Packages
2724
type DirectoryLoader struct {
2825
store registry.Load
2926
directory string
@@ -41,25 +38,16 @@ func NewSQLLoaderForDirectory(store registry.Load, directory string) *DirectoryL
4138
func (d *DirectoryLoader) Populate() error {
4239
log := logrus.WithField("dir", d.directory)
4340

44-
log.Info("validating manifests")
45-
if err := schema.CheckCatalogResources(d.directory); err != nil {
46-
return err
47-
}
48-
4941
log.Info("loading Bundles")
5042
if err := filepath.Walk(d.directory, d.LoadBundleWalkFunc); err != nil {
5143
return err
5244
}
5345

54-
log.Info("loading Packages")
46+
log.Info("loading Packages and Entries")
5547
if err := filepath.Walk(d.directory, d.LoadPackagesWalkFunc); err != nil {
5648
return err
5749
}
5850

59-
log.Info("extracting provided API information")
60-
if err := d.store.AddProvidedAPIs(); err != nil {
61-
return err
62-
}
6351
return nil
6452
}
6553

@@ -93,13 +81,13 @@ func (d *DirectoryLoader) LoadBundleWalkFunc(path string, f os.FileInfo, err err
9381
}
9482

9583
decoder := yaml.NewYAMLOrJSONDecoder(fileReader, 30)
96-
csv := v1alpha1.ClusterServiceVersion{}
84+
csv := unstructured.Unstructured{}
9785

9886
if err = decoder.Decode(&csv); err != nil {
9987
return nil
10088
}
10189

102-
if csv.Kind != v1alpha1.ClusterServiceVersionKind {
90+
if csv.GetKind() != ClusterServiceVersionKind {
10391
return nil
10492
}
10593

@@ -110,7 +98,7 @@ func (d *DirectoryLoader) LoadBundleWalkFunc(path string, f os.FileInfo, err err
11098
return fmt.Errorf("error loading objs in dir: %s", err.Error())
11199
}
112100

113-
if bundle.Size() == 0 {
101+
if bundle == nil || bundle.Size() == 0 {
114102
log.Warnf("no bundle objects found")
115103
return nil
116104
}
@@ -155,15 +143,15 @@ func (d *DirectoryLoader) LoadBundle(csvName string, dir string) (*registry.Bund
155143

156144
if err = decoder.Decode(obj); err != nil {
157145
log.Infof("could not decode contents of file %s into file: %v", path, err)
158-
return nil, nil
146+
continue
159147
}
160148

161149
// Don't include other CSVs in the bundle
162150
if obj.GetKind() == "ClusterServiceVersion" && obj.GetName() != csvName {
163151
continue
164152
}
165153

166-
if obj != nil {
154+
if obj.Object != nil {
167155
bundle.Add(obj)
168156
}
169157

@@ -198,8 +186,8 @@ func (d *DirectoryLoader) LoadPackagesWalkFunc(path string, f os.FileInfo, err e
198186
decoder := yaml.NewYAMLOrJSONDecoder(fileReader, 30)
199187
manifest := registry.PackageManifest{}
200188
if err = decoder.Decode(&manifest); err != nil {
201-
log.Infof("could not decode contents of file %s into package: %v", path, err)
202-
return nil
189+
log.Infof("could not decode contents of file %s into package: %v", path, err)
190+
return nil
203191
}
204192
if manifest.PackageName == "" {
205193
return nil

‎pkg/sqlite/directory_test.go

+10-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@ func TestDirectoryLoader(t *testing.T) {
1616

1717
store, err := NewSQLLiteLoader("test.db")
1818
require.NoError(t, err)
19-
defer os.Remove("test.db")
19+
defer func() {
20+
if err := os.Remove("test.db"); err != nil {
21+
t.Fatal(err)
22+
}
23+
}()
2024

2125
loader := NewSQLLoaderForDirectory(store, "../../manifests")
2226
require.NoError(t, loader.Populate())
@@ -25,7 +29,11 @@ func TestDirectoryLoader(t *testing.T) {
2529
func TestQuerierForDirectory(t *testing.T) {
2630
load, err := NewSQLLiteLoader("test.db")
2731
require.NoError(t, err)
28-
defer os.Remove("test.db")
32+
defer func() {
33+
if err := os.Remove("test.db"); err != nil {
34+
t.Fatal(err)
35+
}
36+
}()
2937

3038
loader := NewSQLLoaderForDirectory(load, "../../manifests")
3139
require.NoError(t, loader.Populate())

‎pkg/sqlite/load.go

+109-130
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ package sqlite
22

33
import (
44
"database/sql"
5-
"encoding/json"
65
"fmt"
76
"strings"
87

98
_ "github.com/mattn/go-sqlite3"
9+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
10+
"k8s.io/apimachinery/pkg/runtime"
11+
"k8s.io/apimachinery/pkg/util/yaml"
1012

1113
"github.com/operator-framework/operator-registry/pkg/registry"
1214
)
@@ -69,7 +71,6 @@ func NewSQLLiteLoader(outFilename string) (*SQLLoader, error) {
6971
FOREIGN KEY(channel_entry_id) REFERENCES channel_entry(entry_id),
7072
FOREIGN KEY(group_name, version, kind) REFERENCES api(group_name, version, kind)
7173
);
72-
CREATE INDEX IF NOT EXISTS replaces ON operatorbundle(json_extract(csv, '$.spec.replaces'));
7374
`
7475

7576
if _, err = db.Exec(createTable); err != nil {
@@ -156,31 +157,23 @@ func (s *SQLLoader) AddPackageChannels(manifest registry.PackageManifest) error
156157
}
157158
defer addChannelEntry.Close()
158159

159-
getReplaces, err := tx.Prepare(`
160-
SELECT DISTINCT json_extract(operatorbundle.csv, '$.spec.replaces')
161-
FROM operatorbundle,json_tree(operatorbundle.csv)
162-
WHERE operatorbundle.name IS ?
163-
`)
160+
addReplaces, err := tx.Prepare("update channel_entry set replaces = ? where entry_id = ?")
164161
if err != nil {
165162
return err
166163
}
167-
defer getReplaces.Close()
164+
defer addReplaces.Close()
168165

169-
getSkips, err := tx.Prepare(`
170-
SELECT DISTINCT value
171-
FROM operatorbundle,json_each(operatorbundle.csv, '$.spec.skips')
172-
WHERE operatorbundle.name IS ?
173-
`)
166+
addAPI, err := tx.Prepare("insert or replace into api(group_name, version, kind, plural) values(?, ?, ?, ?)")
174167
if err != nil {
175168
return err
176169
}
177-
defer getSkips.Close()
170+
defer addAPI.Close()
178171

179-
addReplaces, err := tx.Prepare("update channel_entry set replaces = ? where entry_id = ?")
172+
addAPIProvider, err := tx.Prepare("insert into api_provider(group_name, version, kind, channel_entry_id) values(?, ?, ?, ?)")
180173
if err != nil {
181174
return err
182175
}
183-
defer addReplaces.Close()
176+
defer addAPIProvider.Close()
184177

185178
for _, c := range manifest.Channels {
186179
res, err := addChannelEntry.Exec(c.Name, manifest.PackageName, c.CurrentCSVName, 0)
@@ -196,24 +189,24 @@ func (s *SQLLoader) AddPackageChannels(manifest registry.PackageManifest) error
196189
depth := 1
197190
for {
198191

199-
// create skip entries
200-
skipRows, err := getSkips.Query(channelEntryCSVName)
192+
// Get CSV for current entry
193+
channelEntryCSV, err := s.getCSV(tx, channelEntryCSVName)
201194
if err != nil {
202195
return err
203196
}
204197

205-
for skipRows.Next() {
206-
var skips sql.NullString
207-
if err := skipRows.Scan(&skips); err != nil {
208-
return err
209-
}
198+
if err := s.addProvidedAPIs(tx, channelEntryCSV, currentID); err != nil {
199+
return err
200+
}
210201

211-
if !skips.Valid || skips.String == "" {
212-
break
213-
}
202+
skips, err := channelEntryCSV.GetSkips()
203+
if err != nil {
204+
return err
205+
}
214206

207+
for _, skip := range skips {
215208
// add dummy channel entry for the skipped version
216-
skippedChannelEntry, err := addChannelEntry.Exec(c.Name, manifest.PackageName, skips.String, depth)
209+
skippedChannelEntry, err := addChannelEntry.Exec(c.Name, manifest.PackageName, skip, depth)
217210
if err != nil {
218211
return err
219212
}
@@ -229,160 +222,146 @@ func (s *SQLLoader) AddPackageChannels(manifest registry.PackageManifest) error
229222
return err
230223
}
231224

232-
233225
synthesizedID, err := synthesizedChannelEntry.LastInsertId()
234226
if err != nil {
235227
return err
236228
}
237229

238-
_, err = addReplaces.Exec(skippedID, synthesizedID)
239-
if err != nil {
230+
if _, err = addReplaces.Exec(skippedID, synthesizedID); err != nil {
231+
return err
232+
}
233+
234+
if err := s.addProvidedAPIs(tx, channelEntryCSV, synthesizedID); err != nil {
240235
return err
241236
}
242237

243238
depth += 1
244239
}
245240

246241
// create real replacement chain
247-
replaceRows, err := getReplaces.Query(channelEntryCSVName)
242+
replaces, err := channelEntryCSV.GetReplaces()
248243
if err != nil {
249244
return err
250245
}
251246

252-
if replaceRows.Next() {
253-
var replaced sql.NullString
254-
if err := replaceRows.Scan(&replaced); err != nil {
255-
return err
256-
}
257-
258-
if !replaced.Valid || replaced.String == "" {
259-
break
260-
}
247+
if replaces == "" {
248+
// we've walked the channel until there was no replacement
249+
break
250+
}
261251

262-
replacedChannelEntry, err := addChannelEntry.Exec(c.Name, manifest.PackageName, replaced.String, depth)
263-
if err != nil {
264-
return err
265-
}
266-
replacedID, err := replacedChannelEntry.LastInsertId()
267-
if err != nil {
268-
return err
269-
}
270-
_, err = addReplaces.Exec(replacedID, currentID)
271-
if err != nil {
272-
return err
273-
}
274-
currentID = replacedID
275-
channelEntryCSVName = replaced.String
276-
depth += 1
277-
} else {
252+
replaced, err := s.getCSV(tx, replaces)
253+
if err != nil {
278254
return fmt.Errorf("%s specifies replacement that couldn't be found", c.CurrentCSVName)
279255
}
256+
257+
replacedChannelEntry, err := addChannelEntry.Exec(c.Name, manifest.PackageName, replaced.GetName(), depth)
258+
if err != nil {
259+
return err
260+
}
261+
replacedID, err := replacedChannelEntry.LastInsertId()
262+
if err != nil {
263+
return err
264+
}
265+
_, err = addReplaces.Exec(replacedID, currentID)
266+
if err != nil {
267+
return err
268+
}
269+
currentID = replacedID
270+
channelEntryCSVName = replaces
271+
depth += 1
280272
}
281273
}
282274
return tx.Commit()
283275
}
284276

285-
func (s *SQLLoader) AddProvidedAPIs() error {
286-
tx, err := s.db.Begin()
287-
if err != nil {
288-
return err
289-
}
290-
addAPI, err := tx.Prepare("insert or replace into api(group_name, version, kind, plural) values(?, ?, ?, ?)")
291-
if err != nil {
292-
return err
277+
func (s *SQLLoader) Close() error {
278+
return s.db.Close()
279+
}
280+
281+
func SplitCRDName(crdName string) (plural, group string, err error) {
282+
pluralGroup := strings.SplitN(crdName, ".", 2)
283+
if len(pluralGroup) != 2 {
284+
err = fmt.Errorf("can't split bad CRD name %s", crdName)
285+
return
293286
}
294-
defer addAPI.Close()
295287

296-
addAPIProvider, err := tx.Prepare("insert into api_provider(group_name, version, kind, channel_entry_id) values(?, ?, ?, ?)")
288+
plural = pluralGroup[0]
289+
group = pluralGroup[1]
290+
return
291+
}
292+
293+
func (s *SQLLoader) getCSV(tx *sql.Tx, csvName string) (*registry.ClusterServiceVersion, error) {
294+
getCSV, err := tx.Prepare(`
295+
SELECT DISTINCT operatorbundle.csv
296+
FROM operatorbundle
297+
WHERE operatorbundle.name=? LIMIT 1`)
297298
if err != nil {
298-
return err
299+
return nil, err
299300
}
300-
defer addAPIProvider.Close()
301+
defer getCSV.Close()
301302

302-
// get CRD provided APIs
303-
getChannelEntryProvidedAPIs, err := tx.Prepare(`
304-
SELECT DISTINCT channel_entry.entry_id, json_extract(json_each.value, '$.name', '$.version', '$.kind')
305-
FROM channel_entry INNER JOIN operatorbundle,json_each(operatorbundle.csv, '$.spec.customresourcedefinitions.owned')
306-
ON channel_entry.operatorbundle_name = operatorbundle.name`)
303+
rows, err := getCSV.Query(csvName)
307304
if err != nil {
308-
return err
305+
return nil, err
306+
}
307+
if !rows.Next() {
308+
return nil, fmt.Errorf("no bundle found for csv %s", csvName)
309+
}
310+
var csvStringSQL sql.NullString
311+
if err := rows.Scan(&csvStringSQL); err != nil {
312+
return nil, err
309313
}
310-
defer getChannelEntryProvidedAPIs.Close()
311314

312-
rows, err := getChannelEntryProvidedAPIs.Query()
313-
if err != nil {
314-
return err
315+
dec := yaml.NewYAMLOrJSONDecoder(strings.NewReader(csvStringSQL.String), 10)
316+
unst := &unstructured.Unstructured{}
317+
if err := dec.Decode(unst); err != nil {
318+
return nil, err
315319
}
316-
for rows.Next() {
317-
var channelId sql.NullInt64
318-
var gvkSQL sql.NullString
319320

320-
if err := rows.Scan(&channelId, &gvkSQL); err != nil {
321-
return err
322-
}
323-
apigvk := []string{}
324-
if err := json.Unmarshal([]byte(gvkSQL.String), &apigvk); err != nil {
325-
return err
326-
}
327-
plural, group, err := SplitCRDName(apigvk[0])
328-
if err != nil {
329-
return err
330-
}
331-
if _, err := addAPI.Exec(group, apigvk[1], apigvk[2], plural); err != nil {
332-
return err
333-
}
334-
if _, err := addAPIProvider.Exec(group, apigvk[1], apigvk[2], channelId.Int64); err != nil {
335-
return err
336-
}
321+
csv := &registry.ClusterServiceVersion{}
322+
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unst.UnstructuredContent(), csv); err != nil {
323+
return nil, err
337324
}
338325

339-
getChannelEntryProvidedAPIsAPIService, err := tx.Prepare(`
340-
SELECT DISTINCT channel_entry.entry_id, json_extract(json_each.value, '$.group', '$.version', '$.kind', '$.name')
341-
FROM channel_entry INNER JOIN operatorbundle,json_each(operatorbundle.csv, '$.spec.apiservicedefinitions.owned')
342-
ON channel_entry.operatorbundle_name = operatorbundle.name`)
326+
return csv, nil
327+
}
328+
329+
330+
func (s *SQLLoader) addProvidedAPIs(tx *sql.Tx, csv *registry.ClusterServiceVersion, channelEntryId int64) error {
331+
addAPI, err := tx.Prepare("insert or replace into api(group_name, version, kind, plural) values(?, ?, ?, ?)")
343332
if err != nil {
344333
return err
345334
}
346-
defer getChannelEntryProvidedAPIsAPIService.Close()
335+
defer addAPI.Close()
347336

348-
rows, err = getChannelEntryProvidedAPIsAPIService.Query()
337+
addAPIProvider, err := tx.Prepare("insert into api_provider(group_name, version, kind, channel_entry_id) values(?, ?, ?, ?)")
349338
if err != nil {
350339
return err
351340
}
352-
for rows.Next() {
353-
var channelId sql.NullInt64
354-
var gvkSQL sql.NullString
341+
defer addAPIProvider.Close()
355342

356-
if err := rows.Scan(&channelId, &gvkSQL); err != nil {
357-
return err
358-
}
359-
apigvk := []string{}
360-
if err := json.Unmarshal([]byte(gvkSQL.String), &apigvk); err != nil {
343+
ownedCRDs, _, err := csv.GetCustomResourceDefintions()
344+
for _, crd := range ownedCRDs {
345+
plural, group, err := SplitCRDName(crd.Name)
346+
if err != nil {
361347
return err
362348
}
363-
if _, err := addAPI.Exec(apigvk[0], apigvk[1], apigvk[2], apigvk[3]); err != nil {
349+
if _, err := addAPI.Exec(group, crd.Version, crd.Kind, plural); err != nil {
364350
return err
365351
}
366-
if _, err := addAPIProvider.Exec(apigvk[0], apigvk[1], apigvk[2], channelId.Int64); err != nil {
352+
if _, err := addAPIProvider.Exec(group, crd.Version, crd.Kind, channelEntryId); err != nil {
367353
return err
368354
}
369355
}
370356

371-
return tx.Commit()
372-
}
373-
374-
func (s *SQLLoader) Close() {
375-
s.db.Close()
376-
}
377-
378-
func SplitCRDName(crdName string) (plural, group string, err error) {
379-
pluralGroup := strings.SplitN(crdName, ".", 2)
380-
if len(pluralGroup) != 2 {
381-
err = fmt.Errorf("can't split bad CRD name %s", crdName)
382-
return
357+
ownedAPIs, _, err := csv.GetApiServiceDefinitions()
358+
for _, api := range ownedAPIs {
359+
if _, err := addAPI.Exec(api.Group, api.Version, api.Kind, api.Name); err != nil {
360+
return err
361+
}
362+
if _, err := addAPIProvider.Exec(api.Group, api.Version, api.Kind, channelEntryId); err != nil {
363+
return err
364+
}
383365
}
384-
385-
plural = pluralGroup[0]
386-
group = pluralGroup[1]
387-
return
366+
return nil
388367
}

0 commit comments

Comments
 (0)
Please sign in to comment.