Skip to content

Commit a151be5

Browse files
committed
attempt to publish FBC read/write
Signed-off-by: Jordan Keister <[email protected]>
1 parent 8c8babd commit a151be5

21 files changed

+5425
-20
lines changed

go.mod

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
module github.com/operator-framework/api
1+
module github.com/grokspawn/api
22

33
go 1.18
44

@@ -8,18 +8,25 @@ require (
88
github.com/ghodss/yaml v1.0.0
99
github.com/go-bindata/go-bindata/v3 v3.1.3
1010
github.com/google/cel-go v0.10.1
11-
github.com/google/go-cmp v0.5.6 // indirect
1211
github.com/sirupsen/logrus v1.8.1
1312
github.com/spf13/cobra v1.4.0
1413
github.com/stretchr/testify v1.7.0
15-
google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368
14+
google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac
1615
k8s.io/api v0.24.0
1716
k8s.io/apiextensions-apiserver v0.24.0
1817
k8s.io/apimachinery v0.24.0
1918
k8s.io/client-go v0.24.0
2019
sigs.k8s.io/controller-runtime v0.12.1
2120
)
2221

22+
require (
23+
github.com/h2non/filetype v1.1.3
24+
github.com/h2non/go-is-svg v0.0.0-20160927212452-35e8c4b0612c
25+
github.com/joelanford/ignore v0.0.0-20210610194209-63d4919d8fb2
26+
github.com/operator-framework/operator-registry v1.26.2
27+
sigs.k8s.io/yaml v1.3.0
28+
)
29+
2330
require (
2431
github.com/PuerkitoBio/purell v1.1.1 // indirect
2532
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
@@ -29,6 +36,9 @@ require (
2936
github.com/cespare/xxhash/v2 v2.1.2 // indirect
3037
github.com/davecgh/go-spew v1.1.1 // indirect
3138
github.com/felixge/httpsnoop v1.0.1 // indirect
39+
github.com/go-git/gcfg v1.5.0 // indirect
40+
github.com/go-git/go-billy/v5 v5.1.0 // indirect
41+
github.com/go-git/go-git/v5 v5.3.0 // indirect
3242
github.com/go-logr/logr v1.2.0 // indirect
3343
github.com/go-openapi/jsonpointer v0.19.5 // indirect
3444
github.com/go-openapi/jsonreference v0.19.5 // indirect
@@ -38,10 +48,11 @@ require (
3848
github.com/golang/protobuf v1.5.2 // indirect
3949
github.com/google/gnostic v0.5.7-v3refs // indirect
4050
github.com/google/gofuzz v1.1.0 // indirect
41-
github.com/google/uuid v1.1.2 // indirect
51+
github.com/google/uuid v1.2.0 // indirect
4252
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
4353
github.com/imdario/mergo v0.3.12 // indirect
4454
github.com/inconshreveable/mousetrap v1.0.0 // indirect
55+
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
4556
github.com/josharian/intern v1.0.0 // indirect
4657
github.com/json-iterator/go v1.1.12 // indirect
4758
github.com/kisielk/errcheck v1.5.0 // indirect
@@ -69,18 +80,19 @@ require (
6980
go.opentelemetry.io/proto/otlp v0.7.0 // indirect
7081
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
7182
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
72-
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
83+
golang.org/x/net v0.0.0-20220407224826-aac1ed45d8e3 // indirect
7384
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
74-
golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect
85+
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec // indirect
7586
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
7687
golang.org/x/text v0.3.7 // indirect
7788
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
78-
golang.org/x/tools v0.1.10-0.20220218145154-897bd77cd717 // indirect
89+
golang.org/x/tools v0.1.10 // indirect
7990
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
8091
google.golang.org/appengine v1.6.7 // indirect
81-
google.golang.org/grpc v1.40.0 // indirect
82-
google.golang.org/protobuf v1.27.1 // indirect
92+
google.golang.org/grpc v1.45.0 // indirect
93+
google.golang.org/protobuf v1.28.0 // indirect
8394
gopkg.in/inf.v0 v0.9.1 // indirect
95+
gopkg.in/warnings.v0 v0.1.2 // indirect
8496
gopkg.in/yaml.v2 v2.4.0 // indirect
8597
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
8698
k8s.io/apiserver v0.24.0 // indirect
@@ -91,5 +103,4 @@ require (
91103
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.30 // indirect
92104
sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect
93105
sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect
94-
sigs.k8s.io/yaml v1.3.0 // indirect
95106
)

go.sum

Lines changed: 63 additions & 10 deletions
Large diffs are not rendered by default.

pkg/lib/declcfg/declcfg.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package declcfg
2+
3+
import (
4+
"encoding/json"
5+
6+
"github.com/operator-framework/operator-registry/alpha/property"
7+
)
8+
9+
const (
10+
SchemaPackage = "olm.package"
11+
SchemaChannel = "olm.channel"
12+
SchemaBundle = "olm.bundle"
13+
)
14+
15+
type DeclarativeConfig struct {
16+
Packages []Package
17+
Channels []Channel
18+
Bundles []Bundle
19+
Others []Meta
20+
}
21+
22+
type Package struct {
23+
Schema string `json:"schema"`
24+
Name string `json:"name"`
25+
DefaultChannel string `json:"defaultChannel"`
26+
Icon *Icon `json:"icon,omitempty"`
27+
Description string `json:"description,omitempty"`
28+
Properties []property.Property `json:"properties,omitempty" hash:"set"`
29+
}
30+
31+
type Icon struct {
32+
Data []byte `json:"base64data"`
33+
MediaType string `json:"mediatype"`
34+
}
35+
36+
type Channel struct {
37+
Schema string `json:"schema"`
38+
Name string `json:"name"`
39+
Package string `json:"package"`
40+
Entries []ChannelEntry `json:"entries"`
41+
Properties []property.Property `json:"properties,omitempty" hash:"set"`
42+
}
43+
44+
type ChannelEntry struct {
45+
Name string `json:"name"`
46+
Replaces string `json:"replaces,omitempty"`
47+
Skips []string `json:"skips,omitempty"`
48+
SkipRange string `json:"skipRange,omitempty"`
49+
}
50+
51+
// Bundle specifies all metadata and data of a bundle object.
52+
// Top-level fields are the source of truth, i.e. not CSV values.
53+
//
54+
// Notes:
55+
// - Any field slice type field or type containing a slice somewhere
56+
// where two types/fields are equal if their contents are equal regardless
57+
// of order must have a `hash:"set"` field tag for bundle comparison.
58+
// - Any fields that have a `json:"-"` tag must be included in the equality
59+
// evaluation in bundlesEqual().
60+
type Bundle struct {
61+
Schema string `json:"schema"`
62+
Name string `json:"name"`
63+
Package string `json:"package"`
64+
Image string `json:"image"`
65+
Properties []property.Property `json:"properties,omitempty" hash:"set"`
66+
RelatedImages []RelatedImage `json:"relatedImages,omitempty" hash:"set"`
67+
68+
// These fields are present so that we can continue serving
69+
// the GRPC API the way packageserver expects us to in a
70+
// backwards-compatible way. These are populated from
71+
// any `olm.bundle.object` properties.
72+
//
73+
// These fields will never be persisted in the bundle blob as
74+
// first class fields.
75+
CsvJSON string `json:"-"`
76+
Objects []string `json:"-"`
77+
}
78+
79+
type RelatedImage struct {
80+
Name string `json:"name"`
81+
Image string `json:"image"`
82+
}
83+
84+
type Meta struct {
85+
Schema string
86+
Package string
87+
88+
Blob json.RawMessage
89+
}
90+
91+
func (m Meta) MarshalJSON() ([]byte, error) {
92+
return m.Blob, nil
93+
}
94+
95+
func (m *Meta) UnmarshalJSON(blob []byte) error {
96+
type tmp struct {
97+
Schema string `json:"schema"`
98+
Package string `json:"package,omitempty"`
99+
Properties []property.Property `json:"properties,omitempty"`
100+
}
101+
var t tmp
102+
if err := json.Unmarshal(blob, &t); err != nil {
103+
return err
104+
}
105+
m.Schema = t.Schema
106+
m.Package = t.Package
107+
m.Blob = blob
108+
return nil
109+
}
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
package declcfg
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/blang/semver/v4"
7+
"k8s.io/apimachinery/pkg/util/sets"
8+
9+
"github.com/operator-framework/operator-registry/alpha/model"
10+
"github.com/operator-framework/operator-registry/alpha/property"
11+
)
12+
13+
func ConvertToModel(cfg DeclarativeConfig) (model.Model, error) {
14+
mpkgs := model.Model{}
15+
defaultChannels := map[string]string{}
16+
for _, p := range cfg.Packages {
17+
if p.Name == "" {
18+
return nil, fmt.Errorf("config contains package with no name")
19+
}
20+
21+
if _, ok := mpkgs[p.Name]; ok {
22+
return nil, fmt.Errorf("duplicate package %q", p.Name)
23+
}
24+
25+
mpkg := &model.Package{
26+
Name: p.Name,
27+
Description: p.Description,
28+
Channels: map[string]*model.Channel{},
29+
}
30+
if p.Icon != nil {
31+
mpkg.Icon = &model.Icon{
32+
Data: p.Icon.Data,
33+
MediaType: p.Icon.MediaType,
34+
}
35+
}
36+
defaultChannels[p.Name] = p.DefaultChannel
37+
mpkgs[p.Name] = mpkg
38+
}
39+
40+
channelDefinedEntries := map[string]sets.String{}
41+
for _, c := range cfg.Channels {
42+
mpkg, ok := mpkgs[c.Package]
43+
if !ok {
44+
return nil, fmt.Errorf("unknown package %q for channel %q", c.Package, c.Name)
45+
}
46+
47+
if c.Name == "" {
48+
return nil, fmt.Errorf("package %q contains channel with no name", c.Package)
49+
}
50+
51+
if _, ok := mpkg.Channels[c.Name]; ok {
52+
return nil, fmt.Errorf("package %q has duplicate channel %q", c.Package, c.Name)
53+
}
54+
55+
mch := &model.Channel{
56+
Package: mpkg,
57+
Name: c.Name,
58+
Bundles: map[string]*model.Bundle{},
59+
// NOTICE: The field Properties of the type Channel is for internal use only.
60+
// DO NOT use it for any public-facing functionalities.
61+
// This API is in alpha stage and it is subject to change.
62+
Properties: c.Properties,
63+
}
64+
65+
cde := sets.NewString()
66+
for _, entry := range c.Entries {
67+
if _, ok := mch.Bundles[entry.Name]; ok {
68+
return nil, fmt.Errorf("invalid package %q, channel %q: duplicate entry %q", c.Package, c.Name, entry.Name)
69+
}
70+
cde = cde.Insert(entry.Name)
71+
mch.Bundles[entry.Name] = &model.Bundle{
72+
Package: mpkg,
73+
Channel: mch,
74+
Name: entry.Name,
75+
Replaces: entry.Replaces,
76+
Skips: entry.Skips,
77+
SkipRange: entry.SkipRange,
78+
}
79+
}
80+
channelDefinedEntries[c.Package] = cde
81+
82+
mpkg.Channels[c.Name] = mch
83+
84+
defaultChannelName := defaultChannels[c.Package]
85+
if defaultChannelName == c.Name {
86+
mpkg.DefaultChannel = mch
87+
}
88+
}
89+
90+
// packageBundles tracks the set of bundle names for each package
91+
// and is used to detect duplicate bundles.
92+
packageBundles := map[string]sets.String{}
93+
94+
for _, b := range cfg.Bundles {
95+
if b.Package == "" {
96+
return nil, fmt.Errorf("package name must be set for bundle %q", b.Name)
97+
}
98+
mpkg, ok := mpkgs[b.Package]
99+
if !ok {
100+
return nil, fmt.Errorf("unknown package %q for bundle %q", b.Package, b.Name)
101+
}
102+
103+
bundles, ok := packageBundles[b.Package]
104+
if !ok {
105+
bundles = sets.NewString()
106+
}
107+
if bundles.Has(b.Name) {
108+
return nil, fmt.Errorf("package %q has duplicate bundle %q", b.Package, b.Name)
109+
}
110+
bundles.Insert(b.Name)
111+
packageBundles[b.Package] = bundles
112+
113+
props, err := property.Parse(b.Properties)
114+
if err != nil {
115+
return nil, fmt.Errorf("parse properties for bundle %q: %v", b.Name, err)
116+
}
117+
118+
if len(props.Packages) != 1 {
119+
return nil, fmt.Errorf("package %q bundle %q must have exactly 1 %q property, found %d", b.Package, b.Name, property.TypePackage, len(props.Packages))
120+
}
121+
122+
if b.Package != props.Packages[0].PackageName {
123+
return nil, fmt.Errorf("package %q does not match %q property %q", b.Package, property.TypePackage, props.Packages[0].PackageName)
124+
}
125+
126+
// Parse version from the package property.
127+
rawVersion := props.Packages[0].Version
128+
ver, err := semver.Parse(rawVersion)
129+
if err != nil {
130+
return nil, fmt.Errorf("error parsing bundle %q version %q: %v", b.Name, rawVersion, err)
131+
}
132+
133+
channelDefinedEntries[b.Package] = channelDefinedEntries[b.Package].Delete(b.Name)
134+
found := false
135+
for _, mch := range mpkg.Channels {
136+
if mb, ok := mch.Bundles[b.Name]; ok {
137+
found = true
138+
mb.Image = b.Image
139+
mb.Properties = b.Properties
140+
mb.RelatedImages = relatedImagesToModelRelatedImages(b.RelatedImages)
141+
mb.CsvJSON = b.CsvJSON
142+
mb.Objects = b.Objects
143+
mb.PropertiesP = props
144+
mb.Version = ver
145+
}
146+
}
147+
if !found {
148+
return nil, fmt.Errorf("package %q, bundle %q not found in any channel entries", b.Package, b.Name)
149+
}
150+
}
151+
152+
for pkg, entries := range channelDefinedEntries {
153+
if entries.Len() > 0 {
154+
return nil, fmt.Errorf("no olm.bundle blobs found in package %q for olm.channel entries %s", pkg, entries.List())
155+
}
156+
}
157+
158+
for _, mpkg := range mpkgs {
159+
defaultChannelName := defaultChannels[mpkg.Name]
160+
if defaultChannelName != "" && mpkg.DefaultChannel == nil {
161+
dch := &model.Channel{
162+
Package: mpkg,
163+
Name: defaultChannelName,
164+
Bundles: map[string]*model.Bundle{},
165+
}
166+
mpkg.DefaultChannel = dch
167+
mpkg.Channels[dch.Name] = dch
168+
}
169+
}
170+
171+
if err := mpkgs.Validate(); err != nil {
172+
return nil, err
173+
}
174+
mpkgs.Normalize()
175+
return mpkgs, nil
176+
}
177+
178+
func relatedImagesToModelRelatedImages(in []RelatedImage) []model.RelatedImage {
179+
var out []model.RelatedImage
180+
for _, p := range in {
181+
out = append(out, model.RelatedImage{
182+
Name: p.Name,
183+
Image: p.Image,
184+
})
185+
}
186+
return out
187+
}

0 commit comments

Comments
 (0)