Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ require (
github.com/operator-framework/deppy v0.0.0-20230602120738-cbf2c66b141b
github.com/operator-framework/operator-registry v1.26.3
github.com/operator-framework/rukpak v0.12.0
github.com/stretchr/testify v1.8.1
go.uber.org/zap v1.24.0
k8s.io/apimachinery v0.26.1
k8s.io/client-go v0.26.1
Expand Down Expand Up @@ -49,6 +50,7 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.14.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
Expand Down
5 changes: 5 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -273,13 +273,18 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
Expand Down
198 changes: 96 additions & 102 deletions internal/resolution/resolver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@ import (
"fmt"
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/operator-framework/deppy/pkg/deppy"
"github.com/operator-framework/deppy/pkg/deppy/input"
"github.com/operator-framework/deppy/pkg/deppy/solver"
"github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
Expand All @@ -20,116 +19,111 @@ import (
)

func TestOperatorResolver(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Operator Resolver Suite")
}

func FakeClient(objects ...client.Object) client.Client {
scheme := runtime.NewScheme()
if err := v1alpha1.AddToScheme(scheme); err != nil {
panic(fmt.Sprintf("error creating fake client: %s", err))
testEntityCache := map[deppy.Identifier]input.Entity{"operatorhub/prometheus/0.37.0": *input.NewEntity(
"operatorhub/prometheus/0.37.0", map[string]string{
"olm.bundle.path": `"foo.io/bar/baz"`,
"olm.channel": "{\"channelName\":\"beta\",\"priority\":0,\"replaces\":\"prometheusoperator.0.32.0\"}",
"olm.package": "{\"packageName\":\"prometheus\",\"version\":\"0.37.0\"}",
}),
"operatorhub/prometheus/0.47.0": *input.NewEntity("operatorhub/prometheus/0.47.0", map[string]string{
"olm.bundle.path": `"foo.io/bar/baz"`,
"olm.channel": "{\"channelName\":\"beta\",\"priority\":0,\"replaces\":\"prometheusoperator.0.37.0\"}",
"olm.package": "{\"packageName\":\"prometheus\",\"version\":\"0.47.0\"}",
}),
"operatorhub/packageA/2.0.0": *input.NewEntity("operatorhub/packageA/2.0.0", map[string]string{
"olm.bundle.path": `"foo.io/packageA/packageA:v2.0.0"`,
"olm.channel": "{\"channelName\":\"stable\",\"priority\":0}",
"olm.package": "{\"packageName\":\"packageA\",\"version\":\"2.0.0\"}",
}),
}
return fake.NewClientBuilder().WithScheme(scheme).WithObjects(objects...).Build()
}

var testEntityCache = map[deppy.Identifier]input.Entity{
"operatorhub/prometheus/0.37.0": *input.NewEntity("operatorhub/prometheus/0.37.0", map[string]string{
"olm.bundle.path": `"quay.io/operatorhubio/prometheus@sha256:3e281e587de3d03011440685fc4fb782672beab044c1ebadc42788ce05a21c35"`,
"olm.channel": "{\"channelName\":\"beta\",\"priority\":0,\"replaces\":\"prometheusoperator.0.32.0\"}",
"olm.gvk": "[{\"group\":\"monitoring.coreos.com\",\"kind\":\"Alertmanager\",\"version\":\"v1\"}, {\"group\":\"monitoring.coreos.com\",\"kind\":\"Prometheus\",\"version\":\"v1\"}]",
"olm.package": "{\"packageName\":\"prometheus\",\"version\":\"0.37.0\"}",
}),
"operatorhub/prometheus/0.47.0": *input.NewEntity("operatorhub/prometheus/0.47.0", map[string]string{
"olm.bundle.path": `"quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed"`,
"olm.channel": "{\"channelName\":\"beta\",\"priority\":0,\"replaces\":\"prometheusoperator.0.37.0\"}",
"olm.gvk": "[{\"group\":\"monitoring.coreos.com\",\"kind\":\"Alertmanager\",\"version\":\"v1\"}, {\"group\":\"monitoring.coreos.com\",\"kind\":\"Prometheus\",\"version\":\"v1alpha1\"}]",
"olm.package": "{\"packageName\":\"prometheus\",\"version\":\"0.47.0\"}",
}),
"operatorhub/packageA/2.0.0": *input.NewEntity("operatorhub/packageA/2.0.0", map[string]string{
"olm.bundle.path": `"foo.io/packageA/packageA:v2.0.0"`,
"olm.channel": "{\"channelName\":\"stable\",\"priority\":0}",
"olm.gvk": "[{\"group\":\"foo.io\",\"kind\":\"Foo\",\"version\":\"v1\"}]",
"olm.package": "{\"packageName\":\"packageA\",\"version\":\"2.0.0\"}",
}),
}
testEntitySource := input.NewCacheQuerier(testEntityCache)

var _ = Describe("OperatorResolver", func() {
It("should resolve the packages described by the available Operator resources", func() {
resources := []client.Object{
&v1alpha1.Operator{
ObjectMeta: metav1.ObjectMeta{
Name: "prometheus",
},
Spec: v1alpha1.OperatorSpec{
PackageName: "prometheus",
},
},
&v1alpha1.Operator{
ObjectMeta: metav1.ObjectMeta{
Name: "packageA",
},
Spec: v1alpha1.OperatorSpec{
PackageName: "packageA",
},
},
}
client := FakeClient(resources...)
entitySource := input.NewCacheQuerier(testEntityCache)
variableSource := olm.NewOLMVariableSource(client)
resolver := solver.NewDeppySolver(entitySource, variableSource)
solution, err := resolver.Solve(context.Background())
Expect(err).ToNot(HaveOccurred())
// 2 * required package variables + 2 * bundle variables
Expect(solution.SelectedVariables()).To(HaveLen(4))

Expect(solution.IsSelected("operatorhub/packageA/2.0.0")).To(BeTrue())
Expect(solution.IsSelected("operatorhub/prometheus/0.47.0")).To(BeTrue())
Expect(solution.IsSelected("required package packageA")).To(BeTrue())
Expect(solution.IsSelected("required package prometheus")).To(BeTrue())

Expect(solution.IsSelected("operatorhub/prometheus/0.37.0")).To(BeFalse())

})

It("should not return an error if there are no Operator resources", func() {
var resources []client.Object
client := FakeClient(resources...)
entitySource := input.NewCacheQuerier(testEntityCache)
variableSource := olm.NewOLMVariableSource(client)
resolver := solver.NewDeppySolver(entitySource, variableSource)
solution, err := resolver.Solve(context.Background())
Expect(err).ToNot(HaveOccurred())
Expect(solution.SelectedVariables()).To(HaveLen(0))
})

It("should return an error if the entity source throws an error", func() {
resource := &v1alpha1.Operator{
testResource := []client.Object{
&v1alpha1.Operator{
ObjectMeta: metav1.ObjectMeta{
Name: "prometheus",
},
Spec: v1alpha1.OperatorSpec{
PackageName: "prometheus",
},
}
client := FakeClient(resource)
entitySource := FailEntitySource{}
variableSource := olm.NewOLMVariableSource(client)
resolver := solver.NewDeppySolver(entitySource, variableSource)
solution, err := resolver.Solve(context.Background())
Expect(solution).To(BeNil())
Expect(err).To(HaveOccurred())
})

It("should return an error if the client throws an error", func() {
client := NewFailClientWithError(fmt.Errorf("something bad happened"))
entitySource := input.NewCacheQuerier(testEntityCache)
variableSource := olm.NewOLMVariableSource(client)
resolver := solver.NewDeppySolver(entitySource, variableSource)
solution, err := resolver.Solve(context.Background())
Expect(solution).To(BeNil())
Expect(err).To(HaveOccurred())
})
})
},
&v1alpha1.Operator{
ObjectMeta: metav1.ObjectMeta{
Name: "packageA",
},
Spec: v1alpha1.OperatorSpec{
PackageName: "packageA",
},
},
}

for _, tt := range []struct {
name string
client client.Client
entitySource input.EntitySource
expectedSelectedVariables []deppy.Identifier
expectedError error
}{
{
name: "should resolve the packages described by the available Operator resources",
client: FakeClient(testResource...),
entitySource: testEntitySource,
expectedSelectedVariables: []deppy.Identifier{
"operatorhub/packageA/2.0.0",
"operatorhub/prometheus/0.47.0",
"required package packageA",
"required package prometheus"},
expectedError: nil,
},
{
name: "should not return an error if there are no Operator resources",
client: FakeClient(),
entitySource: testEntitySource,
expectedSelectedVariables: []deppy.Identifier{},
expectedError: nil,
},
{
name: "should return an error if the entity source throws an error",
client: FakeClient(testResource...),
entitySource: FailEntitySource{},
expectedError: fmt.Errorf("error calling filter in entity source"),
},
{
name: "should return an error if the client throws an error",
client: NewFailClientWithError(fmt.Errorf("something bad happened")),
entitySource: testEntitySource,
expectedError: fmt.Errorf("something bad happened"),
},
} {
t.Run(tt.name, func(t *testing.T) {
variableSource := olm.NewOLMVariableSource(tt.client)
resolver := solver.NewDeppySolver(tt.entitySource, variableSource)
solution, err := resolver.Solve(context.Background())

if tt.expectedError != nil {
assert.Equal(t, tt.expectedError, err)
assert.Nil(t, solution)
} else {
assert.Len(t, solution.SelectedVariables(), len(tt.expectedSelectedVariables))
for _, identifier := range tt.expectedSelectedVariables {
assert.True(t, solution.IsSelected(identifier))
}
assert.NoError(t, err)
}

})
}
}

func FakeClient(objects ...client.Object) client.Client {
scheme := runtime.NewScheme()
if err := v1alpha1.AddToScheme(scheme); err != nil {
panic(fmt.Sprintf("error creating fake client: %s", err))
}
return fake.NewClientBuilder().WithScheme(scheme).WithObjects(objects...).Build()
}

var _ input.EntitySource = &FailEntitySource{}

Expand Down