Skip to content

Commit 04a1984

Browse files
joelanfordanik120
authored andcommitted
add a new feature-gated api/v1/query endpoint
Signed-off-by: Joe Lanford <[email protected]>
1 parent a19f91a commit 04a1984

File tree

5 files changed

+387
-87
lines changed

5 files changed

+387
-87
lines changed

catalogd/cmd/catalogd/main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,9 +294,9 @@ func main() {
294294
os.Exit(1)
295295
}
296296

297-
localStorage = storage.LocalDirV1{RootDir: storeDir, RootURL: baseStorageURL}
297+
localStorage = &storage.LocalDirV1{RootDir: storeDir, RootURL: baseStorageURL}
298298

299-
// Config for the the catalogd web server
299+
// Config for the catalogd web server
300300
catalogServerConfig := serverutil.CatalogServerConfig{
301301
ExternalAddr: externalAddr,
302302
CatalogAddr: catalogServerAddr,

catalogd/internal/features/features.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,13 @@ import (
55
"k8s.io/component-base/featuregate"
66
)
77

8-
var catalogdFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{}
8+
const (
9+
APIV1QueryHandler = featuregate.Feature("APIV1QueryHandler")
10+
)
11+
12+
var catalogdFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
13+
APIV1QueryHandler: {Default: false, PreRelease: featuregate.Alpha},
14+
}
915

1016
var CatalogdFeatureGate featuregate.MutableFeatureGate = featuregate.NewFeatureGate()
1117

catalogd/internal/storage/index.go

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
package storage
2+
3+
import (
4+
"cmp"
5+
"encoding/json"
6+
"errors"
7+
"fmt"
8+
"io"
9+
"slices"
10+
11+
"k8s.io/apimachinery/pkg/util/sets"
12+
13+
"github.com/operator-framework/operator-registry/alpha/declcfg"
14+
)
15+
16+
type index struct {
17+
BySchema map[string][]section `json:"by_schema"`
18+
ByPackage map[string][]section `json:"by_package"`
19+
ByName map[string][]section `json:"by_name"`
20+
}
21+
22+
type section struct {
23+
offset int64
24+
length int64
25+
}
26+
27+
func (s *section) MarshalJSON() ([]byte, error) {
28+
return []byte(fmt.Sprintf(`[%d,%d]`, s.offset, s.length)), nil
29+
}
30+
31+
func (s *section) UnmarshalJSON(b []byte) error {
32+
vals := [2]int64{}
33+
if err := json.Unmarshal(b, &vals); err != nil {
34+
return err
35+
}
36+
s.offset = vals[0]
37+
s.length = vals[1]
38+
return nil
39+
}
40+
41+
func (i index) Size() int64 {
42+
size := 0
43+
for k, v := range i.BySchema {
44+
size += len(k) + len(v)*16
45+
}
46+
for k, v := range i.ByPackage {
47+
size += len(k) + len(v)*16
48+
}
49+
for k, v := range i.ByName {
50+
size += len(k) + len(v)*16
51+
}
52+
return int64(size)
53+
}
54+
55+
func (i index) Get(r io.ReaderAt, schema, packageName, name string) (io.Reader, bool) {
56+
sectionSet := i.getSectionSet(schema, packageName, name)
57+
58+
sections := sectionSet.UnsortedList()
59+
slices.SortFunc(sections, func(a, b section) int {
60+
return cmp.Compare(a.offset, b.offset)
61+
})
62+
63+
srs := make([]io.Reader, 0, len(sections))
64+
for _, s := range sections {
65+
sr := io.NewSectionReader(r, s.offset, s.length)
66+
srs = append(srs, sr)
67+
}
68+
return io.MultiReader(srs...), true
69+
}
70+
71+
func (i *index) getSectionSet(schema, packageName, name string) sets.Set[section] {
72+
if schema == "" {
73+
if packageName == "" {
74+
if name == "" {
75+
sectionSet := sets.New[section]()
76+
for _, s := range i.BySchema {
77+
sectionSet.Insert(s...)
78+
}
79+
return sectionSet
80+
} else {
81+
return sets.New[section](i.ByName[name]...)
82+
}
83+
} else {
84+
sectionSet := sets.New[section](i.ByPackage[packageName]...)
85+
if name == "" {
86+
return sectionSet
87+
} else {
88+
return sectionSet.Intersection(sets.New[section](i.ByName[name]...))
89+
}
90+
}
91+
} else {
92+
sectionSet := sets.New[section](i.BySchema[schema]...)
93+
if packageName == "" {
94+
if name == "" {
95+
return sectionSet
96+
} else {
97+
return sectionSet.Intersection(sets.New[section](i.ByName[name]...))
98+
}
99+
} else {
100+
sectionSet = sectionSet.Intersection(sets.New[section](i.ByPackage[packageName]...))
101+
if name == "" {
102+
return sectionSet
103+
} else {
104+
return sectionSet.Intersection(sets.New[section](i.ByName[name]...))
105+
}
106+
}
107+
}
108+
}
109+
110+
func newIndex(r io.Reader) (*index, error) {
111+
idx := &index{
112+
BySchema: make(map[string][]section),
113+
ByPackage: make(map[string][]section),
114+
ByName: make(map[string][]section),
115+
}
116+
var meta declcfg.Meta
117+
dec := json.NewDecoder(r)
118+
for {
119+
i1 := dec.InputOffset()
120+
if err := dec.Decode(&meta); err != nil {
121+
if errors.Is(err, io.EOF) {
122+
break
123+
}
124+
return nil, err
125+
}
126+
i2 := dec.InputOffset()
127+
start := i1
128+
length := i2 - i1
129+
130+
s := section{offset: start, length: length}
131+
idx.BySchema[meta.Schema] = append(idx.BySchema[meta.Schema], s)
132+
idx.ByPackage[meta.Package] = append(idx.ByPackage[meta.Package], s)
133+
idx.ByName[meta.Name] = append(idx.ByName[meta.Name], s)
134+
}
135+
return idx, nil
136+
}

0 commit comments

Comments
 (0)