Skip to content

Commit c984467

Browse files
committed
test(operators): add e2e test for the v2alpha1 operator resource
- Add an e2e test for the v2alpha1 Operator resource - Add some e2e utilities for watching resource changes
1 parent a35b4d0 commit c984467

File tree

5 files changed

+403
-27
lines changed

5 files changed

+403
-27
lines changed

test/e2e/catalog_e2e_test.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@ import (
1010
"reflect"
1111
"time"
1212

13-
"github.com/operator-framework/api/pkg/lib/version"
14-
1513
"github.com/blang/semver"
14+
. "github.com/onsi/ginkgo"
1615
"github.com/stretchr/testify/require"
1716
appsv1 "k8s.io/api/apps/v1"
1817
corev1 "k8s.io/api/core/v1"
@@ -21,7 +20,7 @@ import (
2120
"k8s.io/apimachinery/pkg/labels"
2221
"k8s.io/apimachinery/pkg/util/wait"
2322

24-
. "github.com/onsi/ginkgo"
23+
"github.com/operator-framework/api/pkg/lib/version"
2524
"github.com/operator-framework/api/pkg/operators/v1alpha1"
2625
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry"
2726
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient"

test/e2e/installplan_e2e_test.go

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,38 +2,37 @@ package e2e
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"strings"
78
"sync"
89
"time"
910

1011
"github.com/blang/semver"
12+
. "github.com/onsi/ginkgo"
1113
"github.com/onsi/ginkgo/extensions/table"
12-
operatorsv1 "github.com/operator-framework/api/pkg/operators/v1"
13-
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry"
14-
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/kubernetes/pkg/apis/rbac"
15-
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil"
16-
opver "github.com/operator-framework/api/pkg/lib/version"
14+
"github.com/stretchr/testify/assert"
15+
"github.com/stretchr/testify/require"
16+
appsv1 "k8s.io/api/apps/v1"
1717
authorizationv1 "k8s.io/api/authorization/v1"
18+
corev1 "k8s.io/api/core/v1"
1819
rbacv1 "k8s.io/api/rbac/v1"
20+
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
21+
k8serrors "k8s.io/apimachinery/pkg/api/errors"
22+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1923
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
24+
"k8s.io/apimachinery/pkg/util/wait"
2025
"k8s.io/apimachinery/pkg/watch"
2126
"k8s.io/client-go/util/retry"
2227

23-
"errors"
24-
25-
. "github.com/onsi/ginkgo"
28+
opver "github.com/operator-framework/api/pkg/lib/version"
29+
operatorsv1 "github.com/operator-framework/api/pkg/operators/v1"
2630
operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
2731
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned"
32+
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry"
33+
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/kubernetes/pkg/apis/rbac"
2834
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient"
29-
"github.com/stretchr/testify/assert"
30-
"github.com/stretchr/testify/require"
31-
appsv1 "k8s.io/api/apps/v1"
32-
corev1 "k8s.io/api/core/v1"
33-
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
34-
k8serrors "k8s.io/apimachinery/pkg/api/errors"
35-
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
36-
"k8s.io/apimachinery/pkg/util/wait"
35+
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil"
3736
)
3837

3938
var _ = Describe("Install Plan", func() {

test/e2e/operator_test.go

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
package e2e
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"time"
7+
8+
. "github.com/onsi/ginkgo"
9+
. "github.com/onsi/gomega"
10+
corev1 "k8s.io/api/core/v1"
11+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12+
"k8s.io/apimachinery/pkg/watch"
13+
"k8s.io/client-go/kubernetes"
14+
15+
operatorsv2alpha1 "github.com/operator-framework/api/pkg/operators/v2alpha1"
16+
client "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned/typed/operators/v2alpha1"
17+
"github.com/operator-framework/operator-lifecycle-manager/test/e2e/ctx"
18+
)
19+
20+
// Describes test specs for the Operator resource.
21+
var _ = Describe("Operator", func() {
22+
var (
23+
deleteOpts *metav1.DeleteOptions
24+
listOpts metav1.ListOptions
25+
operatorClient client.OperatorInterface
26+
kubeClient kubernetes.Interface
27+
)
28+
29+
BeforeEach(func() {
30+
// Toggle v2alpha1 feature-gate
31+
toggleCVO()
32+
togglev2alpha1()
33+
34+
// Setup common utilities
35+
listOpts = metav1.ListOptions{}
36+
deleteOpts = &metav1.DeleteOptions{}
37+
operatorClient = ctx.Ctx().OperatorClient().OperatorsV2alpha1().Operators()
38+
kubeClient = ctx.Ctx().KubeClient().KubernetesInterface()
39+
})
40+
41+
AfterEach(func() {
42+
toggleCVO()
43+
togglev2alpha1()
44+
})
45+
46+
// Ensures that an Operator resource can select its components by label and surface them correctly in its status.
47+
//
48+
// Steps:
49+
// 1. Create an Operator resource, o
50+
// 2. Ensure o's status eventually contains its component label selector
51+
// 3. Create namespaces ns-a and ns-b
52+
// 4. Label ns-a with o's component label
53+
// 5. Ensure o's status.components.refs field eventually contains a reference to ns-a
54+
// 6. Create ServiceAccounts sa-a and sa-b in namespaces ns-a and ns-b respectively
55+
// 7. Label sa-a and sa-b with o's component label
56+
// 8. Ensure o's status.components.refs field eventually contains references to sa-a and sa-b
57+
// 9. Remove the component label from sa-b
58+
// 10. Ensure the reference to sa-b is eventually removed from o's status.components.refs field
59+
// 11. Delete ns-a
60+
// 12. Ensure the reference to ns-a is eventually removed from o's status.components.refs field
61+
It("should surface components in its status", func() {
62+
o := &operatorsv2alpha1.Operator{}
63+
o.SetName(genName("o-"))
64+
o, err := operatorClient.Create(o)
65+
Expect(err).ToNot(HaveOccurred())
66+
67+
defer func() {
68+
Expect(operatorClient.Delete(o.GetName(), deleteOpts)).To(Succeed())
69+
}()
70+
71+
By("eventually having a status that contains its component label selector")
72+
w, err := operatorClient.Watch(listOpts)
73+
Expect(err).ToNot(HaveOccurred())
74+
defer w.Stop()
75+
76+
deadline, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
77+
defer cancel()
78+
79+
expectedKey := "operators.coreos.com/" + o.GetName()
80+
awaitPredicates(deadline, w, operatorPredicate(func(op *operatorsv2alpha1.Operator) bool {
81+
if op.Status.Components == nil || op.Status.Components.LabelSelector == nil {
82+
return false
83+
}
84+
85+
for _, requirement := range op.Status.Components.LabelSelector.MatchExpressions {
86+
if requirement.Key == expectedKey && requirement.Operator == metav1.LabelSelectorOpExists {
87+
return true
88+
}
89+
}
90+
91+
return false
92+
}))
93+
defer w.Stop()
94+
95+
// Create namespaces ns-a and ns-b
96+
nsA := &corev1.Namespace{}
97+
nsA.SetName(genName("ns-a-"))
98+
nsB := &corev1.Namespace{}
99+
nsB.SetName(genName("ns-b-"))
100+
101+
for _, ns := range []*corev1.Namespace{nsA, nsB} {
102+
_, err := kubeClient.CoreV1().Namespaces().Create(ns)
103+
Expect(err).ToNot(HaveOccurred())
104+
defer func(name string) {
105+
kubeClient.CoreV1().Namespaces().Delete(name, deleteOpts)
106+
}(ns.GetName())
107+
}
108+
109+
// Label ns-a with o's component label
110+
nsA.SetLabels(map[string]string{expectedKey: ""})
111+
_, err = kubeClient.CoreV1().Namespaces().Update(nsA)
112+
Expect(err).ToNot(HaveOccurred())
113+
114+
// Ensure o's status.components.refs field eventually contains a reference to ns-a
115+
By("eventually listing a single component reference")
116+
componentRefEventuallyExists(w, true, nsA.GetName())
117+
118+
// Create ServiceAccounts sa-a and sa-b in namespaces ns-a and ns-b respectively
119+
saA := &corev1.ServiceAccount{}
120+
saA.SetName(genName("sa-a-"))
121+
saA.SetNamespace(nsA.Name)
122+
saB := &corev1.ServiceAccount{}
123+
saB.SetName(genName("sa-b-"))
124+
saB.SetNamespace(nsB.Name)
125+
126+
for _, sa := range []*corev1.ServiceAccount{saA, saB} {
127+
_, err := kubeClient.CoreV1().ServiceAccounts(sa.GetNamespace()).Create(sa)
128+
Expect(err).ToNot(HaveOccurred())
129+
defer func(namespace, name string) {
130+
kubeClient.CoreV1().ServiceAccounts(namespace).Delete(name, deleteOpts)
131+
}(sa.GetNamespace(), sa.GetName())
132+
}
133+
134+
// Label sa-a and sa-b with o's component label
135+
saA.SetLabels(map[string]string{expectedKey: ""})
136+
_, err = kubeClient.CoreV1().ServiceAccounts(saA.GetNamespace()).Update(saA)
137+
Expect(err).ToNot(HaveOccurred())
138+
saB.SetLabels(map[string]string{expectedKey: ""})
139+
_, err = kubeClient.CoreV1().ServiceAccounts(saB.GetNamespace()).Update(saB)
140+
Expect(err).ToNot(HaveOccurred())
141+
142+
// Ensure o's status.components.refs field eventually contains references to sa-a and sa-b
143+
By("eventually listing multiple component references")
144+
componentRefEventuallyExists(w, true, saA.GetName())
145+
componentRefEventuallyExists(w, true, saB.GetName())
146+
147+
// Remove the component label from sa-b
148+
saB.SetLabels(nil)
149+
_, err = kubeClient.CoreV1().ServiceAccounts(saB.GetNamespace()).Update(saB)
150+
Expect(err).ToNot(HaveOccurred())
151+
152+
// Ensure the reference to sa-b is eventually removed from o's status.components.refs field
153+
By("removing a component's reference when it no longer bears the component label")
154+
componentRefEventuallyExists(w, false, saB.GetName())
155+
156+
// Delete ns-a
157+
Expect(kubeClient.CoreV1().Namespaces().Delete(nsA.GetName(), deleteOpts)).To(Succeed())
158+
159+
// Ensure the reference to ns-a is eventually removed from o's status.components.refs field
160+
By("removing a component's reference when it no longer exists")
161+
componentRefEventuallyExists(w, false, nsA.GetName())
162+
})
163+
164+
})
165+
166+
func componentRefEventuallyExists(w watch.Interface, exists bool, refName string) {
167+
deadline, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
168+
defer cancel()
169+
170+
awaitPredicates(deadline, w, operatorPredicate(func(op *operatorsv2alpha1.Operator) bool {
171+
if op.Status.Components == nil {
172+
return false
173+
}
174+
175+
for _, ref := range op.Status.Components.Refs {
176+
if ref.Name == refName {
177+
return exists
178+
}
179+
}
180+
181+
return !exists
182+
}))
183+
}
184+
185+
func operatorPredicate(fn func(*operatorsv2alpha1.Operator) bool) predicateFunc {
186+
return func(event watch.Event) bool {
187+
o, ok := event.Object.(*operatorsv2alpha1.Operator)
188+
if !ok {
189+
panic(fmt.Sprintf("unexpected event object type %T in deployment", event.Object))
190+
}
191+
192+
return fn(o)
193+
}
194+
}

test/e2e/subscription_e2e_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,14 @@ import (
2525
"k8s.io/apimachinery/pkg/util/wait"
2626
"k8s.io/client-go/discovery"
2727

28+
"github.com/operator-framework/api/pkg/lib/version"
2829
"github.com/operator-framework/api/pkg/operators/v1alpha1"
2930
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned"
3031
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install"
3132
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry"
3233
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver"
3334
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/comparison"
3435
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient"
35-
"github.com/operator-framework/api/pkg/lib/version"
3636
"github.com/operator-framework/operator-lifecycle-manager/test/e2e/ctx"
3737
)
3838

0 commit comments

Comments
 (0)