-
Notifications
You must be signed in to change notification settings - Fork 48
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
At the moment it contains: * Types which embed `declcfg.Package`, `declcfg.Channel` and `declcfg.Bundle`. Later we will be adding more methods to them (for unmarshalling properties, for example) * A function to fetch metadata by scheme & catalog name * `Filter` function which can filter all these 3 types using a given predicate * `And`, `Or` and `Not` composite predicates to be used with `Filter` Later we might expand/change this package to be more convinient in use with variable sources. Signed-off-by: Mikalai Radchuk <mradchuk@redhat.com>
- Loading branch information
Showing
5 changed files
with
276 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package catalogmetadata | ||
|
||
// Predicate returns true if the object should be kept when filtering | ||
type Predicate[T Schemas] func(entity *T) bool | ||
|
||
// Filter filters a slice accordingly to | ||
func Filter[T Schemas](in []*T, test Predicate[T]) []*T { | ||
out := []*T{} | ||
for i := range in { | ||
if test(in[i]) { | ||
out = append(out, in[i]) | ||
} | ||
} | ||
return out | ||
} | ||
|
||
func And[T Schemas](predicates ...Predicate[T]) Predicate[T] { | ||
return func(obj *T) bool { | ||
eval := true | ||
for _, predicate := range predicates { | ||
eval = eval && predicate(obj) | ||
if !eval { | ||
return false | ||
} | ||
} | ||
return eval | ||
} | ||
} | ||
|
||
func Or[T Schemas](predicates ...Predicate[T]) Predicate[T] { | ||
return func(obj *T) bool { | ||
eval := false | ||
for _, predicate := range predicates { | ||
eval = eval || predicate(obj) | ||
if eval { | ||
return true | ||
} | ||
} | ||
return eval | ||
} | ||
} | ||
|
||
func Not[T Schemas](predicate Predicate[T]) Predicate[T] { | ||
return func(obj *T) bool { | ||
return !predicate(obj) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package catalogmetadata_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/operator-framework/operator-registry/alpha/declcfg" | ||
"github.com/stretchr/testify/assert" | ||
|
||
"github.com/operator-framework/operator-controller/internal/catalogmetadata" | ||
) | ||
|
||
func TestFilter(t *testing.T) { | ||
in := []*catalogmetadata.Bundle{ | ||
{Bundle: declcfg.Bundle{Name: "operator1.v1", Package: "operator1", Image: "fake1"}}, | ||
{Bundle: declcfg.Bundle{Name: "operator1.v2", Package: "operator1", Image: "fake2"}}, | ||
{Bundle: declcfg.Bundle{Name: "operator2.v1", Package: "operator2", Image: "fake1"}}, | ||
} | ||
|
||
for _, tt := range []struct { | ||
name string | ||
predicate catalogmetadata.Predicate[catalogmetadata.Bundle] | ||
want []*catalogmetadata.Bundle | ||
}{ | ||
{ | ||
name: "simple filter with one predicate", | ||
predicate: func(bundle *catalogmetadata.Bundle) bool { | ||
return bundle.Name == "operator1.v1" | ||
}, | ||
want: []*catalogmetadata.Bundle{ | ||
{declcfg.Bundle{Name: "operator1.v1", Package: "operator1", Image: "fake1"}}, | ||
}, | ||
}, | ||
{ | ||
name: "filter with Not predicate", | ||
predicate: catalogmetadata.Not(func(bundle *catalogmetadata.Bundle) bool { | ||
return bundle.Name == "operator1.v1" | ||
}), | ||
want: []*catalogmetadata.Bundle{ | ||
{declcfg.Bundle{Name: "operator1.v2", Package: "operator1", Image: "fake2"}}, | ||
{declcfg.Bundle{Name: "operator2.v1", Package: "operator2", Image: "fake1"}}, | ||
}, | ||
}, | ||
{ | ||
name: "filter with And predicate", | ||
predicate: catalogmetadata.And( | ||
func(bundle *catalogmetadata.Bundle) bool { | ||
return bundle.Name == "operator1.v1" | ||
}, | ||
func(bundle *catalogmetadata.Bundle) bool { | ||
return bundle.Image == "fake1" | ||
}, | ||
), | ||
want: []*catalogmetadata.Bundle{ | ||
{declcfg.Bundle{Name: "operator1.v1", Package: "operator1", Image: "fake1"}}, | ||
}, | ||
}, | ||
{ | ||
name: "filter with Or predicate", | ||
predicate: catalogmetadata.Or( | ||
func(bundle *catalogmetadata.Bundle) bool { | ||
return bundle.Name == "operator1.v1" | ||
}, | ||
func(bundle *catalogmetadata.Bundle) bool { | ||
return bundle.Image == "fake1" | ||
}, | ||
), | ||
want: []*catalogmetadata.Bundle{ | ||
{declcfg.Bundle{Name: "operator1.v1", Package: "operator1", Image: "fake1"}}, | ||
{declcfg.Bundle{Name: "operator2.v1", Package: "operator2", Image: "fake1"}}, | ||
}, | ||
}, | ||
} { | ||
t.Run(tt.name, func(t *testing.T) { | ||
actual := catalogmetadata.Filter(in, tt.predicate) | ||
assert.Equal(t, tt.want, actual) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package catalogmetadata | ||
|
||
import ( | ||
"github.com/operator-framework/operator-registry/alpha/declcfg" | ||
) | ||
|
||
type Schemas interface { | ||
Package | Bundle | Channel | ||
} | ||
|
||
type Package struct { | ||
declcfg.Package | ||
} | ||
|
||
type Channel struct { | ||
declcfg.Channel | ||
} | ||
|
||
type Bundle struct { | ||
declcfg.Bundle | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package catalogmetadata | ||
|
||
import ( | ||
"encoding/json" | ||
|
||
catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" | ||
) | ||
|
||
func Unmarshal[T Schemas](cm []catalogd.CatalogMetadata) ([]*T, error) { | ||
contents := make([]*T, 0, len(cm)) | ||
for _, cm := range cm { | ||
var content T | ||
if err := json.Unmarshal(cm.Spec.Content, &content); err != nil { | ||
return nil, err | ||
} | ||
contents = append(contents, &content) | ||
} | ||
|
||
return contents, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
package catalogmetadata_test | ||
|
||
import ( | ||
"encoding/json" | ||
"testing" | ||
|
||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
utilruntime "k8s.io/apimachinery/pkg/util/runtime" | ||
|
||
catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" | ||
"github.com/operator-framework/operator-registry/alpha/declcfg" | ||
"github.com/operator-framework/operator-registry/alpha/property" | ||
"github.com/stretchr/testify/assert" | ||
|
||
"github.com/operator-framework/operator-controller/internal/catalogmetadata" | ||
) | ||
|
||
var ( | ||
scheme *runtime.Scheme | ||
) | ||
|
||
func init() { | ||
scheme = runtime.NewScheme() | ||
utilruntime.Must(catalogd.AddToScheme(scheme)) | ||
} | ||
|
||
func TestFetchByScheme(t *testing.T) { | ||
fakeCatalogName := "fake-catalog" | ||
|
||
validBundle := `{ | ||
"schema": "olm.bundle", | ||
"name": "fake1.v1.0.0", | ||
"package": "fake1", | ||
"image": "fake-image", | ||
"properties": [ | ||
{ | ||
"type": "olm.package", | ||
"value": {"packageName":"fake1","version":"1.0.0"} | ||
} | ||
] | ||
}` | ||
|
||
for _, tt := range []struct { | ||
name string | ||
objs []catalogd.CatalogMetadata | ||
wantData []*catalogmetadata.Bundle | ||
wantErr string | ||
}{ | ||
{ | ||
name: "valid objects", | ||
objs: []catalogd.CatalogMetadata{ | ||
{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "obj-1", | ||
Labels: map[string]string{"schema": declcfg.SchemaBundle, "catalog": fakeCatalogName}, | ||
}, | ||
Spec: catalogd.CatalogMetadataSpec{ | ||
Content: json.RawMessage(validBundle), | ||
}, | ||
}, | ||
}, | ||
wantData: []*catalogmetadata.Bundle{ | ||
{ | ||
Bundle: declcfg.Bundle{ | ||
Schema: declcfg.SchemaBundle, | ||
Name: "fake1.v1.0.0", | ||
Package: "fake1", | ||
Image: "fake-image", | ||
Properties: []property.Property{ | ||
{ | ||
Type: property.TypePackage, | ||
Value: json.RawMessage(`{"packageName":"fake1","version":"1.0.0"}`), | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
{ | ||
name: "invalid objects", | ||
objs: []catalogd.CatalogMetadata{ | ||
{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "obj-1", | ||
Labels: map[string]string{"schema": declcfg.SchemaBundle, "catalog": fakeCatalogName}, | ||
}, | ||
Spec: catalogd.CatalogMetadataSpec{ | ||
Content: json.RawMessage(`{"name":123123123}`), | ||
}, | ||
}, | ||
}, | ||
wantErr: "json: cannot unmarshal number into Go struct field Bundle.name of type string", | ||
}, | ||
{ | ||
name: "not found", | ||
wantData: []*catalogmetadata.Bundle{}, | ||
}, | ||
} { | ||
t.Run(tt.name, func(t *testing.T) { | ||
data, err := catalogmetadata.Unmarshal[catalogmetadata.Bundle](tt.objs) | ||
assert.Equal(t, tt.wantData, data) | ||
if tt.wantErr != "" { | ||
assert.EqualError(t, err, tt.wantErr) | ||
} else { | ||
assert.NoError(t, err) | ||
} | ||
}) | ||
} | ||
} |