Skip to content

Commit

Permalink
poc: new schemas
Browse files Browse the repository at this point in the history
Signed-off-by: Joe Lanford <[email protected]>
  • Loading branch information
joelanford committed Sep 28, 2024
1 parent 1adde98 commit cc649f0
Show file tree
Hide file tree
Showing 10 changed files with 448 additions and 3 deletions.
178 changes: 178 additions & 0 deletions alpha/action/migrations/001_v2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
package migrations

import (
"encoding/json"
"fmt"
"slices"
"strings"

"github.com/Masterminds/semver/v3"
"k8s.io/apimachinery/pkg/util/sets"

"github.com/operator-framework/operator-registry/alpha/declcfg"
fbcv2 "github.com/operator-framework/operator-registry/alpha/fbc/v2"
"github.com/operator-framework/operator-registry/alpha/property"
)

func v2(cfg *declcfg.DeclarativeConfig) error {
slices.DeleteFunc(cfg.Packages, func(p declcfg.Package) bool {
pkgV2 := fbcv2.Package{
Schema: "olm.package.v2",
Package: p.Name,
ShortDescription: ellipsesDescription(p.Description),
LongDescription: p.Description,
}
cfg.PackagesV2 = append(cfg.PackagesV2, pkgV2)

if p.Icon != nil {
iconV2 := fbcv2.Icon{
Schema: "olm.icon.v2",
Package: p.Name,
MediaType: p.Icon.MediaType,
Data: p.Icon.Data,
}
cfg.IconsV2 = append(cfg.IconsV2, iconV2)
}
return true
})

nameToVersion := map[string]semver.Version{}
var errs []error
slices.DeleteFunc(cfg.Bundles, func(b declcfg.Bundle) bool {
var (
vers *semver.Version
err error

properties = map[string]json.RawMessage{}
constraints = map[string]json.RawMessage{}

rawListProperties = map[string][]json.RawMessage{}
rawListConstraints = map[string][]json.RawMessage{}
)

for _, p := range b.Properties {
switch p.Type {
case property.TypePackage:
var pkgProp property.Package
if err := json.Unmarshal(p.Value, &pkgProp); err != nil {
errs = append(errs, fmt.Errorf("could not migrate bundle %q: %v", b.Name, err))
return true
}
vers, err = semver.NewVersion(pkgProp.Version)
if err != nil {
errs = append(errs, fmt.Errorf("could not migrate bundle %q: %v", b.Name, err))
return true
}
case property.TypeCSVMetadata:
properties[p.Type] = p.Value
case property.TypeGVK, property.TypeBundleObject:
rawListProperties[p.Type] = append(rawListProperties[p.Type], p.Value)
case property.TypeGVKRequired, property.TypePackageRequired, property.TypeConstraint:
rawListConstraints[p.Type] = append(rawListConstraints[p.Type], p.Value)
default:
errs = append(errs, fmt.Errorf("could not migrate bundle %q: unknown property type %q cannot be translated to v2 properties format", b.Name, p.Type))
return true
}
}
for k, v := range rawListProperties {
properties[k], _ = json.Marshal(v)
}

for k, v := range rawListConstraints {
constraints[k], _ = json.Marshal(v)
}

nameToVersion[b.Name] = *vers

relatedURIs := sets.New[string]()
for _, r := range b.RelatedImages {
relatedURIs.Insert(fmt.Sprintf("docker://%s", r.Image))
}
bundleV2 := fbcv2.Bundle{
Schema: "olm.bundle.v2",
Package: b.Package,
Name: fmt.Sprintf("%s-%s-%d", b.Package, vers, 1),
Version: *vers,
Release: 1,
URI: fmt.Sprintf("docker://%s", b.Image),
RelatedURIs: sets.List(relatedURIs),
Properties: properties,
Constraints: constraints,
}
cfg.BundlesV2 = append(cfg.BundlesV2, bundleV2)
return true
})
if len(errs) > 0 {
return fmt.Errorf("error migrating bundles: %v", errs)
}

slices.DeleteFunc(cfg.Channels, func(c declcfg.Channel) bool {
entries := []fbcv2.ChannelEntry{}
for _, e := range c.Entries {
var upgradesFromBuilder strings.Builder
if e.Replaces != "" {
upgradesFromBuilder.WriteString(fmt.Sprintf("%s", nameToVersion[e.Replaces]))
}
for _, s := range e.Skips {
upgradesFromBuilder.WriteString(fmt.Sprintf(" %s", nameToVersion[s]))
}
if e.SkipRange != "" {
upgradesFromBuilder.WriteString(fmt.Sprintf(" %s", e.SkipRange))
}
upgradesFromStr := upgradesFromBuilder.String()

// If the original channel entry has no upgrade edges, make
// sure to explicitly configure it to an impossible range
// equivalent to "no upgrades".
if upgradesFromStr == "" {
upgradesFromStr = fmt.Sprintf("<0.0.0 >0.0.0")
}
upgradesFrom, err := semver.NewConstraint(upgradesFromStr)
if err != nil {
errs = append(errs, fmt.Errorf("count not migrate channel %q: %v", c.Name, err))
return true
}

entries = append(entries, fbcv2.ChannelEntry{
Version: nameToVersion[e.Name],
UpgradesFrom: *upgradesFrom,
})
}

rawListProperties := map[string][]json.RawMessage{}
for _, p := range c.Properties {
rawListProperties[p.Type] = append(rawListProperties[p.Type], p.Value)
}

properties := map[string]json.RawMessage{}
for k, v := range rawListProperties {
properties[k], _ = json.Marshal(v)
}

channelV2 := fbcv2.Channel{
Schema: "olm.channel.v2",
Package: c.Package,
Name: c.Name,
Entries: entries,
Properties: properties,
}
cfg.ChannelsV2 = append(cfg.ChannelsV2, channelV2)
return true
})
if len(errs) > 0 {
return fmt.Errorf("error migrating channels: %v", errs)
}

return nil
}

func ellipsesDescription(s string) string {
if len(s) <= 60 {
return s
}
lastSpace := strings.LastIndex(s[:57], " ")
if lastSpace == -1 {
lastSpace = 57
}
return s[:lastSpace] + "..."
}
1 change: 1 addition & 0 deletions alpha/action/migrations/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ type Migrations struct {
var allMigrations = []Migration{
newMigration(NoMigrations, "do nothing", func(_ *declcfg.DeclarativeConfig) error { return nil }),
newMigration("bundle-object-to-csv-metadata", `migrates bundles' "olm.bundle.object" to "olm.csv.metadata"`, bundleObjectToCSVMetadata),
newMigration("v2", "migrates olm.package, olm.channel, and olm.bundle to their equivalent v2 APIs", v2),
}

func NewMigrations(name string) (*Migrations, error) {
Expand Down
13 changes: 11 additions & 2 deletions alpha/declcfg/declcfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import (
"errors"
"fmt"

prettyunmarshaler "github.com/operator-framework/operator-registry/pkg/prettyunmarshaler"

"golang.org/x/text/cases"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/sets"

fbcv2 "github.com/operator-framework/operator-registry/alpha/fbc/v2"
"github.com/operator-framework/operator-registry/alpha/property"
prettyunmarshaler "github.com/operator-framework/operator-registry/pkg/prettyunmarshaler"
)

const (
Expand All @@ -28,6 +28,11 @@ type DeclarativeConfig struct {
Bundles []Bundle
Deprecations []Deprecation
Others []Meta

PackagesV2 []fbcv2.Package
ChannelsV2 []fbcv2.Channel
BundlesV2 []fbcv2.Bundle
IconsV2 []fbcv2.Icon
}

type Package struct {
Expand Down Expand Up @@ -201,6 +206,10 @@ func extractUniqueMetaKeys(blobMap map[string]any, m *Meta) error {
}

func (destination *DeclarativeConfig) Merge(src *DeclarativeConfig) {
destination.PackagesV2 = append(destination.PackagesV2, src.PackagesV2...)
destination.ChannelsV2 = append(destination.ChannelsV2, src.ChannelsV2...)
destination.BundlesV2 = append(destination.BundlesV2, src.BundlesV2...)

destination.Packages = append(destination.Packages, src.Packages...)
destination.Channels = append(destination.Channels, src.Channels...)
destination.Bundles = append(destination.Bundles, src.Bundles...)
Expand Down
79 changes: 78 additions & 1 deletion alpha/declcfg/write.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"k8s.io/apimachinery/pkg/util/sets"
"sigs.k8s.io/yaml"

fbcv2 "github.com/operator-framework/operator-registry/alpha/fbc/v2"
"github.com/operator-framework/operator-registry/alpha/property"
)

Expand Down Expand Up @@ -395,8 +396,27 @@ type encoder interface {
}

func writeToEncoder(cfg DeclarativeConfig, enc encoder) error {
pkgNames := sets.NewString()
pkgV2Names := sets.NewString()
packagesV2ByName := map[string][]fbcv2.Package{}
for _, p := range cfg.PackagesV2 {
pkgName := p.Package
pkgV2Names.Insert(pkgName)
packagesV2ByName[pkgName] = append(packagesV2ByName[pkgName], p)
}
channelsV2ByPackage := map[string][]fbcv2.Channel{}
for _, c := range cfg.ChannelsV2 {
pkgName := c.Package
pkgV2Names.Insert(pkgName)
channelsV2ByPackage[pkgName] = append(channelsV2ByPackage[pkgName], c)
}
bundlesV2ByPackage := map[string][]fbcv2.Bundle{}
for _, b := range cfg.BundlesV2 {
pkgName := b.Package
pkgV2Names.Insert(pkgName)
bundlesV2ByPackage[pkgName] = append(bundlesV2ByPackage[pkgName], b)
}

pkgNames := sets.NewString()
packagesByName := map[string][]Package{}
for _, p := range cfg.Packages {
pkgName := p.Name
Expand Down Expand Up @@ -428,6 +448,38 @@ func writeToEncoder(cfg DeclarativeConfig, enc encoder) error {
deprecationsByPackage[pkgName] = append(deprecationsByPackage[pkgName], d)
}

for _, pName := range pkgV2Names.List() {
if len(pName) == 0 {
continue
}
pkgs := packagesV2ByName[pName]
for _, p := range pkgs {
if err := enc.Encode(p); err != nil {
return err
}
}

channels := channelsV2ByPackage[pName]
sort.Slice(channels, func(i, j int) bool {
return channels[i].Name < channels[j].Name
})
for _, c := range channels {
if err := enc.Encode(c); err != nil {
return err
}
}

bundles := bundlesV2ByPackage[pName]
sort.Slice(bundles, func(i, j int) bool {
return bundles[i].Name < bundles[j].Name
})
for _, b := range bundles {
if err := enc.Encode(b); err != nil {
return err
}
}
}

for _, pName := range pkgNames.List() {
if len(pName) == 0 {
continue
Expand Down Expand Up @@ -499,6 +551,15 @@ func writeToEncoder(cfg DeclarativeConfig, enc encoder) error {
type WriteFunc func(config DeclarativeConfig, w io.Writer) error

func WriteFS(cfg DeclarativeConfig, rootDir string, writeFunc WriteFunc, fileExt string) error {
channelsV2ByPackage := map[string][]fbcv2.Channel{}
for _, c := range cfg.ChannelsV2 {
channelsV2ByPackage[c.Package] = append(channelsV2ByPackage[c.Package], c)
}
bundlesV2ByPackage := map[string][]fbcv2.Bundle{}
for _, b := range cfg.BundlesV2 {
bundlesV2ByPackage[b.Package] = append(bundlesV2ByPackage[b.Package], b)
}

channelsByPackage := map[string][]Channel{}
for _, c := range cfg.Channels {
channelsByPackage[c.Package] = append(channelsByPackage[c.Package], c)
Expand All @@ -512,6 +573,22 @@ func WriteFS(cfg DeclarativeConfig, rootDir string, writeFunc WriteFunc, fileExt
return err
}

for _, p := range cfg.PackagesV2 {
fcfg := DeclarativeConfig{
PackagesV2: []fbcv2.Package{p},
ChannelsV2: channelsV2ByPackage[p.Package],
BundlesV2: bundlesV2ByPackage[p.Package],
}
pkgDir := filepath.Join(rootDir, p.Package)
if err := os.MkdirAll(pkgDir, 0777); err != nil {
return err
}
filename := filepath.Join(pkgDir, fmt.Sprintf("catalog.v2%s", fileExt))
if err := writeFile(fcfg, filename, writeFunc); err != nil {
return err
}
}

for _, p := range cfg.Packages {
fcfg := DeclarativeConfig{
Packages: []Package{p},
Expand Down
51 changes: 51 additions & 0 deletions alpha/fbc/v2/bundle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package v2

import (
"encoding/json"

"github.com/Masterminds/semver/v3"
)

type Bundle struct {
// Schema is the FBC schema version of the bundle. Always "olm.bundle.v2".
Schema string `json:"schema"`

// Package is the name of the package to which this bundle belongs.
Package string `json:"package"`

// Name is the name of the bundle. It is required to be in the format of
// "package-version-release", and mnust be unique within a catalog.
Name string `json:"name"`

// Version is the version of the software packaged in this bundle.
Version semver.Version `json:"version"`

// Release is the number of times the Version of this bundle has been
// released.
Release uint32 `json:"release"`

// URI is the location of the bundle.
URI string `json:"uri"`

// RelatedURIs is a list of related URIs that are associated with the
// bundle. URIs should be included here if they are referenced or used by
// the bundle.
RelatedURIs []string `json:"relatedURIs,omitempty"`

// Annotations is a map of string keys to string values. Annotations are
// used to store simple arbitrary metadata about the bundle.
Annotations map[string]string `json:"annotations,omitempty"`

// Properties is a map of string keys to arbitrary JSON-encoded values.
// Properties are used to store complex metadata about the bundle. A
// property's "type" key is used to determine how to interpret the
// JSON-encoded value. Unrecognized properties MUST be ignored.
Properties map[string]json.RawMessage `json:"properties,omitempty"`

// Constraints is a map of string keys to arbitrary JSON-encoded values.
// Constraints are used to store complex constraints that the bundle
// requires. A constraint's "type" key is used to determine how to
// interpret the JSON-encoded value. Unrecognized constraints MUST be
// treated as unsatisfiable.
Constraints map[string]json.RawMessage `json:"constraints,omitempty"`
}
Loading

0 comments on commit cc649f0

Please sign in to comment.