Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

grpc changes, model conversion, validation #1161

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 11 additions & 8 deletions alpha/declcfg/declcfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const (
SchemaPackage = "olm.package"
SchemaChannel = "olm.channel"
SchemaBundle = "olm.bundle"
SchemaDeprecation = "olm.catalog.deprecation"
SchemaDeprecation = "olm.deprecations"
)

type DeclarativeConfig struct {
Expand Down Expand Up @@ -93,16 +93,19 @@ type RelatedImage struct {
}

type Deprecation struct {
Schema string `json:"schema"`
Package string `json:"package"`
Name string `json:"name,omitempty"`
Deprecations []DeprecationEntry `json:"deprecations"`
Schema string `json:"schema"`
Package string `json:"package"`
Entries []DeprecationEntry `json:"entries"`
}

type DeprecationEntry struct {
Schema string `json:"schema"`
Name string `json:"name,omitempty"`
Message json.RawMessage `json:"message"`
Reference PackageScopedReference `json:"reference"`
grokspawn marked this conversation as resolved.
Show resolved Hide resolved
Message string `json:"message"`
}

type PackageScopedReference struct {
Schema string `json:"schema"`
Name string `json:"name,omitempty"`
}

type Meta struct {
Expand Down
75 changes: 70 additions & 5 deletions alpha/declcfg/declcfg_to_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func ConvertToModel(cfg DeclarativeConfig) (model.Model, error) {
mpkgs[p.Name] = mpkg
}

channelDefinedEntries := map[string]sets.String{}
channelDefinedEntries := map[string]sets.Set[string]{}
for _, c := range cfg.Channels {
mpkg, ok := mpkgs[c.Package]
if !ok {
Expand All @@ -62,7 +62,7 @@ func ConvertToModel(cfg DeclarativeConfig) (model.Model, error) {
Properties: c.Properties,
}

cde := sets.NewString()
cde := sets.Set[string]{}
for _, entry := range c.Entries {
if _, ok := mch.Bundles[entry.Name]; ok {
return nil, fmt.Errorf("invalid package %q, channel %q: duplicate entry %q", c.Package, c.Name, entry.Name)
Expand All @@ -89,7 +89,7 @@ func ConvertToModel(cfg DeclarativeConfig) (model.Model, error) {

// packageBundles tracks the set of bundle names for each package
// and is used to detect duplicate bundles.
packageBundles := map[string]sets.String{}
packageBundles := map[string]sets.Set[string]{}

for _, b := range cfg.Bundles {
if b.Package == "" {
Expand All @@ -102,7 +102,7 @@ func ConvertToModel(cfg DeclarativeConfig) (model.Model, error) {

bundles, ok := packageBundles[b.Package]
if !ok {
bundles = sets.NewString()
bundles = sets.Set[string]{}
}
if bundles.Has(b.Name) {
return nil, fmt.Errorf("package %q has duplicate bundle %q", b.Package, b.Name)
Expand Down Expand Up @@ -151,7 +151,7 @@ func ConvertToModel(cfg DeclarativeConfig) (model.Model, error) {

for pkg, entries := range channelDefinedEntries {
if entries.Len() > 0 {
return nil, fmt.Errorf("no olm.bundle blobs found in package %q for olm.channel entries %s", pkg, entries.List())
return nil, fmt.Errorf("no olm.bundle blobs found in package %q for olm.channel entries %s", pkg, sets.List[string](entries))
}
}

Expand All @@ -168,6 +168,71 @@ func ConvertToModel(cfg DeclarativeConfig) (model.Model, error) {
}
}

// deprecationsByPackage tracks the set of package names
// and is used to detect duplicate packages.
deprecationsByPackage := sets.New[string]()

for i, deprecation := range cfg.Deprecations {

// no need to validate schema, since it could not be unmarshaled if missing/invalid

if deprecation.Package == "" {
return nil, fmt.Errorf("package name must be set for deprecation item %v", i)
}

// must refer to package in this catalog
mpkg, ok := mpkgs[deprecation.Package]
if !ok {
return nil, fmt.Errorf("cannot apply deprecations to an unknown package %q", deprecation.Package)
}

// must be unique per package
if deprecationsByPackage.Has(deprecation.Package) {
return nil, fmt.Errorf("expected a maximum of one deprecation per package: %q", deprecation.Package)
}
deprecationsByPackage.Insert(deprecation.Package)

references := sets.New[PackageScopedReference]()
everettraven marked this conversation as resolved.
Show resolved Hide resolved

for j, entry := range deprecation.Entries {
if entry.Reference.Schema == "" {
return nil, fmt.Errorf("schema must be set for deprecation entry [%v] for package %q", deprecation.Package, j)
}

if references.Has(entry.Reference) {
return nil, fmt.Errorf("duplicate deprecation entry %#v for package %q", entry.Reference, deprecation.Package)
}
references.Insert(entry.Reference)

switch entry.Reference.Schema {
case SchemaBundle:
if !packageBundles[deprecation.Package].Has(entry.Reference.Name) {
return nil, fmt.Errorf("cannot deprecate bundle %q for package %q: bundle not found", entry.Reference.Name, deprecation.Package)
}
for _, mch := range mpkg.Channels {
if mb, ok := mch.Bundles[entry.Reference.Name]; ok {
mb.Deprecation = &model.Deprecation{Message: entry.Message}
}
}
case SchemaChannel:
ch, ok := mpkg.Channels[entry.Reference.Name]
if !ok {
return nil, fmt.Errorf("cannot deprecate channel %q for package %q: channel not found", entry.Reference.Name, deprecation.Package)
}
ch.Deprecation = &model.Deprecation{Message: entry.Message}

case SchemaPackage:
if entry.Reference.Name != "" {
return nil, fmt.Errorf("package name must be empty for deprecated package %q (specified %q)", deprecation.Package, entry.Reference.Name)
}
mpkg.Deprecation = &model.Deprecation{Message: entry.Message}

default:
return nil, fmt.Errorf("cannot deprecate object %#v referenced by entry %v for package %q: object schema unknown", entry.Reference, j, deprecation.Package)
}
}
}

if err := mpkgs.Validate(); err != nil {
return nil, err
}
Expand Down
32 changes: 16 additions & 16 deletions alpha/declcfg/load_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -338,13 +338,12 @@ func TestLoadFS(t *testing.T) {
},
Deprecations: []Deprecation{
{
Schema: "olm.catalog.deprecation",
Schema: SchemaDeprecation,
Package: "kiali",
Name: "bobs-discount-name",
Deprecations: []DeprecationEntry{
{Schema: "olm.bundle", Name: "kiali-operator.v1.68.0", Message: json.RawMessage(`"kiali-operator.v1.68.0 is deprecated. Uninstall and install kiali-operator.v1.72.0 for support.\n"`)},
{Schema: "olm.package", Name: "kiali", Message: json.RawMessage(`"package kiali is end of life. Please use 'kiali-new' package for support.\n"`)},
{Schema: "olm.channel", Name: "alpha", Message: json.RawMessage(`"channel alpha is no longer supported. Please switch to channel 'stable'.\n"`)},
Entries: []DeprecationEntry{
{Reference: PackageScopedReference{Schema: SchemaBundle, Name: "kiali-operator.v1.68.0"}, Message: "kiali-operator.v1.68.0 is deprecated. Uninstall and install kiali-operator.v1.72.0 for support.\n"},
{Reference: PackageScopedReference{Schema: SchemaPackage}, Message: "package kiali is end of life. Please use 'kiali-new' package for support.\n"},
{Reference: PackageScopedReference{Schema: SchemaChannel, Name: "alpha"}, Message: "channel alpha is no longer supported. Please switch to channel 'stable'.\n"},
},
},
},
Expand Down Expand Up @@ -899,22 +898,23 @@ present in the .indexignore file.`),
}
deprecations = &fstest.MapFile{
Data: []byte(`---
schema: olm.catalog.deprecation
schema: olm.deprecations
package: kiali
name: bobs-discount-name
deprecations:
- schema: olm.bundle
name: kiali-operator.v1.68.0
entries:
- reference:
schema: olm.bundle
name: kiali-operator.v1.68.0
message: |
kiali-operator.v1.68.0 is deprecated. Uninstall and install kiali-operator.v1.72.0 for support.
- schema: olm.package
name: kiali
- reference:
schema: olm.package
message: |
package kiali is end of life. Please use 'kiali-new' package for support.
- schema: olm.channel
name: alpha
- reference:
schema: olm.channel
name: alpha
message: |
channel alpha is no longer supported. Please switch to channel 'stable'.`),
channel alpha is no longer supported. Please switch to channel 'stable'.`),
}

validFS = fstest.MapFS{
Expand Down
13 changes: 10 additions & 3 deletions alpha/declcfg/write.go
Original file line number Diff line number Diff line change
Expand Up @@ -425,10 +425,17 @@ func writeToEncoder(cfg DeclarativeConfig, enc encoder) error {
}
}

//
// Normally we would order the deprecations, but it really doesn't make sense since
// - there will be 0 or 1 of them for any given package
// - they have no other useful field for ordering
//
// validation is typically via conversion to a model.Model and invoking model.Package.Validate()
// It's possible that a user of the object could create a slice containing more then 1
// Deprecation object for a package, and it would bypass validation if this
// function gets called without conversion.
//
deprecations := deprecationsByPackage[pName]
sort.SliceStable(deprecations, func(i, j int) bool {
return deprecations[i].Name < deprecations[j].Name
})
for _, d := range deprecations {
if err := enc.Encode(d); err != nil {
return err
Expand Down
41 changes: 36 additions & 5 deletions alpha/model/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ import (
"github.com/operator-framework/operator-registry/alpha/property"
)

type Deprecation struct {
Message string `json:"message"`
}

func init() {
t := types.NewType("svg", "image/svg+xml")
filetype.AddMatcher(t, svg.Is)
Expand Down Expand Up @@ -44,6 +48,7 @@ type Package struct {
Icon *Icon
DefaultChannel *Channel
Channels map[string]*Channel
Deprecation *Deprecation
}

func (m *Package) Validate() error {
Expand Down Expand Up @@ -84,12 +89,17 @@ func (m *Package) Validate() error {
if m.DefaultChannel != nil && !foundDefault {
result.subErrors = append(result.subErrors, fmt.Errorf("default channel %q not found in channels list", m.DefaultChannel.Name))
}

if err := m.Deprecation.Validate(); err != nil {
result.subErrors = append(result.subErrors, fmt.Errorf("invalid deprecation: %v", err))
}

return result.orNil()
}

type Icon struct {
Data []byte
MediaType string
Data []byte `json:"base64data"`
MediaType string `json:"mediatype"`
}

func (i *Icon) Validate() error {
Expand Down Expand Up @@ -131,9 +141,10 @@ func (i *Icon) validateData() error {
}

type Channel struct {
Package *Package
Name string
Bundles map[string]*Bundle
Package *Package
Name string
Bundles map[string]*Bundle
Deprecation *Deprecation
// NOTICE: The field Properties of the type Channel is for internal use only.
// DO NOT use it for any public-facing functionalities.
// This API is in alpha stage and it is subject to change.
Expand Down Expand Up @@ -206,6 +217,11 @@ func (c *Channel) Validate() error {
result.subErrors = append(result.subErrors, fmt.Errorf("bundle %q not correctly linked to parent channel", b.Name))
}
}

if err := c.Deprecation.Validate(); err != nil {
result.subErrors = append(result.subErrors, fmt.Errorf("invalid deprecation: %v", err))
}

return result.orNil()
}

Expand Down Expand Up @@ -264,6 +280,7 @@ type Bundle struct {
SkipRange string
Properties []property.Property
RelatedImages []RelatedImage
Deprecation *Deprecation

// These fields are present so that we can continue serving
// the GRPC API the way packageserver expects us to in a
Expand Down Expand Up @@ -318,6 +335,10 @@ func (b *Bundle) Validate() error {
result.subErrors = append(result.subErrors, errors.New("bundle image must be set"))
}

if err := b.Deprecation.Validate(); err != nil {
result.subErrors = append(result.subErrors, fmt.Errorf("invalid deprecation: %v", err))
}

return result.orNil()
}

Expand Down Expand Up @@ -374,3 +395,13 @@ func (m Model) AddBundle(b Bundle) {
p.DefaultChannel = b.Channel
}
}

func (d *Deprecation) Validate() error {
if d == nil {
return nil
}
if d.Message == "" {
return errors.New("message must be set")
}
return nil
}
4 changes: 2 additions & 2 deletions pkg/api/grpc_health_v1/health.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 8 additions & 4 deletions pkg/api/grpc_health_v1/health_grpc.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions pkg/api/model_to_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ func ConvertModelBundleToAPIBundle(b model.Bundle) (*Bundle, error) {
}
}

var deprecation *Deprecation
if b.Deprecation != nil {
deprecation = &Deprecation{
Message: b.Deprecation.Message,
}
}

apiDeps, err := convertModelPropertiesToAPIDependencies(b.Properties)
if err != nil {
return nil, fmt.Errorf("convert model properties to api dependencies: %v", err)
Expand All @@ -71,6 +78,7 @@ func ConvertModelBundleToAPIBundle(b model.Bundle) (*Bundle, error) {
Skips: b.Skips,
CsvJson: csvJson,
Object: b.Objects,
Deprecation: deprecation,
}, nil
}

Expand Down